class NaturalBornSlugger::AttributeComposer

Public Class Methods

default_options() click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 15
def self.default_options
  {
    require_all: false,
    join_with: '',
    callback: false
  }
end
extract_dependencies_from_options(dependencies={}) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 23
def self.extract_dependencies_from_options(dependencies={})
  options = default_options.dup
  options.keys.each do |option|
    if dependencies.has_key? option
      options[option] = dependencies.delete(option)
    end
  end
  [dependencies, options]
end
new(name, options) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 6
def initialize(name, options)
  @name = name.to_s
  @dependencies, options = self.class.extract_dependencies_from_options(options)
  options.each do |option, value|
    self.instance_variable_set("@#{option}", value)
  end
  raise ConfigurationError.new(self.name, name, "no dependent attributes were specified") if @dependencies.empty?
end
resolve_dependency(object, dependency) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 33
def self.resolve_dependency(object, dependency)
  dependency_chain = dependency.to_s.split('.')
  dependency_chain.each do |method|
    object = object.try :send, method
    break unless object
  end
  object
end

Public Instance Methods

add_to(klass) click to toggle source

Adds composite attribute methods to the class:

a getter, and updater method.

Also adds a setter that either calls the updater

or throws an error, depending on configuration
# File lib/natural_born_slugger/attribute_composer.rb, line 85
def add_to(klass)

  unless klass.respond_to? :composite_attributes
    klass.class_attribute(:composite_attributes)
  end
  klass.composite_attributes ||= {}
  klass.composite_attributes[@name] = self

  klass.send :define_method, "update_#{@name}".to_sym do
    name = __method__.to_s.gsub 'update_', ''
    new_value = self.class.composite_attributes[name].evaluate(self)
    if self.respond_to? "#{name}=".to_sym
      new_value = self.send "#{name}=".to_sym, new_value
    end
    self.instance_variable_set("@#{name}", new_value)
    #TODO: What is the fate of callbacks? Should the feature be removed? Potential race condition if the value was mutated by the callback
    #composer.callback(self, old_value, new_value)
    new_value
  end

  # Define instance attribute getter: calls setter
  klass.send :define_method, @name do
    name = __method__.to_s
    if frozen?
      self.instance_variable_get("@#{name}")
    else
      self.send "update_#{name}".to_sym
    end
  end

end
callback(object, old_value, new_value) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 72
def callback(object, old_value, new_value)
  if @callback
    unless old_value == new_value
      object.instance_exec old_value, new_value, &@callback
    end
  end
end
compose_attribute(resolved_dependencies) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 43
def compose_attribute(resolved_dependencies)
  resolved_dependencies.map do |resolved_dependency, strategy|
    case strategy
    when Symbol # Symbols represent string methods to call on the resolved dependency
      resolved_dependency.to_s.send(strategy)
    when String # Strings represent formats to fit the resolved dependency into
      strategy % resolved_dependency
    when Regexp # Regexps represent patterns to pull out of the resolved dependency and join
      resolved_dependency.scan(strategy).join(@join_with)
    when Proc   # Procs should take one parameter and return a string or nil
      strategy.call(resolved_dependency)
    else        # If no strategy provided, use resolved dependency as is
      resolved_dependency.to_s
    end
  # Remove nil components if `compact_dependencies` is true
  end.reject{|string| not string or string.empty? }.join(@join_with)
end
evaluate(object) click to toggle source
# File lib/natural_born_slugger/attribute_composer.rb, line 61
def evaluate(object)
  resolved_dependencies = @dependencies.map do |dependency, strategy|
    [self.class.resolve_dependency(object, dependency), strategy]
  end
  # Check existence of all attribute dependencies if `require_all` is true
  if @require_all ? resolved_dependencies.map(&:first).all? : true
    composite = compose_attribute(resolved_dependencies)
    composite.empty? ? nil : composite
  end
end