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