representable 0.0.1.alpha1 → 0.0.1

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 (102) hide show
  1. data/README.rdoc +58 -153
  2. data/lib/representable.rb +18 -42
  3. data/lib/representable/bindings/json_bindings.rb +69 -0
  4. data/lib/representable/bindings/xml_bindings.rb +152 -0
  5. data/lib/representable/definition.rb +1 -12
  6. data/lib/representable/json.rb +66 -0
  7. data/lib/representable/version.rb +1 -1
  8. data/lib/representable/xml.rb +32 -38
  9. data/representable.gemspec +3 -2
  10. data/test/bindings_test.rb +110 -0
  11. data/test/json_test.rb +130 -0
  12. data/test/{roxml_test.rb → representable_test.rb} +28 -9
  13. data/test/test_helper.rb +2 -0
  14. data/test/xml_test.rb +192 -0
  15. metadata +32 -105
  16. data/History.txt +0 -354
  17. data/TODO +0 -37
  18. data/VERSION +0 -1
  19. data/examples/amazon.rb +0 -35
  20. data/examples/current_weather.rb +0 -27
  21. data/examples/dashed_elements.rb +0 -20
  22. data/examples/library.rb +0 -40
  23. data/examples/posts.rb +0 -27
  24. data/examples/rails.rb +0 -70
  25. data/examples/twitter.rb +0 -37
  26. data/examples/xml/active_record.xml +0 -70
  27. data/examples/xml/amazon.xml +0 -133
  28. data/examples/xml/current_weather.xml +0 -89
  29. data/examples/xml/dashed_elements.xml +0 -52
  30. data/examples/xml/posts.xml +0 -23
  31. data/examples/xml/twitter.xml +0 -422
  32. data/lib/representable/references.rb +0 -153
  33. data/spec/definition_spec.rb +0 -495
  34. data/spec/examples/active_record_spec.rb +0 -41
  35. data/spec/examples/amazon_spec.rb +0 -54
  36. data/spec/examples/current_weather_spec.rb +0 -37
  37. data/spec/examples/dashed_elements_spec.rb +0 -20
  38. data/spec/examples/library_spec.rb +0 -46
  39. data/spec/examples/post_spec.rb +0 -24
  40. data/spec/examples/twitter_spec.rb +0 -32
  41. data/spec/roxml_integration_test.rb +0 -289
  42. data/spec/roxml_spec.rb +0 -372
  43. data/spec/shared_specs.rb +0 -15
  44. data/spec/spec_helper.rb +0 -5
  45. data/spec/support/libxml.rb +0 -3
  46. data/spec/support/nokogiri.rb +0 -3
  47. data/spec/xml/array_spec.rb +0 -36
  48. data/spec/xml/attributes_spec.rb +0 -71
  49. data/spec/xml/encoding_spec.rb +0 -53
  50. data/spec/xml/namespace_spec.rb +0 -270
  51. data/spec/xml/namespaces_spec.rb +0 -67
  52. data/spec/xml/object_spec.rb +0 -82
  53. data/spec/xml/parser_spec.rb +0 -21
  54. data/spec/xml/text_spec.rb +0 -71
  55. data/test/fixtures/book_malformed.xml +0 -5
  56. data/test/fixtures/book_pair.xml +0 -8
  57. data/test/fixtures/book_text_with_attribute.xml +0 -5
  58. data/test/fixtures/book_valid.xml +0 -5
  59. data/test/fixtures/book_with_authors.xml +0 -7
  60. data/test/fixtures/book_with_contributions.xml +0 -9
  61. data/test/fixtures/book_with_contributors.xml +0 -7
  62. data/test/fixtures/book_with_contributors_attrs.xml +0 -7
  63. data/test/fixtures/book_with_default_namespace.xml +0 -9
  64. data/test/fixtures/book_with_depth.xml +0 -6
  65. data/test/fixtures/book_with_octal_pages.xml +0 -4
  66. data/test/fixtures/book_with_publisher.xml +0 -7
  67. data/test/fixtures/book_with_wrapped_attr.xml +0 -3
  68. data/test/fixtures/dictionary_of_attr_name_clashes.xml +0 -8
  69. data/test/fixtures/dictionary_of_attrs.xml +0 -6
  70. data/test/fixtures/dictionary_of_guarded_names.xml +0 -6
  71. data/test/fixtures/dictionary_of_mixeds.xml +0 -4
  72. data/test/fixtures/dictionary_of_name_clashes.xml +0 -10
  73. data/test/fixtures/dictionary_of_names.xml +0 -4
  74. data/test/fixtures/dictionary_of_texts.xml +0 -10
  75. data/test/fixtures/library.xml +0 -30
  76. data/test/fixtures/library_uppercase.xml +0 -30
  77. data/test/fixtures/muffins.xml +0 -3
  78. data/test/fixtures/nameless_ageless_youth.xml +0 -2
  79. data/test/fixtures/node_with_attr_name_conflicts.xml +0 -1
  80. data/test/fixtures/node_with_name_conflicts.xml +0 -4
  81. data/test/fixtures/numerology.xml +0 -4
  82. data/test/fixtures/person.xml +0 -1
  83. data/test/fixtures/person_with_guarded_mothers.xml +0 -13
  84. data/test/fixtures/person_with_mothers.xml +0 -10
  85. data/test/mocks/dictionaries.rb +0 -57
  86. data/test/mocks/mocks.rb +0 -279
  87. data/test/support/fixtures.rb +0 -11
  88. data/test/unit/definition_test.rb +0 -235
  89. data/test/unit/deprecations_test.rb +0 -24
  90. data/test/unit/to_xml_test.rb +0 -81
  91. data/test/unit/xml_attribute_test.rb +0 -39
  92. data/test/unit/xml_block_test.rb +0 -81
  93. data/test/unit/xml_bool_test.rb +0 -122
  94. data/test/unit/xml_convention_test.rb +0 -150
  95. data/test/unit/xml_hash_test.rb +0 -115
  96. data/test/unit/xml_initialize_test.rb +0 -49
  97. data/test/unit/xml_name_test.rb +0 -141
  98. data/test/unit/xml_namespace_test.rb +0 -31
  99. data/test/unit/xml_object_test.rb +0 -206
  100. data/test/unit/xml_required_test.rb +0 -94
  101. data/test/unit/xml_text_test.rb +0 -71
  102. data/website/index.html +0 -98
@@ -43,7 +43,7 @@ module Representable
43
43
  elsif opts[:from] == :namespace
44
44
  opts[:from] = '*'
45
45
  @sought_type = :namespace
46
- elsif opts[:from].to_s.starts_with?('@')
46
+ elsif opts[:from].to_s =~ /^@/ # FIXME: move me to xml.
47
47
  @sought_type = :attr
48
48
  opts[:from].sub!('@', '')
49
49
  end
@@ -65,7 +65,6 @@ module Representable
65
65
  sought_type.is_a?(Class)
66
66
  end
67
67
 
68
-
69
68
  def name?
70
69
  @name == '*'
71
70
  end
@@ -88,16 +87,6 @@ module Representable
88
87
 
89
88
  value
90
89
  end
91
-
92
- def to_ref
93
- case sought_type
94
- when :attr then XMLAttributeRef
95
- when :text then XMLTextRef
96
- when :namespace then XMLNameSpaceRef
97
- when Symbol then raise ArgumentError, "Invalid type argument #{sought_type}"
98
- else XMLObjectRef
99
- end.new(self)
100
- end
101
90
 
102
91
  private
103
92
  def extract_type(as)
@@ -0,0 +1,66 @@
1
+ require 'json'
2
+ require 'representable/bindings/json_bindings'
3
+
4
+ module Representable
5
+ module JSON
6
+ BINDING_FOR_TYPE = { # TODO: refactor #representable_accessor for better extendability.
7
+ :text => TextBinding,
8
+ }
9
+ def self.binding_for_definition(definition)
10
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
11
+ end
12
+
13
+ def self.included(base)
14
+ base.class_eval do
15
+ include Representable
16
+ include InstanceMethods
17
+ end
18
+ base.extend ClassMethods # DISCUSS: do that dynamically?
19
+ end
20
+
21
+ module ClassMethods
22
+ # Creates a new Ruby object from XML using mapping information declared in the class.
23
+ #
24
+ # Example:
25
+ # book = Book.from_xml("<book><name>Beyond Java</name></book>")
26
+ def from_json(data, options={})
27
+ # DISCUSS: extract #from_json call in Bindings to this place.
28
+ data = ::JSON[data] if data.is_a?(String) # DISCUSS: #from_json sometimes receives a string (in nestings).
29
+ data ||= {}
30
+
31
+ data = data[representation_name] unless options[:wrap] == false
32
+
33
+ create_from_json.tap do |inst|
34
+ refs = representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
35
+
36
+ refs.each do |ref|
37
+ value = ref.value_in(data)
38
+
39
+ inst.send(ref.definition.setter, value)
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+ def create_from_json(*args)
46
+ new(*args)
47
+ end
48
+ end
49
+
50
+ module InstanceMethods # :nodoc:
51
+ # Returns a Nokogiri::XML object representing this object.
52
+ def to_json(options={})
53
+ attributes = {}.tap do |root|
54
+ refs = self.class.representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
55
+
56
+ refs.each do |ref|
57
+ value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
58
+ ref.update_json(root, value) if value
59
+ end
60
+ end
61
+
62
+ options[:wrap] == false ? attributes : {self.class.representation_name => attributes}
63
+ end
64
+ end
65
+ end # Xml
66
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "0.0.1.alpha1"
2
+ VERSION = "0.0.1"
3
3
  end
@@ -1,50 +1,44 @@
1
+ require 'representable'
2
+ require 'representable/bindings/xml_bindings'
3
+
1
4
  module Representable
2
- module Xml
3
- module Declarations
4
- # Sets the name of the XML element that represents this class. Use this
5
- # to override the default lowercase class name.
6
- #
7
- # Example:
8
- # class BookWithPublisher
9
- # xml_name :book
10
- # end
11
- #
12
- # Without the xml_name annotation, the XML mapped tag would have been "bookwithpublisher".
13
- def xml_name(name)
14
- self.explicit_representation_name = name
15
- end
16
-
17
- def xml_accessor(*args) # TODO: remove me, just for back-compat.
18
- representable_accessor(*args)
5
+ module XML
6
+ BINDING_FOR_TYPE = {
7
+ :attr => AttributeBinding,
8
+ :text => TextBinding,
9
+ :namespace=> NamespaceBinding,
10
+ }
11
+
12
+ def self.binding_for_definition(definition)
13
+ (BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
14
+ end
15
+
16
+ def self.included(base)
17
+ base.class_eval do
18
+ include Representable
19
+ include InstanceMethods
20
+ extend ClassMethods
19
21
  end
20
-
22
+ end
23
+
24
+ class Definition < Representable::Definition
25
+ # FIXME: extract xml-specific from Definition.
21
26
  end
22
27
 
23
28
  module ClassMethods
29
+ def definition_class
30
+ Definition
31
+ end
32
+
33
+ # Creates a new Ruby object from XML using mapping information declared in the class.
24
34
  #
25
- # Creates a new Ruby object from XML using mapping information
26
- # annotated in the class.
27
- #
28
- # The input data is either an XML::Node, String, Pathname, or File representing
29
- # the XML document.
30
- #
31
- # Example
32
- # book = Book.from_xml(File.read("book.xml"))
33
- # or
35
+ # Example:
34
36
  # book = Book.from_xml("<book><name>Beyond Java</name></book>")
35
- #
36
- # _initialization_args_ passed into from_xml will be passed into
37
- # the object's .new, prior to populating the xml_attrs.
38
- #
39
- # After the instatiation and xml population
40
- #
41
- # See also: xml_initialize
42
- #
43
37
  def from_xml(data, *args)
44
38
  xml = Nokogiri::XML::Node.from(data)
45
39
 
46
40
  create_from_xml(*args).tap do |inst|
47
- refs = representable_attrs.map {|attr| attr.to_ref }
41
+ refs = representable_attrs.map {|attr| XML.binding_for_definition(attr) }
48
42
 
49
43
  refs.each do |ref|
50
44
  value = ref.value_in(xml)
@@ -61,12 +55,12 @@ module Representable
61
55
  end
62
56
 
63
57
  module InstanceMethods # :nodoc:
64
- # Returns an XML object representing this object
58
+ # Returns a Nokogiri::XML object representing this object.
65
59
  def to_xml(params={})
66
60
  params.reverse_merge!(:name => self.class.representation_name)
67
61
 
68
62
  Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
69
- refs = self.class.representable_attrs.map {|attr| attr.to_ref }
63
+ refs = self.class.representable_attrs.map {|attr| XML.binding_for_definition(attr) }
70
64
 
71
65
  refs.each do |ref|
72
66
  value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
@@ -12,17 +12,18 @@ Gem::Specification.new do |s|
12
12
  s.email = ["apotonick@gmail.com"]
13
13
  s.homepage = "http://representable.apotomo.de"
14
14
  s.summary = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
15
- s.description = %q{CMaps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
15
+ s.description = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_dependency "activesupport", "~> 3.0.0"
22
+ s.add_dependency "activesupport"
23
23
  s.add_dependency "hooks"
24
24
  s.add_dependency "nokogiri"
25
25
  s.add_dependency "i18n"
26
+ s.add_dependency "json"
26
27
 
27
28
  s.add_development_dependency "rspec"
28
29
  s.add_development_dependency "test_xml"
@@ -0,0 +1,110 @@
1
+ require 'test_helper'
2
+
3
+ class ReferenceTest < MiniTest::Spec
4
+ describe "ObjectRef with []" do
5
+ before do
6
+ @ref = Representable::XML::ObjectBinding.new(Representable::Definition.new(:songs, :as => [Hash]))
7
+ end
8
+
9
+ it "responds to #default" do
10
+ assert_equal [], @ref.send(:default)
11
+ end
12
+ end
13
+
14
+
15
+ describe "TextRef#value_in" do
16
+ def parse_xml(xml); Nokogiri::XML::Node.from(xml); end
17
+
18
+ before do
19
+ @ref = Representable::XML::TextBinding.new(Representable::Definition.new(:song))
20
+ end
21
+
22
+ it "returns found value" do
23
+ assert_equal "Unkoil", @ref.value_in(parse_xml("<a><song>Unkoil</song></a>"))
24
+ end
25
+ end
26
+ end
27
+
28
+ class DefinitionTest < MiniTest::Spec
29
+ describe "generic API" do
30
+ before do
31
+ @def = Representable::Definition.new(:songs)
32
+ end
33
+
34
+ it "responds to #typed?" do
35
+ assert ! @def.typed?
36
+ assert Representable::Definition.new(:songs, :as => Hash).typed?
37
+ assert Representable::Definition.new(:songs, :as => [Hash]).typed?
38
+ end
39
+ end
40
+
41
+
42
+ describe "#apply" do
43
+ it "works with a single item" do
44
+ @d = Representable::Definition.new(:song)
45
+ assert_equal 2, @d.apply(1) { |v| v+1 }
46
+ end
47
+
48
+ it "works with collection" do
49
+ @d = Representable::Definition.new(:song, :as => [])
50
+ assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
51
+ end
52
+
53
+ it "skips with collection and nil" do
54
+ @d = Representable::Definition.new(:song, :as => [])
55
+ assert_equal nil, @d.apply(nil) { |v| v+1 }
56
+ end
57
+ end
58
+
59
+ describe ":as => []" do
60
+ before do
61
+ @def = Representable::Definition.new(:songs, :as => [], :tag => :song)
62
+ end
63
+
64
+ it "responds to #accessor" do
65
+ assert_equal "songs", @def.accessor
66
+ end
67
+
68
+ it "responds to #array?" do
69
+ assert @def.array?
70
+ end
71
+
72
+ it "responds to #name" do
73
+ assert_equal "songs", @def.accessor
74
+ end
75
+
76
+ it "responds to #instance_variable_name" do
77
+ assert_equal :"@songs", @def.instance_variable_name
78
+ end
79
+
80
+ it "responds to #setter" do
81
+ assert_equal :"songs=", @def.setter
82
+ end
83
+
84
+ it "responds to #sought_type" do
85
+ assert_equal :text, @def.sought_type
86
+ end
87
+ end
88
+
89
+
90
+ describe ":as => [Item]" do
91
+ before do
92
+ @def = Representable::Definition.new(:songs, :as => [Hash])
93
+ end
94
+
95
+ it "responds to #sought_type" do
96
+ assert_equal Hash, @def.sought_type
97
+ end
98
+ end
99
+
100
+
101
+ describe ":as => Item" do
102
+ before do
103
+ @def = Representable::Definition.new(:songs, :as => Hash)
104
+ end
105
+
106
+ it "responds to #sought_type" do
107
+ assert_equal Hash, @def.sought_type
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,130 @@
1
+ require 'test_helper'
2
+ require 'representable/json'
3
+
4
+ module JsonTest
5
+ class APITest < MiniTest::Spec
6
+ Json = Representable::JSON
7
+ Def = Representable::Definition
8
+
9
+ describe "Xml module" do
10
+ describe "#binding_for_definition" do
11
+ it "returns ObjectBinding" do
12
+ assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :as => Hash))
13
+ end
14
+
15
+ it "returns TextBinding" do
16
+ assert_kind_of Json::TextBinding, Json.binding_for_definition(Def.new(:band))
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ class PropertyTest < MiniTest::Spec
23
+ describe "property :name" do
24
+ class Band
25
+ include Representable::JSON
26
+ representable_property :name
27
+ end
28
+
29
+ it "#from_json creates correct accessors" do
30
+ band = Band.from_json({:band => {:name => "Bombshell Rocks"}}.to_json)
31
+ assert_equal "Bombshell Rocks", band.name
32
+ end
33
+
34
+ it "#to_json serializes correctly" do
35
+ band = Band.new
36
+ band.name = "Cigar"
37
+
38
+ assert_equal "{\"band\"=>{\"name\"=>\"Cigar\"}}", band.to_json.to_s
39
+ end
40
+ end
41
+
42
+ describe "property :name, :as => []" do
43
+ class CD
44
+ include Representable::JSON
45
+ representable_property :songs, :as => []
46
+ end
47
+
48
+ it "#from_json creates correct accessors" do
49
+ cd = CD.from_json({:cd => {:songs => ["Out in the cold", "Microphone"]}}.to_json)
50
+ assert_equal ["Out in the cold", "Microphone"], cd.songs
51
+ end
52
+
53
+ it "#to_json serializes correctly" do
54
+ cd = CD.new
55
+ cd.songs = ["Out in the cold", "Microphone"]
56
+
57
+ assert_equal "{\"cd\"=>{\"songs\"=>[\"Out in the cold\", \"Microphone\"]}}", cd.to_json.to_s
58
+ end
59
+ end
60
+ end
61
+
62
+ class TypedPropertyTest < MiniTest::Spec
63
+ describe ":as => Item" do
64
+ class Label
65
+ include Representable::JSON
66
+ representable_property :name
67
+ end
68
+
69
+ class Album
70
+ include Representable::JSON
71
+ representable_property :label, :as => Label
72
+ end
73
+
74
+ it "#from_json creates one Item instance" do
75
+ album = Album.from_json({:album => {:label => "Fat Wreck"}}.to_json)
76
+ assert_equal "Bad Religion", album.label.name
77
+ end
78
+
79
+ describe "#to_json" do
80
+ it "serializes" do
81
+ label = Label.new; label.name = "Fat Wreck"
82
+ album = Album.new; album.label = label
83
+
84
+ assert_equal "{\"album\"=>{\"label\"=>{\"name\"=>\"Fat Wreck\"}}}", album.to_json.to_s
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ class CollectionTest < MiniTest::Spec
92
+ describe ":as => [Band]" do
93
+ class Band
94
+ include Representable::JSON
95
+ representable_property :name
96
+
97
+ def initialize(name="")
98
+ self.name = name
99
+ end
100
+ end
101
+
102
+ class Compilation
103
+ include Representable::JSON
104
+ representable_property :bands, :as => [Band]
105
+ end
106
+
107
+ describe "#from_json" do
108
+ it "pushes collection items to array" do
109
+ cd = Compilation.from_json({:compilation => {:bands => [
110
+ {:name => "Cobra Skulls"},
111
+ {:name => "Diesel Boy"}]}}.to_json)
112
+ assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
113
+ end
114
+
115
+ it "collections can be empty" do
116
+ cd = Compilation.from_json({:compilation => {}}.to_json)
117
+ assert_equal [], cd.bands
118
+ end
119
+ end
120
+
121
+ it "responds to #to_json" do
122
+ cd = Compilation.new
123
+ cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
124
+
125
+ assert_equal "{\"compilation\"=>{\"bands\"=>[{\"name\"=>\"Diesel Boy\"}, {\"name\"=>\"Bad Religion\"}]}}", cd.to_json.to_s
126
+ end
127
+ end
128
+
129
+ end
130
+ end