module EDSL::DSL
These methods will be extended into any class which includes EDSL
. They provide a mechanism for declaring HTML elements as properties of another object
Public Class Methods
Allow an accessor to be accessed by a different name
# File lib/edsl/dsl.rb, line 153 def self.alias_accessor(new_name, acc_name) self.class.send(:alias_method, new_name, acc_name) end
This vastly simplifies defining custom accessors. If we had to use the base element method for every field that would be tedious. This method allows us to extend the DSL
to add a shorthand method for elements.
If we extend the DSL
like this:
EDSL.define_accessor(:text_field, how: :text_field, default_method: :value, assign_method: :set)
Then we can use an easier syntax to declare a text field
text_field(:username, id: 'some_id')
# File lib/edsl/dsl.rb, line 146 def self.define_accessor(acc_name, default_opts) self.class.send(:define_method, acc_name) do |name, opts = {}, &block| element(name, default_opts.merge(opts), &block) end end
Allow multiple accessors to be defined at once
# File lib/edsl/dsl.rb, line 158 def self.define_accessors(accessor_array) accessor_array.each { |acc| define_accessor(*acc) } end
Public Instance Methods
Helper function to reduce perceived complexity of element
# File lib/edsl/dsl.rb, line 89 def _add_assignment_methods(name, element_method, opts) assign_method = opts.delete(:assign_method) return unless assign_method define_method("#{name}=") do |value| return assign_method.call(name, self, value) if assign_method.is_a?(Proc) send(element_method).send(assign_method, value) end end
Helper function to reduce perceived complexity of element
# File lib/edsl/dsl.rb, line 101 def _add_common_methods(name, element_method, opts) default_method = opts.delete(:default_method) presence_method = opts.delete(:presence_method) || :present? define_method(name) do return default_method.call(name, self) if default_method.is_a?(Proc) default_method.nil? ? send(element_method) : send(element_method).send(default_method) end define_method("#{name}?") do return presence_method.call(name, self) if presence_method.is_a?(Proc) send(element_method).send(presence_method) end end
rubocop:disable Metrics/AbcSize Helper function to reduce perceived complexity of element
# File lib/edsl/dsl.rb, line 120 def _add_element_method(name, opts, &block) how = opts.delete(:how) hooks = opts.delete(:hooks) wrapper_fn = opts.delete(:wrapper_fn) || ->(e, _p) { return e } ele_meth = "#{name}_element" define_method(ele_meth) do ele = yield self if block_given? ele ||= how.call(name, self, opts) if how.is_a?(Proc) ele ||= send(how, opts) ele = wrapper_fn.call(ele, self) hooks.nil? ? ele : send(:apply_hooks, hooks, ele) end ele_meth end
This is the core accessor on which everything else is based. Given a name and some options this will generate the following methods:
name - Executes the method found in the :default_method option, or the element itself if none provided. name= - Executes the method found in the :assign_method option, passing it the value. (optional). name? - Executes the method found in the :presence_method option, or present?
For example a text field would look like this:
element(:username, id: 'some_id', how: :text_field, default_method: :value, assign_method: :set)
The :how option can either be something that is sendable. or a proc
If send is used, the options will be passed on to the method. If a proc is used it will be called with the name and opts param as well as the container
A text field could be declared like this:
element(:username, id: 'some_id', how: Proc.new { |name, container, opts| container.text_field(opts) }, default_method: :value, assign_method: :set)
# File lib/edsl/dsl.rb, line 82 def element(name, opts, &block) element_method = _add_element_method(name, opts, &block) _add_common_methods(name, element_method, opts) _add_assignment_methods(name, element_method, opts) end