Your Rails views reimagined



Matestack provides a collection of open source gems made for Ruby on Rails developers. Matestack enables you to craft maintainable web UIs in pure Ruby, skipping ERB and HTML. UI code becomes a native and fun part of your Rails app. Thanks to reactive core components, reactivity can be optionally added on top without writing JavaScript, just using a simple Ruby DSL.

You end up writing 50% less code while increasing productivity, maintainability and developer happiness. Work with pure Ruby. If necessary, extend with pure JavaScript. No Opal involved.

Craft your UI based on your components written in pure Ruby. Utilizing Ruby's amazing language features, you're able to create a cleaner and more maintainable UI implementation.

Create Ruby classes within your Rails project and call matestack's core components through a Ruby DSL in order to craft your UIs.

The Ruby method "div" for example calls one of the static core components, responsible for rendering HTML tags. A component can take Strings, Integers, Symbols, Arrays or Hashes (...) as optional properties (e.g. "title") or require them (e.g. "body").

app/matestack/components/card.rb


class Components::Card < Matestack::Ui::Component requires :body optional :title optional :image def response div class: "card shadow-sm border-0 bg-light" do img path: image, class: "w-100" if image.present? div class: "card-body" do heading size: 5, text: title if title.present? paragraph class: "card-text", text: body end end end end 

Register your Ruby UI component classes with your desired DSL method and use the "matestack_component" helper in order to render your component within existing ERB views or Rails controllers.

The Ruby method "card" for example calls your "Card" class, enabling you to create a reuseable card components, abstracting UI complexity in your own components.

app/views/your_view.html.erb


<!-- some other erb markup -->
<%= matestack_component :card, title: "hello", body: "world" %>
<!-- some other erb markup --> 

app/matestack/components/registry.rb

module Components::Registry Matestack::Ui::Core::Component::Registry.register_components( card: Components::Card, #... ) end

Split your UI implementation into multiple small chunks helping others (and yourself) to better understand your implementation.

Using this approach helps you to create a clean, readable and maintainable codebase.

app/matestack/components/card.rb


class Components::Card < Matestack::Ui::Component requires :body optional :title optional :image optional :footer def response div class: "card shadow-sm border-0 bg-light" do img path: image, class: "w-100" if image.present? card_content card_footer if footer.present? end end def card_content div class: "card-body" do heading size: 5, text: title if title.present? paragraph class: "card-text", text: body end end def card_footer div class: "card-footer text-muted" do plain footer end end end 

app/views/your_view.html.erb

<!-- some other erb markup -->
<%= matestack_component :blue_card, title: "hello", body: "world", footer: "foo" %>
<!-- some other erb markup -->

Because it's just a Ruby class, you can use class inheritance in order to further improve the quality of your UI implementation.

Class inheritance can be used to easily create variants of UI components but still reuse parts of the implementation

app/matestack/components/blue_card.rb


class Components::BlueCard < Components::Card def response div class: "card shadow-sm border-0 bg-primary text-white" do img path: image, class: "w-100" if image.present? card_content #defined in parent class card_footer if footer.present? #defined in parent class end end end 

app/matestack/components/registry.rb

module Components::Registry Matestack::Ui::Core::Component::Registry.register_components( blue_card: Components::BlueCard, #... ) end

app/views/your_view.html.erb

<!-- some other erb markup -->
<%= matestack_component :blue_card, title: "hello", body: "world" %>
<!-- some other erb markup -->

Just like you used matestack's core components on your own UI component, you can use your own UI components within other custom UI components.

You decide when using a Ruby method partial should be replaced by another self contained UI component!

app/matestack/components/card.rb


class Components::Card < Matestack::Ui::Component requires :body optional :title optional :image def response div class: "card shadow-sm border-0 bg-light" do img path: image, class: "w-100" if image.present? # calling the CardBody component rather than using Ruby method partials card_body title: title, body: body end end end 

app/matestack/components/card_body.rb


class Components::CardBody < Matestack::Ui::Component requires :body optional :title def response # Just an example. Would make more sense, if this component had # a more complex structure div class: "card-body" do heading size: 5, text: title if title.present? paragraph class: "card-text", text: body end end end 

app/matestack/components/registry.rb

module Components::Registry Matestack::Ui::Core::Component::Registry.register_components( card: Components::Card, card_body: Components::CardBody, #... ) end

Sometimes it's not enough to just pass simple data into a component. No worries! You can just yield a block into your components!

Using this approach gives you more flexibility when using your UI components. Ofcourse yielding can be used alongside passing in simple params.

app/matestack/components/card.rb


class Components::Card < Matestack::Ui::Component requires :body optional :title optional :image def response div class: "card shadow-sm border-0 bg-light" do img path: image, class: "w-100" if image.present? card_body do # yielding a block into the card_body component heading size: 5, text: title if title.present? paragraph class: "card-text", text: body end end end end 

app/matestack/components/card_body.rb


class Components::CardBody < Matestack::Ui::Component def response # Just an example. Would make more sense, if this component had # a more complex structure div class: "card-body" do yield_components end end end 

If you need to inject multiple blocks into your UI component, you can use "slots"!

Slots help you to build complex UI components with multiple named content placeholders for highest implementation flexibility!

app/matestack/components/card.rb


class Components::Card < Matestack::Ui::Component requires :body optional :title optional :image def response div class: "card shadow-sm border-0 bg-light" do img path: image, class: "w-100" if image.present? card_body slots: { heading: heading_slot, body: body_slot } end end def heading_slot slot do heading size: 5, text: title if title.present? end end def body_slot slot do paragraph class: "card-text", text: body end end end 

app/matestack/components/card_body.rb


class Components::CardBody < Matestack::Ui::Component requires :slots def response # Just an example. Would make more sense, if this component had # a more complex structure div class: "card-body" do div class: "heading-section" do slot slots[:heading] end div class: "body-section" do slot slots[:body] end end end end