representable 0.0.1.alpha1

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.
Files changed (103) hide show
  1. data/.gitignore +7 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/History.txt +354 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +186 -0
  8. data/Rakefile +10 -0
  9. data/TODO +37 -0
  10. data/VERSION +1 -0
  11. data/examples/amazon.rb +35 -0
  12. data/examples/current_weather.rb +27 -0
  13. data/examples/dashed_elements.rb +20 -0
  14. data/examples/library.rb +40 -0
  15. data/examples/posts.rb +27 -0
  16. data/examples/rails.rb +70 -0
  17. data/examples/twitter.rb +37 -0
  18. data/examples/xml/active_record.xml +70 -0
  19. data/examples/xml/amazon.xml +133 -0
  20. data/examples/xml/current_weather.xml +89 -0
  21. data/examples/xml/dashed_elements.xml +52 -0
  22. data/examples/xml/posts.xml +23 -0
  23. data/examples/xml/twitter.xml +422 -0
  24. data/lib/representable.rb +257 -0
  25. data/lib/representable/definition.rb +109 -0
  26. data/lib/representable/nokogiri_extensions.rb +19 -0
  27. data/lib/representable/references.rb +153 -0
  28. data/lib/representable/version.rb +3 -0
  29. data/lib/representable/xml.rb +79 -0
  30. data/representable.gemspec +29 -0
  31. data/spec/definition_spec.rb +495 -0
  32. data/spec/examples/active_record_spec.rb +41 -0
  33. data/spec/examples/amazon_spec.rb +54 -0
  34. data/spec/examples/current_weather_spec.rb +37 -0
  35. data/spec/examples/dashed_elements_spec.rb +20 -0
  36. data/spec/examples/library_spec.rb +46 -0
  37. data/spec/examples/post_spec.rb +24 -0
  38. data/spec/examples/twitter_spec.rb +32 -0
  39. data/spec/roxml_integration_test.rb +289 -0
  40. data/spec/roxml_spec.rb +372 -0
  41. data/spec/shared_specs.rb +15 -0
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/support/libxml.rb +3 -0
  44. data/spec/support/nokogiri.rb +3 -0
  45. data/spec/xml/array_spec.rb +36 -0
  46. data/spec/xml/attributes_spec.rb +71 -0
  47. data/spec/xml/encoding_spec.rb +53 -0
  48. data/spec/xml/namespace_spec.rb +270 -0
  49. data/spec/xml/namespaces_spec.rb +67 -0
  50. data/spec/xml/object_spec.rb +82 -0
  51. data/spec/xml/parser_spec.rb +21 -0
  52. data/spec/xml/text_spec.rb +71 -0
  53. data/test/fixtures/book_malformed.xml +5 -0
  54. data/test/fixtures/book_pair.xml +8 -0
  55. data/test/fixtures/book_text_with_attribute.xml +5 -0
  56. data/test/fixtures/book_valid.xml +5 -0
  57. data/test/fixtures/book_with_authors.xml +7 -0
  58. data/test/fixtures/book_with_contributions.xml +9 -0
  59. data/test/fixtures/book_with_contributors.xml +7 -0
  60. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  61. data/test/fixtures/book_with_default_namespace.xml +9 -0
  62. data/test/fixtures/book_with_depth.xml +6 -0
  63. data/test/fixtures/book_with_octal_pages.xml +4 -0
  64. data/test/fixtures/book_with_publisher.xml +7 -0
  65. data/test/fixtures/book_with_wrapped_attr.xml +3 -0
  66. data/test/fixtures/dictionary_of_attr_name_clashes.xml +8 -0
  67. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  68. data/test/fixtures/dictionary_of_guarded_names.xml +6 -0
  69. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  70. data/test/fixtures/dictionary_of_name_clashes.xml +10 -0
  71. data/test/fixtures/dictionary_of_names.xml +4 -0
  72. data/test/fixtures/dictionary_of_texts.xml +10 -0
  73. data/test/fixtures/library.xml +30 -0
  74. data/test/fixtures/library_uppercase.xml +30 -0
  75. data/test/fixtures/muffins.xml +3 -0
  76. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  77. data/test/fixtures/node_with_attr_name_conflicts.xml +1 -0
  78. data/test/fixtures/node_with_name_conflicts.xml +4 -0
  79. data/test/fixtures/numerology.xml +4 -0
  80. data/test/fixtures/person.xml +1 -0
  81. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  82. data/test/fixtures/person_with_mothers.xml +10 -0
  83. data/test/mocks/dictionaries.rb +57 -0
  84. data/test/mocks/mocks.rb +279 -0
  85. data/test/roxml_test.rb +58 -0
  86. data/test/support/fixtures.rb +11 -0
  87. data/test/test_helper.rb +6 -0
  88. data/test/unit/definition_test.rb +235 -0
  89. data/test/unit/deprecations_test.rb +24 -0
  90. data/test/unit/to_xml_test.rb +81 -0
  91. data/test/unit/xml_attribute_test.rb +39 -0
  92. data/test/unit/xml_block_test.rb +81 -0
  93. data/test/unit/xml_bool_test.rb +122 -0
  94. data/test/unit/xml_convention_test.rb +150 -0
  95. data/test/unit/xml_hash_test.rb +115 -0
  96. data/test/unit/xml_initialize_test.rb +49 -0
  97. data/test/unit/xml_name_test.rb +141 -0
  98. data/test/unit/xml_namespace_test.rb +31 -0
  99. data/test/unit/xml_object_test.rb +206 -0
  100. data/test/unit/xml_required_test.rb +94 -0
  101. data/test/unit/xml_text_test.rb +71 -0
  102. data/website/index.html +98 -0
  103. metadata +248 -0
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'test'
8
+ test.test_files = FileList['test/*_test.rb']
9
+ test.verbose = true
10
+ end
data/TODO ADDED
@@ -0,0 +1,37 @@
1
+ Planned:
2
+
3
+ v 3.2
4
+
5
+ * Support outputting namespaced attrs
6
+
7
+ * Consider class_inheritable_attribute rather than superclass.try stuff.
8
+
9
+ * Do some benchmarking
10
+
11
+ * Back with http://xml-object.rubyforge.org/doc/ to minimize need for specifications?
12
+
13
+ * Commandeer #parse to use opposite #from_xml, but in an unrooted, collection-friendly fashion,
14
+ ala HappyMapper's parse
15
+
16
+ v 3.x
17
+
18
+ * :self => true for sending method_missing to this attribute?
19
+
20
+ * :attributes extensions ala HappyMapper?
21
+
22
+ * Add xml_attrs helper to share :in declarations between several attributes. E.g.:
23
+
24
+ xml_reader :count, :in => 'Attributes', :as => Integer
25
+ xml_reader :something_else, :in => 'Attributes', :as => Date
26
+
27
+ becomes:
28
+
29
+ xml_attrs :in => 'Attributes' do |xml|
30
+ xml.reader :count, :as => Integer
31
+ xml.reader :something_else, :as => Date
32
+ end
33
+
34
+ * Ensure (perhaps optionally) that references are unambiguous. That is error/warn
35
+ a singular specification has multiple possible node references
36
+
37
+ * Use lazy evaluation to minimize parsing time for large files
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 3.1.6
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+
4
+ # The document `pita.xml` contains both a default namespace and the 'georss'
5
+ # namespace (for the 'point' xml_reader).
6
+ module PITA
7
+ class Base
8
+ include ROXML
9
+ xml_convention :camelcase
10
+ end
11
+
12
+ class Item < Base
13
+ xml_reader :asin, :from => 'ASIN'
14
+ xml_reader :detail_page_url, :from => 'DetailPageURL'
15
+ xml_reader :manufacturer, :in => 'ItemAttributes'
16
+ # this is the only xml_reader that exists in a different namespace, so it
17
+ # must be explicitly specified
18
+ xml_reader :point, :namespace => 'georss'
19
+ end
20
+
21
+ class ItemSearchResponse < Base
22
+ xml_reader :total_results, :as => Integer, :in => 'Items'
23
+ xml_reader :total_pages, :as => Integer, :in => 'Items'
24
+ xml_reader :items, :as => [Item]
25
+ end
26
+ end
27
+
28
+ unless defined?(Spec)
29
+ response = PITA::ItemSearchResponse.from_xml(xml_for('amazon'))
30
+ p response.total_results
31
+ p response.total_pages
32
+ response.items.each do |i|
33
+ puts i.asin, i.detail_page_url, i.manufacturer, i.point, ''
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+
4
+ class Base
5
+ include ROXML
6
+ xml_convention :dasherize
7
+ xml_namespace 'aws'
8
+ end
9
+
10
+ class WeatherObservation < Base
11
+ xml_name 'ob'
12
+ xml_reader :temperature, :as => Float, :from => 'aws:temp'
13
+ xml_reader :feels_like, :as => Integer
14
+ xml_reader :current_condition #, :attributes => {:icon => String} # pending
15
+ end
16
+
17
+ class Weather < Base
18
+ xml_reader :observation, :as => WeatherObservation, :required => true
19
+ end
20
+
21
+ unless defined?(Spec)
22
+ current_weather = Weather.from_xml(xml_for('current_weather')).observation
23
+ puts "temperature: #{current_weather.temperature}"
24
+ puts "feels_like: #{current_weather.feels_like}"
25
+ puts "current_condition: #{current_weather.current_condition}"
26
+ # puts "current_condition.icon: #{current_weather.current_condition.icon}" # pending
27
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+
4
+ module GitHub
5
+ class Commit
6
+ include ROXML
7
+ xml_convention :dasherize
8
+
9
+ xml_reader :url
10
+ xml_reader :tree
11
+ xml_reader :message
12
+ xml_reader :id
13
+ xml_reader :committed_date, :as => Date
14
+ end
15
+ end
16
+
17
+ unless defined?(Spec)
18
+ commit = GitHub::Commit.from_xml(xml_for('dashed_elements'))
19
+ puts commit.committed_date, commit.url, commit.id, ''
20
+ end
@@ -0,0 +1,40 @@
1
+ class Publisher
2
+ include ROXML
3
+
4
+ xml_accessor :name
5
+
6
+ def initialize(name = nil)
7
+ @name = name
8
+ end
9
+
10
+ def ==(other)
11
+ name == other.name
12
+ end
13
+
14
+ # other important functionality
15
+ end
16
+
17
+ class Novel
18
+ include ROXML
19
+
20
+ xml_accessor :isbn, :from => "@ISBN" # attribute with name 'ISBN'
21
+ xml_accessor :title
22
+ xml_accessor :description, :cdata => true # text node with cdata protection
23
+ xml_accessor :author
24
+ xml_accessor :publisher, :as => Publisher # singular object reference for illustrative purposes.
25
+
26
+ def ==(other)
27
+ self.class.roxml_attrs.map(&:accessor).all? {|attr| send(attr) == other.send(attr) }
28
+ end
29
+ end
30
+
31
+ class Library
32
+ include ROXML
33
+
34
+ xml_accessor :name, :from => "NAME", :cdata => true
35
+ xml_accessor :novels, :as => [Novel] # by default roxml searches for books for in <novel> children, then, if none are present, in ./novels/novel children
36
+
37
+ def ==(other)
38
+ name == other.name && novels == other.novels
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+
4
+ class Post
5
+ include ROXML
6
+
7
+ xml_reader :href, :from => :attr
8
+ xml_reader :hash, :from => :attr
9
+ xml_reader :description, :from => :attr
10
+ xml_reader :tag, :from => :attr
11
+ xml_reader :created_at, :from => '@time'
12
+ xml_reader :others, :from => :attr, :as => Integer
13
+ xml_reader :extended, :from => :attr
14
+ end
15
+
16
+ class Posts
17
+ include ROXML
18
+
19
+ xml_reader :posts, :as => [Post]
20
+ end
21
+
22
+ unless defined?(Spec)
23
+ posts = Posts.from_xml(xml_for('posts'))
24
+ posts.posts.each do |post|
25
+ puts post.description, post.href, post.extended, ''
26
+ end
27
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+ require 'sqlite3'
4
+ require 'active_record'
5
+
6
+ DB_PATH = 'spec/examples/rails.sqlite3'
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => DB_PATH
10
+ )
11
+
12
+ class Waypoint < ActiveRecord::Base
13
+ include ROXML
14
+
15
+ belongs_to :route
16
+
17
+ xml_attr :isLeg
18
+ xml_attr :lonlatx
19
+ xml_attr :lonlaty
20
+ xml_attr :gridReference
21
+ xml_attr :ascent
22
+ xml_attr :descent
23
+ xml_attr :distance
24
+ xml_attr :bearing
25
+ xml_attr :timemins
26
+ end
27
+
28
+ class Route < ActiveRecord::Base
29
+ include ROXML
30
+
31
+ has_many :waypoints
32
+
33
+ xml_attr :title
34
+ xml_attr :totalDist
35
+ xml_attr :totalMins
36
+ xml_attr :totalHg
37
+ xml_attr :lonlatx
38
+ xml_attr :lonlaty
39
+ xml_attr :grcenter
40
+
41
+ xml_attr :waypoints, :as => [Waypoint], :in => "waypoints"
42
+ end
43
+
44
+ # do a quick pseudo migration. This should only get executed on the first run
45
+ if !Waypoint.table_exists?
46
+ ActiveRecord::Base.connection.create_table(:waypoints) do |t|
47
+ t.column :route_id, :integer
48
+ t.column :isLeg, :string
49
+ t.column :lonlatx, :string
50
+ t.column :lonlaty, :string
51
+ t.column :gridReference, :string
52
+ t.column :ascent, :string
53
+ t.column :descent, :string
54
+ t.column :distance, :string
55
+ t.column :bearing, :string
56
+ t.column :timeMins, :string
57
+ end
58
+ end
59
+
60
+ if !Route.table_exists?
61
+ ActiveRecord::Base.connection.create_table(:routes) do |t|
62
+ t.column :title, :string
63
+ t.column :totalDist, :string
64
+ t.column :totalMins, :string
65
+ t.column :totalHg, :string
66
+ t.column :lonlatx, :string
67
+ t.column :lonlaty, :string
68
+ t.column :grcenter, :string
69
+ end
70
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative './../spec/spec_helper'
3
+ require 'time'
4
+
5
+ class User
6
+ include ROXML
7
+
8
+ xml_reader :id, :as => Integer
9
+ xml_reader :name
10
+ xml_reader :screen_name
11
+ xml_reader :location
12
+ xml_reader :description
13
+ xml_reader :profile_image_url
14
+ xml_reader :url
15
+ xml_reader :protected?
16
+ xml_reader :followers_count, :as => Integer
17
+ end
18
+
19
+ class Status
20
+ include ROXML
21
+
22
+ xml_reader :id, :as => Integer
23
+ xml_reader :text
24
+ xml_reader :created_at # This defaults to :as => DateTime, due to the '_at'
25
+ xml_reader :source
26
+ xml_reader :truncated?
27
+ xml_reader :in_reply_to_status_id, :as => Integer
28
+ xml_reader :in_reply_to_user_id, :as => Integer
29
+ xml_reader :favorited?
30
+ xml_reader :user, :as => User
31
+ end
32
+
33
+ class Statuses
34
+ include ROXML
35
+
36
+ xml_reader :statuses, :as => [Status]
37
+ end
@@ -0,0 +1,70 @@
1
+ <route>
2
+ <totalDist>11185.321521477119</totalDist>
3
+ <totalHg>640</totalHg>
4
+ <totalMins>235.75000000000003</totalMins>
5
+ <lonlatx>357865</lonlatx>
6
+ <lonlaty>271635</lonlaty>
7
+ <grcenter>SH 71635 57865</grcenter>
8
+ <waypoints>
9
+ <waypoint>
10
+ <isLeg>false</isLeg>
11
+ <lonlatx>357290</lonlatx>
12
+ <lonlaty>271650</lonlaty>
13
+ <gridReference>SH 71650 57290</gridReference>
14
+ <ascent>81</ascent>
15
+ <descent>220</descent>
16
+ <distance>0</distance>
17
+ <bearing>0</bearing>
18
+ </waypoint>
19
+ <waypoint>
20
+ <isLeg>false</isLeg>
21
+ <lonlatx>357260</lonlatx>
22
+ <lonlaty>274600</lonlaty>
23
+ <gridReference>SH 74600 57260</gridReference>
24
+ <ascent>275</ascent>
25
+ <descent>48</descent>
26
+ <distance>2950.152538429157</distance>
27
+ <bearing>91</bearing>
28
+ </waypoint>
29
+ <waypoint>
30
+ <isLeg>false</isLeg>
31
+ <lonlatx>359160</lonlatx>
32
+ <lonlaty>273330</lonlaty>
33
+ <gridReference>SH 73330 59160</gridReference>
34
+ <ascent>73</ascent>
35
+ <descent>170</descent>
36
+ <distance>2285.3664913969487</distance>
37
+ <bearing>326</bearing>
38
+ </waypoint>
39
+ <waypoint>
40
+ <isLeg>false</isLeg>
41
+ <lonlatx>359170</lonlatx>
42
+ <lonlaty>270050</lonlaty>
43
+ <gridReference>SH 70050 59170</gridReference>
44
+ <ascent>182</ascent>
45
+ <descent>172</descent>
46
+ <distance>3280.015243867016</distance>
47
+ <bearing>270</bearing>
48
+ </waypoint>
49
+ <waypoint>
50
+ <isLeg>false</isLeg>
51
+ <lonlatx>357470</lonlatx>
52
+ <lonlaty>269740</lonlaty>
53
+ <gridReference>SH 69740 57470</gridReference>
54
+ <ascent>29</ascent>
55
+ <descent>107</descent>
56
+ <distance>1728.0335644888382</distance>
57
+ <bearing>190</bearing>
58
+ </waypoint>
59
+ <waypoint>
60
+ <isLeg>false</isLeg>
61
+ <lonlatx>356840</lonlatx>
62
+ <lonlaty>270440</lonlaty>
63
+ <gridReference>SH 70440 56840</gridReference>
64
+ <ascent>640</ascent>
65
+ <descent>717</descent>
66
+ <distance>941.7536832951597</distance>
67
+ <bearing>132</bearing>
68
+ </waypoint>
69
+ </waypoints>
70
+ </route>
@@ -0,0 +1,133 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05" xmlns:georss="http://www.georss.org/georss">
3
+ <OperationRequest>
4
+ <HTTPHeaders>
5
+ <Header Name="UserAgent">
6
+ </Header>
7
+ </HTTPHeaders>
8
+ <RequestId>16WRJBVEM155Q026KCV1</RequestId>
9
+ <Arguments>
10
+ <Argument Name="SearchIndex" Value="Books"></Argument>
11
+ <Argument Name="Service" Value="AWSECommerceService"></Argument>
12
+ <Argument Name="Title" Value="Ruby on Rails"></Argument>
13
+ <Argument Name="Operation" Value="ItemSearch"></Argument>
14
+ <Argument Name="AWSAccessKeyId" Value="dontbeaswoosh"></Argument>
15
+ </Arguments>
16
+ <RequestProcessingTime>0.064924955368042</RequestProcessingTime>
17
+ </OperationRequest>
18
+ <Items>
19
+ <Request>
20
+ <IsValid>True</IsValid>
21
+ <ItemSearchRequest>
22
+ <SearchIndex>Books</SearchIndex>
23
+ <Title>Ruby on Rails</Title>
24
+ </ItemSearchRequest>
25
+ </Request>
26
+ <TotalResults>22</TotalResults>
27
+ <TotalPages>3</TotalPages>
28
+ <Item>
29
+ <ASIN>0321480791</ASIN>
30
+ <georss:point>38.5351715088 -121.7948684692</georss:point>
31
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
32
+ <ItemAttributes>
33
+ <Author>Michael Hartl</Author>
34
+ <Author>Aurelius Prochazka</Author>
35
+ <Manufacturer>Addison-Wesley Professional</Manufacturer>
36
+ <ProductGroup>Book</ProductGroup>
37
+ <Title>RailsSpace: Building a Social Networking Website with Ruby on Rails (Addison-Wesley Professional Ruby Series)</Title>
38
+ </ItemAttributes>
39
+ </Item>
40
+ <Item>
41
+ <ASIN>047022388X</ASIN>
42
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=047022388X%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/047022388X%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
43
+ <ItemAttributes>
44
+ <Author>Noel Rappin</Author>
45
+ <Manufacturer>Wrox</Manufacturer>
46
+ <ProductGroup>Book</ProductGroup>
47
+ <Title>Professional Ruby on Rails</Title>
48
+ </ItemAttributes>
49
+ </Item>
50
+ <Item>
51
+ <ASIN>1590598814</ASIN>
52
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1590598814%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/1590598814%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
53
+ <ItemAttributes>
54
+ <Author>Ola Bini</Author>
55
+ <Manufacturer>Apress</Manufacturer>
56
+ <ProductGroup>Book</ProductGroup>
57
+ <Title>Practical JRuby on Rails Web 2.0 Projects: Bringing Ruby on Rails to Java</Title>
58
+ </ItemAttributes>
59
+ </Item>
60
+ <Item>
61
+ <ASIN>0596101325</ASIN>
62
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0596101325%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0596101325%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
63
+ <ItemAttributes>
64
+ <Author>Bruce Tate</Author>
65
+ <Author>Curt Hibbs</Author>
66
+ <Manufacturer>O'Reilly Media, Inc.</Manufacturer>
67
+ <ProductGroup>Book</ProductGroup>
68
+ <Title>Ruby on Rails: Up and Running</Title>
69
+ </ItemAttributes>
70
+ </Item>
71
+ <Item>
72
+ <ASIN>0470081201</ASIN>
73
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0470081201%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0470081201%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
74
+ <ItemAttributes>
75
+ <Author>Barry Burd</Author>
76
+ <Manufacturer>For Dummies</Manufacturer>
77
+ <ProductGroup>Book</ProductGroup>
78
+ <Title>Ruby on Rails For Dummies (For Dummies (Computer/Tech))</Title>
79
+ </ItemAttributes>
80
+ </Item>
81
+ <Item>
82
+ <ASIN>0975841955</ASIN>
83
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0975841955%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0975841955%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
84
+ <ItemAttributes>
85
+ <Author>Patrick Lenz</Author>
86
+ <Manufacturer>SitePoint</Manufacturer>
87
+ <ProductGroup>Book</ProductGroup>
88
+ <Title>Build Your Own Ruby on Rails Web Applications</Title>
89
+ </ItemAttributes>
90
+ </Item>
91
+ <Item>
92
+ <ASIN>0470069155</ASIN>
93
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0470069155%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0470069155%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
94
+ <ItemAttributes>
95
+ <Author>Steve, Ph.D. Holzner</Author>
96
+ <Manufacturer>Wrox</Manufacturer>
97
+ <ProductGroup>Book</ProductGroup>
98
+ <Title>Beginning Ruby on Rails (Wrox Beginning Guides)</Title>
99
+ </ItemAttributes>
100
+ </Item>
101
+ <Item>
102
+ <ASIN>1590597362</ASIN>
103
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1590597362%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/1590597362%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
104
+ <ItemAttributes>
105
+ <Author>Christian Hellsten</Author>
106
+ <Author>Jarkko Laine</Author>
107
+ <Manufacturer>Apress</Manufacturer>
108
+ <ProductGroup>Book</ProductGroup>
109
+ <Title>Beginning Ruby on Rails E-Commerce: From Novice to Professional (Rails)</Title>
110
+ </ItemAttributes>
111
+ </Item>
112
+ <Item>
113
+ <ASIN>1590597524</ASIN>
114
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=1590597524%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/1590597524%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
115
+ <ItemAttributes>
116
+ <Author>Justin Williams</Author>
117
+ <Manufacturer>friends of ED</Manufacturer>
118
+ <ProductGroup>Book</ProductGroup>
119
+ <Title>Rails Solutions: Ruby on Rails Made Easy (Solutions)</Title>
120
+ </ItemAttributes>
121
+ </Item>
122
+ <Item>
123
+ <ASIN>0321517067</ASIN>
124
+ <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0321517067%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321517067%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
125
+ <ItemAttributes>
126
+ <Author>Aurelius Prochazka</Author>
127
+ <Manufacturer>Addison-Wesley Professional</Manufacturer>
128
+ <ProductGroup>Book</ProductGroup>
129
+ <Title>RailsSpace Ruby on Rails Tutorial (Video Training) (LiveLessons)</Title>
130
+ </ItemAttributes>
131
+ </Item>
132
+ </Items>
133
+ </ItemSearchResponse>