Last active
January 10, 2021 00:17
-
-
Save andrewmcodes/072e7f7319c20f8bd39368ffd322f101 to your computer and use it in GitHub Desktop.
OpenStruct example with Components in Rails/Bridgetown/etc. Hopefully you can follow this - I tossed it together quickly.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%# | |
This is a page or partial in my Rails app. | |
While this is not how I typically use them, | |
I think it was a perfect example and exactly | |
why I reached for it today. | |
Here is a page with a simple component: | |
%> | |
<%= render FieldComponent.new(type: "email", name: "email_address", label: "Email Address") %> | |
<%# This will output HTML like: | |
<field-component> | |
<label>Email Address</label> | |
<input type="email" name="email_address" /> | |
</field-component> | |
%> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Initial snippet came from: https://www.bridgetownrb.com/docs/erb-and-beyond#rendering-ruby-components | |
# But I tweaked it bc I really didn't like the choice of syntax (no offense in the least - total preference) | |
# Here is what it was: | |
# | |
# class FieldComponent | |
# def initialize(type: "text", name:, label:) | |
# @type, @name, @label = type, name, label | |
# end | |
# def render_in(view_context) | |
# <<~HTML | |
# <field-component> | |
# <label>#{@label}</label> | |
# <input type="#{@type}" name="#{@name}" /> | |
# </field-component> | |
# HTML | |
# end | |
# end | |
# | |
class FieldComponent | |
attr_reader :name, :label, :type | |
def initialize(name, label, type: "text") | |
@name = name # | required | |
@label = label # | required | |
@type = type # | optional, default: "text" | |
end | |
def render_in(view_context) | |
<<~HTML | |
<field-component> | |
<label>#{label}</label> | |
<input type="#{type}" name="#{name}" /> | |
</field-component> | |
HTML | |
end | |
end | |
# | |
# The output will be the same, we just have different stylistic preferences | |
# and so do all teams. Imagine the author of that snippet committed his | |
# version to the codebase, and this is a new one. Already we have two | |
# different ways of doing things for something we are going to be making over and over again | |
# and maybe the person coming behind me prefers his attr_readers are private methods | |
# | |
# Im sure you can see where we will be in a year or two at this rate. | |
# | |
# (╯°□°)╯︵ ┻━┻ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 🎉 Enter the OptStruct gem. | |
# | |
# 1. It is less code (yh yah I know some of yall dont care about that but this is a simple example keep an open mind) | |
# 2. It is arguably more readable - especially given the context of what we are doing. | |
# 3. It sets a pattern out of the gate that wont suffer from some of the more pedantic patterns we all have. | |
# | |
class FieldComponent < OptStruct.new | |
required :label, :name | |
option :type, default: "text" | |
def render_in(view_context) | |
<<~HTML | |
<field-component> | |
<label>#{label}</label> | |
<input type="#{type}" name="#{name}" /> | |
</field-component> | |
HTML | |
end | |
end | |
# | |
# 👀 example showing how you can build new options using existing ones | |
# ``` | |
# def display_name | |
# options.fetch(:display_name) { "[#{type}] -> #{name}" } | |
# end | |
# ``` | |
# | |
# 🤙 The OptStruct has callbacks you can hook into as well: https://github.com/carlzulauf/opt_struct#on-initialization | |
# | |
# And more! | |
# | |
# 🤔 When (and basically the only time) do I reach for this? When I am about to create possibly | |
# hundreds of POROs that are all going to be representing the same concept. | |
# | |
# But if it is not something we are going to be constantly repeating and wouldnt benefit | |
# from trying to mitigate the technical debt now vs. later.. | |
# | |
# I have just gone with an example from 01. Again - simple example. | |
# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment