graft 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -22,6 +22,7 @@ spec = Gem::Specification.new do |s|
22
22
  s.add_dependency('tzinfo', '>= 0.3.12')
23
23
  s.add_dependency('builder', '>= 2.1.2')
24
24
  s.add_dependency('activesupport', '>= 2.0')
25
+ s.add_dependency('json', '>= 1.1.7')
25
26
  end
26
27
 
27
28
  Rake::GemPackageTask.new(spec) do |pkg|
@@ -0,0 +1,9 @@
1
+ class Hash
2
+
3
+ def /(path)
4
+ path.to_s.split('/').inject(self) do |data, key|
5
+ (data.is_a?(Hash) && data[key]) ? data[key] : nil
6
+ end
7
+ end
8
+
9
+ end
data/lib/graft/json.rb ADDED
@@ -0,0 +1,14 @@
1
+ $:.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'json'
4
+ require 'graft/core_ext/hash'
5
+
6
+ require 'graft/model'
7
+ require 'graft/json/attribute'
8
+ require 'graft/json/model'
9
+
10
+ module Graft
11
+ def self.included(other)
12
+ other.send(:include, Graft::Json::Model)
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Graft
2
+ module Json
3
+ class Attribute
4
+
5
+ attr_reader :name, :source
6
+
7
+ def initialize(name, source = nil)
8
+ @name = name
9
+ @source = source.nil? ? @name.to_s : source
10
+ end
11
+
12
+ def value_from(data)
13
+ data/source
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module Graft
2
+ module Json
3
+ module Model
4
+
5
+ module ClassMethods
6
+
7
+ def attribute(name, options = {})
8
+ source = options[:from]
9
+
10
+ self.attributes << Graft::Json::Attribute.new(name, source)
11
+ class_eval "attr_accessor :#{name}"
12
+ end
13
+
14
+ def data_from(json_or_hash)
15
+ json_or_hash.is_a?(String) ? JSON.parse(json_or_hash) : json_or_hash
16
+ end
17
+
18
+ end
19
+
20
+ def self.included(other)
21
+ other.send(:extend, Graft::Model::ClassMethods)
22
+ other.send(:extend, Graft::Json::Model::ClassMethods)
23
+ other.send(:include, Graft::Model::InstanceMethods)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
data/lib/graft/model.rb CHANGED
@@ -1,69 +1,38 @@
1
1
  module Graft
2
2
  module Model
3
-
3
+
4
4
  module ClassMethods
5
-
6
5
  def attributes
7
6
  @attributes ||= []
8
7
  end
9
8
 
10
- def attribute(name, options = {})
11
- source = options[:from]
12
- type = options[:type] || :string
13
-
14
- self.attributes << Attribute.new(name, type, source)
15
- class_eval "attr_accessor :#{name}"
16
- end
17
-
18
- def collection_from(xml, node)
19
- (Hpricot.XML(xml)/node).map {|n| new n.to_s }
9
+ def collection_from(data_source, node)
10
+ (data_from(data_source)/node).map {|n| new n }
20
11
  end
21
-
22
12
  end
23
-
13
+
24
14
  module InstanceMethods
25
15
 
26
- def initialize(document = nil)
27
- self.document = document
28
- self.populate_from(self.document) unless self.document.nil?
16
+ def initialize(source_data = nil)
17
+ self.source_data = source_data
18
+ self.populate_from(self.source_data) unless self.source_data.nil?
29
19
  end
30
20
 
31
- def document=(document)
32
- @document = document.is_a?(String) ? Hpricot.XML(document) : document
21
+ def source_data=(source_data)
22
+ @source_data = self.class.data_from(source_data)
33
23
  end
34
24
 
35
- def document
36
- @document
25
+ def source_data
26
+ @source_data
37
27
  end
38
28
 
39
- def populate_from(document)
29
+ def populate_from(data_source)
40
30
  self.class.attributes.each do |attribute|
41
- value = attribute.value_from(document)
31
+ value = attribute.value_from(self.class.data_from(data_source))
42
32
  self.send("#{attribute.name}=".to_sym, value) unless value.nil?
43
33
  end
44
34
  end
45
35
 
46
- def to_hash
47
- self.class.attributes.inject({}) {|h,a| h.merge(a.name.to_s => send(a.name)) }
48
- end
49
-
50
- def to_xml(tag_name)
51
- xml = Builder::XmlMarkup.new
52
- xml.instruct!
53
- xml.tag! tag_name do
54
- to_hash.each do |attribute, value|
55
- xml.tag! attribute, value
56
- end
57
- end
58
- xml.target!
59
- end
60
-
61
-
62
- end
63
-
64
- def self.included(other)
65
- other.send(:extend, Graft::Model::ClassMethods)
66
- other.send(:include, Graft::Model::InstanceMethods)
67
36
  end
68
37
 
69
38
  end
data/lib/graft/version.rb CHANGED
@@ -2,8 +2,8 @@ module Graft
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 1
6
- TINY = 1
5
+ MINOR = 2
6
+ TINY = 0
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
data/lib/graft/xml.rb ADDED
@@ -0,0 +1,19 @@
1
+ $:.unshift File.dirname(__FILE__) + '/..'
2
+
3
+ require 'hpricot'
4
+ require 'builder'
5
+ require 'tzinfo'
6
+ require 'active_support/core_ext/blank'
7
+ require 'active_support/time_with_zone'
8
+ require 'active_support/inflector'
9
+
10
+ require 'graft/model'
11
+ require 'graft/xml/type'
12
+ require 'graft/xml/attribute'
13
+ require 'graft/xml/model'
14
+
15
+ module Graft
16
+ def self.included(other)
17
+ other.send(:include, Graft::Xml::Model)
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module Graft
2
+ module Xml
3
+ class Attribute
4
+
5
+ # TODO: Refactor the location / attribute logic into a Source class
6
+
7
+ attr_reader :name, :sources
8
+
9
+ def initialize(name, type = :string, sources = nil)
10
+ @name = name.to_sym
11
+ @type = type
12
+
13
+ @sources = Array(sources)
14
+ @sources << @name.to_s if @sources.empty?
15
+ end
16
+
17
+ def type_class
18
+ "Graft::Xml::Type::#{@type.to_s.camelize}".constantize
19
+ end
20
+
21
+ def split(source)
22
+ location, attribute = source.split('@')
23
+ location = self.name.to_s if location.blank?
24
+
25
+ [location, attribute]
26
+ end
27
+
28
+ def node_for(document, source)
29
+ document.at(location(source)) || document.search("//[@#{attribute(source)}]").first
30
+ end
31
+
32
+ def attribute(source)
33
+ location, attribute = source.split('@')
34
+ attribute || location
35
+ end
36
+
37
+ def location(source)
38
+ split(source).first
39
+ end
40
+
41
+ def value_from(document)
42
+ values = sources.map do |source|
43
+ node = node_for(document, source)
44
+ (node.attributes[attribute(source)] || node.inner_text) unless node.nil?
45
+ end
46
+
47
+ type_class.new(values.compact.first).value
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,49 @@
1
+ module Graft
2
+ module Xml
3
+ module Model
4
+
5
+ module ClassMethods
6
+
7
+ def attribute(name, options = {})
8
+ source = options[:from]
9
+ type = options[:type] || :string
10
+
11
+ self.attributes << Graft::Xml::Attribute.new(name, type, source)
12
+ class_eval "attr_accessor :#{name}"
13
+ end
14
+
15
+ def data_from(xml_or_document)
16
+ xml_or_document.is_a?(String) ? Hpricot.XML(xml_or_document) : xml_or_document
17
+ end
18
+
19
+ end
20
+
21
+ module InstanceMethods
22
+
23
+ def to_hash
24
+ self.class.attributes.inject({}) {|h,a| h.merge(a.name.to_s => send(a.name)) }
25
+ end
26
+
27
+ def to_xml(tag_name)
28
+ xml = Builder::XmlMarkup.new
29
+ xml.instruct!
30
+ xml.tag! tag_name do
31
+ to_hash.each do |attribute, value|
32
+ xml.tag! attribute, value
33
+ end
34
+ end
35
+ xml.target!
36
+ end
37
+
38
+ end
39
+
40
+ def self.included(other)
41
+ other.send(:extend, Graft::Model::ClassMethods)
42
+ other.send(:extend, Graft::Xml::Model::ClassMethods)
43
+ other.send(:include, Graft::Model::InstanceMethods)
44
+ other.send(:include, Graft::Xml::Model::InstanceMethods)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,91 @@
1
+ module Graft
2
+ module Xml
3
+
4
+ # = Type
5
+ #
6
+ class Type
7
+
8
+ class ConversionError < StandardError; end
9
+
10
+ def initialize(source)
11
+ @source = source
12
+ end
13
+
14
+ def convertible?
15
+ true
16
+ end
17
+
18
+ def value
19
+ raise ConversionError unless (@source.blank? || convertible?)
20
+ @source.blank? ? nil : convert
21
+ end
22
+
23
+ # = String
24
+ #
25
+ class String < Type
26
+
27
+ def convert
28
+ @source
29
+ end
30
+
31
+ end
32
+
33
+ # = Boolean
34
+ #
35
+ class Boolean < Type
36
+ def true_values
37
+ ['true', '1']
38
+ end
39
+
40
+ def false_values
41
+ ['false', '0']
42
+ end
43
+
44
+ def convertible?
45
+ (true_values + false_values).include?(@source)
46
+ end
47
+
48
+ def convert
49
+ true_values.include?(@source) ? true : false
50
+ end
51
+ end
52
+
53
+ # = Integer
54
+ #
55
+ class Integer < Type
56
+ def convertible?
57
+ !@source.match(/\d+/).nil?
58
+ end
59
+
60
+ def convert
61
+ @source.to_i
62
+ end
63
+ end
64
+
65
+ # = Time
66
+ #
67
+ class Time < Type
68
+
69
+ def timestamp?
70
+ !@source.match(/^\d+$/).nil?
71
+ end
72
+
73
+ def convert
74
+ timestamp? ? ::Time.at(@source.to_i) : ::Time.parse(@source)
75
+ end
76
+
77
+ end
78
+
79
+ # = Date
80
+ #
81
+ class Date < Type
82
+
83
+ def convert
84
+ ::Date.parse(@source)
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+ end
91
+ end
data/test/test_helper.rb CHANGED
@@ -4,13 +4,14 @@ $:.reject! { |e| e.include? 'TextMate' }
4
4
  require 'rubygems'
5
5
  require 'throat_punch'
6
6
 
7
- require File.dirname(__FILE__) + '/../lib/graft'
7
+ require File.dirname(__FILE__) + '/../lib/graft/xml'
8
+ require File.dirname(__FILE__) + '/../lib/graft/json'
8
9
 
9
10
  class Test::Unit::TestCase
10
11
 
11
12
  def self.implementation_klass
12
13
  class_name = self.to_s.match(/([a-zA-Z]+)Test$/)[1]
13
- klass = Graft::Type.const_get(class_name)
14
+ klass = Graft::Xml::Type.const_get(class_name)
14
15
 
15
16
  klass
16
17
  end
@@ -30,7 +31,7 @@ class Test::Unit::TestCase
30
31
 
31
32
  should "fail when converting '#{source}'" do
32
33
  o = klass.new(source)
33
- lambda { o.value }.should raise_error(Graft::Type::ConversionError)
34
+ lambda { o.value }.should raise_error(Graft::Xml::Type::ConversionError)
34
35
  end
35
36
  end
36
37
 
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ class HashTest < Test::Unit::TestCase
4
+
5
+ context "An instance of Hash" do
6
+
7
+ should "be able to extract a value using the / operator" do
8
+ hash = {'key' => 'value'}
9
+ (hash/'key').should == 'value'
10
+ end
11
+
12
+ should "be able to extract nested values using the / operator" do
13
+ hash = {'user' => {'name' => 'luser'}}
14
+ (hash/'user/name').should == 'luser'
15
+ end
16
+
17
+ should "return nil when attempting to extract a value that doesn't exist" do
18
+ hash = {'user' => {'name' => 'luser'}}
19
+ (hash/'user/username').should be(nil)
20
+ end
21
+
22
+ should "be able to fetch a value using a symbol" do
23
+ hash = {'key' => 'value'}
24
+ (hash/:key).should == 'value'
25
+ end
26
+
27
+ end
28
+
29
+ end