graft 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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