lib/craft.rb in craft-0.0.2 vs lib/craft.rb in craft-0.1.0

- old
+ new

@@ -5,58 +5,61 @@ # # Examples # # module Transformations # IntegerTransform = lambda { |n| Integer n.text } +# Timestamp = lambda { Time.now } # end # # class Person < Craft # include Transformations # # one :name, 'div.name' # one :age, 'div.age', IntegerTransform # many :friends, 'li.friend', Person +# stub :created_at, Timestamp # end # class Craft class << self - # We alias call to new so that crafted objects may nest themselves or other - # crafted objects as transformations. - alias call new + # Returns an Array of names for the attributes defined in the class. + attr :attribute_names - # Define a method that extracts a collection of values from a parsed + # Define an attribute that extracts a collection of values from a parsed # document. # - # name - The Symbol name of the method. + # name - The Symbol name of the attribute. # paths - One or more String XPath of CSS queries. An optional Proc # transformation on the extracted value may be appended. If none is # appended, the default transformation returns the stripped String # value of the node. # - # Returns an Array. + # Returns nothing. def many(name, *paths) - transform = pop_transformation paths + transform = pop_transform_from_paths paths + @attribute_names << name define_method name do - @node.search(*paths).map { |node| transform.call node } + @node.search(*paths).map { |node| instance_exec node, &transform } end end - # Define a method that extracts a single value from a parsed document. + # Define an attribute that extracts a single value from a parsed document. # - # name - The Symbol name of the method. + # name - The Symbol name of the attribute. # paths - One or more String XPath of CSS queries. An optional Proc # transformation on the extracted value may be appended. If none is # appended, the default transformation returns the stripped String # value of the node. # - # Returns an Object. + # Returns nothing. def one(name, *paths) - transform = pop_transformation paths + transform = pop_transform_from_paths paths + @attribute_names << name define_method name do - transform.call @node.at(*paths) + instance_exec @node.at(*paths), &transform end end # Parse a document. # @@ -65,27 +68,60 @@ # Returns an instance of its self. def parse(body) new Nokogiri body end + # Define an attribute that returns a value without parsing the document. + # + # name - The Symbol name of the attribute. + # value - Some value the attribute should return. If given a Proc, the + # value will be generated dynamically (default: nil). + # + # Returns nothing. + def stub(name, value = nil) + @attribute_names << name + + define_method name do + value.respond_to?(:call) ? instance_exec(&value) : value + end + end + + def to_proc + klass = self + ->(node) { klass.new node, self } + end + private - def pop_transformation(array) - if array.last.respond_to? :call + def inherited(subclass) + subclass.instance_variable_set :@attribute_names, [] + end + + def pop_transform_from_paths(array) + if array.last.respond_to? :to_proc array.pop else - Module.new do - def self.call(node) - node.text.strip if node - end - end + ->(node) { node.text.strip if node } end end end + attr :parent + # Craft a new object. # # node - A Nokogiri::XML::Node. - def initialize(node) + def initialize(node, parent = nil) @node = node + @parent = parent + end + + # Returns the Hash attributes. + def attributes + Hash[attribute_names.map { |key| [key, self.send(key)] }] + end + + # Returns an Array of names for the attributes on this object. + def attribute_names + self.class.attribute_names end end