reagent-graft 0.1.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/README.rdoc ADDED
@@ -0,0 +1,44 @@
1
+ = Graft
2
+
3
+ == Description
4
+
5
+ Graft provides an easy way to map XML data onto your Ruby classes.
6
+
7
+ == Installation
8
+
9
+ Stable:
10
+
11
+ $ sudo gem install graft
12
+
13
+ Bleeding edge:
14
+
15
+ $ sudo gem install reagent-graft --source=http://gems.github.com
16
+
17
+ == Usage
18
+
19
+ TBD
20
+
21
+ == License
22
+
23
+ Copyright (c) 2009 Patrick Reagan (reaganpr@gmail.com)
24
+
25
+ Permission is hereby granted, free of charge, to any person
26
+ obtaining a copy of this software and associated documentation
27
+ files (the "Software"), to deal in the Software without
28
+ restriction, including without limitation the rights to use,
29
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
30
+ copies of the Software, and to permit persons to whom the
31
+ Software is furnished to do so, subject to the following
32
+ conditions:
33
+
34
+ The above copyright notice and this permission notice shall be
35
+ included in all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
39
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
41
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
42
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
43
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
44
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+
5
+ require 'lib/graft/version'
6
+
7
+ task :default => :test
8
+
9
+ spec = Gem::Specification.new do |s|
10
+ s.name = 'graft'
11
+ s.version = Graft::Version.to_s
12
+ s.has_rdoc = true
13
+ s.extra_rdoc_files = %w(README.rdoc)
14
+ s.rdoc_options = %w(--main README.rdoc)
15
+ s.summary = "Simple XML to attribute mapping for your Ruby classes"
16
+ s.author = 'Patrick Reagan'
17
+ s.email = 'reaganpr@gmail.com'
18
+ s.homepage = 'http://sneaq.net/'
19
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
20
+
21
+ s.add_dependency('hpricot', '~> 0.6.0')
22
+ s.add_dependency('activesupport', '~> 2.0')
23
+ end
24
+
25
+ Rake::GemPackageTask.new(spec) do |pkg|
26
+ pkg.gem_spec = spec
27
+ end
28
+
29
+ Rake::TestTask.new do |t|
30
+ t.libs << 'test'
31
+ t.test_files = FileList["test/**/*_test.rb"]
32
+ t.verbose = true
33
+ end
34
+
35
+ desc 'Generate the gemspec to serve this Gem from Github'
36
+ task :github do
37
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
38
+ File.open(file, 'w') {|f| f << spec.to_ruby }
39
+ puts "Created gemspec: #{file}"
40
+ end
@@ -0,0 +1,44 @@
1
+ module Graft
2
+ class Attribute
3
+
4
+ # TODO: Refactor the location / attribute logic into a Source class
5
+
6
+ attr_reader :name, :sources
7
+
8
+ def initialize(name, sources = nil)
9
+ @name = name.to_sym
10
+
11
+ @sources = Array(sources)
12
+ @sources << @name.to_s if @sources.empty?
13
+ end
14
+
15
+ def split(source)
16
+ location, attribute = source.split('@')
17
+ location = self.name.to_s if location.blank?
18
+
19
+ [location, attribute]
20
+ end
21
+
22
+ def node_for(document, source)
23
+ document.at(location(source)) || document.search("//[@#{attribute(source)}]").first
24
+ end
25
+
26
+ def attribute(source)
27
+ location, attribute = source.split('@')
28
+ attribute || location
29
+ end
30
+
31
+ def location(source)
32
+ split(source).first
33
+ end
34
+
35
+ def value_from(document)
36
+ values = sources.map do |source|
37
+ node = node_for(document, source)
38
+ (node.attributes[attribute(source)] || node.inner_text) unless node.nil?
39
+ end
40
+ values.compact.first
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module Graft
2
+ module Model
3
+
4
+ module ClassMethods
5
+
6
+ def attributes
7
+ @attributes ||= []
8
+ end
9
+
10
+ def attribute(name, options = {})
11
+ self.attributes << Attribute.new(name, options[:from])
12
+ class_eval "attr_accessor :#{name}"
13
+ end
14
+
15
+ end
16
+
17
+ module InstanceMethods
18
+
19
+ def initialize(document = nil)
20
+ self.document = document
21
+ self.populate_from(self.document) unless self.document.nil?
22
+ end
23
+
24
+ def document=(document)
25
+ @document = document.is_a?(String) ? Hpricot.XML(document) : document
26
+ end
27
+
28
+ def document
29
+ @document
30
+ end
31
+
32
+ def populate_from(document)
33
+ self.class.attributes.each do |attribute|
34
+ value = attribute.value_from(document)
35
+ self.send("#{attribute.name}=".to_sym, value) unless value.nil?
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ def self.included(other)
42
+ other.send(:extend, Graft::Model::ClassMethods)
43
+ other.send(:include, Graft::Model::InstanceMethods)
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ module Graft
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 0
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
data/lib/graft.rb ADDED
@@ -0,0 +1,7 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'hpricot'
4
+ require 'active_support/core_ext/blank'
5
+
6
+ require 'graft/attribute'
7
+ require 'graft/model'
@@ -0,0 +1,7 @@
1
+ # http://sneaq.net/textmate-wtf
2
+ $:.reject! { |e| e.include? 'TextMate' }
3
+
4
+ require 'rubygems'
5
+ require 'throat_punch'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/graft'
@@ -0,0 +1,133 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ module Graft
4
+ class AttributeTest < Test::Unit::TestCase
5
+ describe "An instance of the Attribute class" do
6
+
7
+ it "should know the name of the attribute" do
8
+ attr = Attribute.new('foo')
9
+ attr.name.should == :foo
10
+ end
11
+
12
+ it "should have a default source" do
13
+ attr = Attribute.new(:foo)
14
+ attr.sources.should == ['foo']
15
+ end
16
+
17
+ it "should be able to assign multiple sources" do
18
+ attr = Attribute.new(:foo, ['foo1', 'foo2'])
19
+ attr.sources.should == ['foo1', 'foo2']
20
+ end
21
+
22
+ it "should pull the location from the source" do
23
+ attr = Attribute.new('foo')
24
+ attr.location('foo').should == 'foo'
25
+ end
26
+
27
+ it "should return the location when splitting" do
28
+ attr = Attribute.new('foo')
29
+ attr.split('foo').should == ['foo', nil]
30
+ end
31
+
32
+ it "should return the name for the location when splitting if the location isn't specified" do
33
+ attr = Attribute.new('foo')
34
+ attr.split('@bar').should == ['foo', 'bar']
35
+ end
36
+
37
+ it "should allow the setting of the location information" do
38
+ attr = Attribute.new('foo', 'bar')
39
+ attr.sources.should == ['bar']
40
+ end
41
+
42
+ it "should allow the setting of the attribute value" do
43
+ attr = Attribute.new('foo')
44
+ attr.attribute('@bogon').should == 'bogon'
45
+ end
46
+
47
+ it "should use the location as the attribute" do
48
+ attr = Attribute.new('foo')
49
+ attr.attribute('foo').should == 'foo'
50
+ end
51
+
52
+ it "should use the attribute for the attribute if specified" do
53
+ attr = Attribute.new(:id, '@nsid')
54
+ attr.attribute('@nsid').should == 'nsid'
55
+ end
56
+
57
+ it "should be able to retrieve the node from the path" do
58
+ document = Hpricot.XML('<name>Bassdrive</name>')
59
+ expected = document.at('name')
60
+
61
+ attr = Attribute.new(:name)
62
+ attr.node_for(document, 'name').should == expected
63
+ end
64
+
65
+ it "should be able to retrieve the node that contains the specified attribute" do
66
+ document = Hpricot.XML('<user id="1337" />')
67
+ expected = document.at('user')
68
+
69
+ attr = Attribute.new(:id)
70
+ attr.node_for(document, '@id').should == expected
71
+ end
72
+
73
+ it "should be able to retrieve the node for the specified attribute" do
74
+ document = Hpricot.XML('<user nsid="1337" />')
75
+ expected = document.at('user')
76
+
77
+ attr = Attribute.new(:id, '@nsid')
78
+ attr.node_for(document, '@nsid').should == expected
79
+ end
80
+
81
+ it "should be able to pull simple values from an XML document" do
82
+ document = Hpricot.XML('<name>Bassdrive</name>')
83
+ attr = Attribute.new(:name)
84
+ attr.value_from(document).should == 'Bassdrive'
85
+ end
86
+
87
+ it "should be able to pull an attribute value from the current XML node" do
88
+ document = Hpricot.XML('<user id="1337" />')
89
+ attr = Attribute.new(:id)
90
+ attr.value_from(document).should == '1337'
91
+ end
92
+
93
+ it "should be able to pull a specific attribute value from the current XML node" do
94
+ document = Hpricot.XML('<user nsid="1337" />')
95
+ attr = Attribute.new(:id, '@nsid')
96
+ attr.value_from(document).should == '1337'
97
+ end
98
+
99
+ it "should be able to pull an attribute value for a node and attribute" do
100
+ document = Hpricot.XML('<station><genre slug="dnb">Drum & Bass</genre></station>')
101
+ attr = Attribute.new(:slug, 'station/genre@slug')
102
+ attr.value_from(document).should == 'dnb'
103
+ end
104
+
105
+ it "should be able to pull a value from a nested XML node" do
106
+ document = Hpricot.XML('<rsp><user>blip</user></rsp>')
107
+ attr = Attribute.new(:user)
108
+ attr.value_from(document).should == 'blip'
109
+ end
110
+
111
+ # it "should be able to pull a value from an attribute once it's found a node by another attribute" do
112
+ # document = Hpricot.XML('<node type="blip" value="bleep" />')
113
+ # attr = Attribute.new(:blip, "node[@type='blip]@value")
114
+ #
115
+ # attr.value_from(document).should == 'bleep'
116
+ # end
117
+
118
+ it "should return nil if it cannot find the specified node" do
119
+ document = Hpricot.XML('<user id="1" />')
120
+ attr = Attribute.new(:photoset, '@nsid')
121
+ attr.value_from(document).should be(nil)
122
+ end
123
+
124
+ it "should be able to try a series of nodes to find a value" do
125
+ document = Hpricot.XML('<photoid>123</photoid>')
126
+
127
+ attr = Attribute.new(:id, ['photo@nsid', 'photoid'])
128
+ attr.value_from(document).should == '123'
129
+ end
130
+
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class EmptyModel
4
+ include Graft::Model
5
+ end
6
+
7
+ class ModelWithAttributes
8
+ include Graft::Model
9
+
10
+ attribute :name
11
+ attribute :description, :from => 'desc'
12
+ attribute :rating, :from => 'rating@value'
13
+ attribute :size, :from => "node[@type='size']@value"
14
+
15
+ end
16
+
17
+ class ModelTest < Test::Unit::TestCase
18
+
19
+ describe "The EmptyModel class" do
20
+ it "should have an empty list of attributes if none are supplied" do
21
+ EmptyModel.attributes.should == []
22
+ end
23
+
24
+ end
25
+
26
+ describe "The ModelWithAttributes class" do
27
+ it "should know the names of all its attributes" do
28
+ ModelWithAttributes.attributes.map {|a| a.name.to_s }.should == %w(name description rating size)
29
+ end
30
+ end
31
+
32
+ describe "An instance of the ModelWithAttributes class" do
33
+
34
+ before { @simple_xml = '<name>Graft</name>' }
35
+
36
+ it "should have default reader method for :name" do
37
+ ModelWithAttributes.new.respond_to?(:name).should be(true)
38
+ end
39
+
40
+ it "should be able to populate its data on initialization" do
41
+ xml = Hpricot.XML(@simple_xml)
42
+ ModelWithAttributes.new(xml).name.should == 'Graft'
43
+ end
44
+
45
+ it "should have a reference to the original document" do
46
+ xml = Hpricot.XML(@simple_xml)
47
+ ModelWithAttributes.new(xml).document.should == xml
48
+ end
49
+
50
+ it "should be able to populate from an XML string" do
51
+ ModelWithAttributes.new(@simple_xml).name.should == 'Graft'
52
+ end
53
+
54
+ context "when populating data from an XML document" do
55
+
56
+ before do
57
+ xml = <<-XML
58
+ <name>Graft</name>
59
+ <desc>A sweet Ruby library</desc>
60
+ <rating value="100" />
61
+ <node type="color" value="blue" />
62
+ <node type="size" value="large" />
63
+ XML
64
+
65
+ @model = ModelWithAttributes.new
66
+ @model.populate_from(Hpricot.XML(xml))
67
+ end
68
+
69
+ it "should have the correct value for :name" do
70
+ @model.name.should == 'Graft'
71
+ end
72
+
73
+ it "should have the correct value for :description" do
74
+ @model.description.should == 'A sweet Ruby library'
75
+ end
76
+
77
+ it "should have the correct value for :rating" do
78
+ @model.rating.should == '100'
79
+ end
80
+
81
+ # it "should have the correct value for :size" do
82
+ # @model.size.should == 'large'
83
+ # end
84
+
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ module Graft
4
+ class SourceTest < Test::Unit::TestCase
5
+
6
+ describe "An instance of the Source class" do
7
+
8
+
9
+
10
+ end
11
+
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reagent-graft
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Patrick Reagan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-06 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.6.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "2.0"
34
+ version:
35
+ description:
36
+ email: reaganpr@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ files:
44
+ - README.rdoc
45
+ - Rakefile
46
+ - lib/graft
47
+ - lib/graft/attribute.rb
48
+ - lib/graft/model.rb
49
+ - lib/graft/version.rb
50
+ - lib/graft.rb
51
+ - test/test_helper.rb
52
+ - test/unit
53
+ - test/unit/attribute_test.rb
54
+ - test/unit/model_test.rb
55
+ - test/unit/source_test.rb
56
+ has_rdoc: true
57
+ homepage: http://sneaq.net/
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --main
61
+ - README.rdoc
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.2.0
80
+ signing_key:
81
+ specification_version: 2
82
+ summary: Simple XML to attribute mapping for your Ruby classes
83
+ test_files: []
84
+