representable 3.0.0 → 3.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.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +23 -0
  3. data/CHANGES.md +33 -0
  4. data/Gemfile +4 -12
  5. data/LICENSE +1 -1
  6. data/README.md +12 -13
  7. data/Rakefile +1 -6
  8. data/TODO +1 -3
  9. data/TODO-4.0.md +72 -0
  10. data/lib/representable/binding.rb +32 -12
  11. data/lib/representable/cached.rb +1 -1
  12. data/lib/representable/coercion.rb +8 -6
  13. data/lib/representable/config.rb +13 -3
  14. data/lib/representable/debug.rb +23 -15
  15. data/lib/representable/declarative.rb +12 -7
  16. data/lib/representable/decorator.rb +1 -1
  17. data/lib/representable/definition.rb +7 -3
  18. data/lib/representable/deserializer.rb +5 -4
  19. data/lib/representable/for_collection.rb +1 -1
  20. data/lib/representable/hash/allow_symbols.rb +9 -11
  21. data/lib/representable/hash/binding.rb +1 -0
  22. data/lib/representable/hash/collection.rb +4 -2
  23. data/lib/representable/hash.rb +9 -2
  24. data/lib/representable/hash_methods.rb +3 -2
  25. data/lib/representable/insert.rb +1 -1
  26. data/lib/representable/json/collection.rb +3 -0
  27. data/lib/representable/json.rb +8 -7
  28. data/lib/representable/object/binding.rb +5 -1
  29. data/lib/representable/object.rb +1 -1
  30. data/lib/representable/option.rb +19 -0
  31. data/lib/representable/pipeline.rb +3 -2
  32. data/lib/representable/pipeline_factories.rb +4 -2
  33. data/lib/representable/populator.rb +1 -1
  34. data/lib/representable/represent.rb +1 -0
  35. data/lib/representable/serializer.rb +3 -2
  36. data/lib/representable/version.rb +1 -1
  37. data/lib/representable/xml/binding.rb +19 -13
  38. data/lib/representable/xml/namespace.rb +122 -0
  39. data/lib/representable/xml.rb +12 -10
  40. data/lib/representable/yaml/binding.rb +1 -0
  41. data/lib/representable/yaml.rb +6 -2
  42. data/lib/representable.rb +19 -25
  43. data/representable.gemspec +8 -9
  44. data/test/as_test.rb +7 -7
  45. data/test/binding_test.rb +14 -14
  46. data/test/cached_test.rb +59 -49
  47. data/test/class_test.rb +9 -9
  48. data/test/coercion_test.rb +33 -22
  49. data/test/config/inherit_test.rb +14 -14
  50. data/test/config_test.rb +20 -20
  51. data/test/decorator_scope_test.rb +4 -4
  52. data/test/decorator_test.rb +33 -20
  53. data/test/default_test.rb +8 -8
  54. data/test/defaults_options_test.rb +3 -3
  55. data/test/definition_test.rb +38 -40
  56. data/test/{example.rb → examples/example.rb} +0 -1
  57. data/test/examples/object.rb +1 -5
  58. data/test/exec_context_test.rb +8 -8
  59. data/test/features_test.rb +6 -6
  60. data/test/filter_test.rb +8 -8
  61. data/test/for_collection_test.rb +10 -10
  62. data/test/generic_test.rb +13 -13
  63. data/test/getter_setter_test.rb +5 -5
  64. data/test/hash_bindings_test.rb +1 -1
  65. data/test/hash_test.rb +45 -23
  66. data/test/heritage_test.rb +16 -13
  67. data/test/if_test.rb +9 -9
  68. data/test/include_exclude_test.rb +14 -14
  69. data/test/inherit_test.rb +18 -18
  70. data/test/inline_test.rb +24 -24
  71. data/test/instance_test.rb +31 -31
  72. data/test/is_representable_test.rb +10 -10
  73. data/test/json_test.rb +29 -7
  74. data/test/lonely_test.rb +31 -31
  75. data/test/nested_test.rb +13 -13
  76. data/test/object_test.rb +9 -9
  77. data/test/option_test.rb +36 -0
  78. data/test/parse_pipeline_test.rb +3 -5
  79. data/test/pipeline_test.rb +50 -50
  80. data/test/populator_test.rb +18 -18
  81. data/test/prepare_test.rb +4 -4
  82. data/test/private_options_test.rb +2 -2
  83. data/test/reader_writer_test.rb +2 -2
  84. data/test/render_nil_test.rb +2 -2
  85. data/test/represent_test.rb +14 -14
  86. data/test/representable_test.rb +34 -36
  87. data/test/schema_test.rb +8 -11
  88. data/test/serialize_deserialize_test.rb +2 -2
  89. data/test/skip_test.rb +14 -14
  90. data/test/stringify_hash_test.rb +3 -3
  91. data/test/test_helper.rb +26 -14
  92. data/test/uncategorized_test.rb +10 -10
  93. data/test/user_options_test.rb +4 -4
  94. data/test/wrap_test.rb +19 -19
  95. data/test/xml_bindings_test.rb +0 -4
  96. data/test/xml_namespace_test.rb +186 -0
  97. data/test/xml_test.rb +103 -43
  98. data/test/yaml_test.rb +51 -26
  99. metadata +42 -35
  100. data/.travis.yml +0 -7
  101. data/lib/representable/TODO.getting_serious +0 -11
  102. data/lib/representable/autoload.rb +0 -10
  103. data/test/mongoid_test.rb +0 -31
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Representable
2
4
  module Hash
3
5
  module AllowSymbols
4
- private
6
+ private
7
+
5
8
  def filter_wrap_for(data, *args)
6
9
  super(Conversion.stringify_keys(data), *args)
7
10
  end
@@ -11,17 +14,12 @@ module Representable
11
14
  end
12
15
  end
13
16
 
14
- class Conversion
15
- # DISCUSS: we could think about mixin in IndifferentAccess here (either hashie or ActiveSupport).
16
- # or decorating the hash.
17
+ module Conversion
17
18
  def self.stringify_keys(hash)
18
- hash = hash.dup
19
-
20
- hash.keys.each do |k|
21
- hash[k.to_s] = hash.delete(k)
22
- end
23
- hash
19
+ hash.keys.collect do |key|
20
+ [ key.to_s, hash[key] ]
21
+ end.to_h
24
22
  end
25
23
  end
26
24
  end
27
- end
25
+ end
@@ -5,6 +5,7 @@ module Representable
5
5
  class Binding < Representable::Binding
6
6
  def self.build_for(definition)
7
7
  return Collection.new(definition) if definition.array?
8
+
8
9
  new(definition)
9
10
  end
10
11
 
@@ -1,3 +1,5 @@
1
+ require 'representable/hash'
2
+
1
3
  module Representable::Hash
2
4
  module Collection
3
5
  include Representable::Hash
@@ -20,7 +22,7 @@ module Representable::Hash
20
22
  # TODO: revise lonely collection and build separate pipeline where we just use Serialize, etc.
21
23
 
22
24
  def create_representation_with(doc, options, format)
23
- options = normalize_options(options)
25
+ options = normalize_options(**options)
24
26
  options[:_self] = options
25
27
 
26
28
  bin = representable_bindings_for(format, options).first
@@ -30,7 +32,7 @@ module Representable::Hash
30
32
  end
31
33
 
32
34
  def update_properties_from(doc, options, format)
33
- options = normalize_options(options)
35
+ options = normalize_options(**options)
34
36
  options[:_self] = options
35
37
 
36
38
  bin = representable_bindings_for(format, options).first
@@ -6,6 +6,9 @@ module Representable
6
6
  # If you plan to write your own representer for a new media type, try to use this module (e.g., check how JSON reuses Hash's internal
7
7
  # architecture).
8
8
  module Hash
9
+ autoload :Collection, 'representable/hash/collection'
10
+ autoload :AllowSymbols, 'representable/hash/allow_symbols'
11
+
9
12
  def self.included(base)
10
13
  base.class_eval do
11
14
  include Representable # either in Hero or HeroRepresentation.
@@ -34,15 +37,19 @@ module Representable
34
37
  hash = create_representation_with({}, options, binding_builder)
35
38
 
36
39
  return hash if options[:wrap] == false
37
- return hash unless wrap = options[:wrap] || representation_wrap(options)
40
+ return hash unless (wrap = options[:wrap] || representation_wrap(options))
38
41
 
39
42
  {wrap => hash}
40
43
  end
41
44
 
45
+ alias_method :render, :to_hash
46
+ alias_method :parse, :from_hash
47
+
42
48
  private
43
49
  def filter_wrap(data, options)
44
50
  return data if options[:wrap] == false
45
- return data unless wrap = options[:wrap] || representation_wrap(options)
51
+ return data unless (wrap = options[:wrap] || representation_wrap(options))
52
+
46
53
  filter_wrap_for(data, wrap)
47
54
  end
48
55
 
@@ -20,8 +20,9 @@ module Representable
20
20
  def filter_keys_for!(hash, options)
21
21
  excluding = options[:exclude]
22
22
  # TODO: use same filtering method as in normal representer in Representable#create_representation_with.
23
- return hash unless props = options.delete(:exclude) || options.delete(:include)
24
- hash.reject { |k,v| excluding ? props.include?(k.to_sym) : !props.include?(k.to_sym) }
23
+ return hash unless (props = (options.delete(:exclude) || options.delete(:include)))
24
+
25
+ hash.select { |k, _v| excluding ? !props.include?(k.to_sym) : props.include?(k.to_sym) }
25
26
  end
26
27
  end
27
28
  end
@@ -35,4 +35,4 @@ module Representable
35
35
 
36
36
  Insert = Pipeline::Function::Insert.new
37
37
  end # Pipeline
38
- end
38
+ end
@@ -1,3 +1,6 @@
1
+ require 'representable/json'
2
+ require 'representable/hash/collection'
3
+
1
4
  module Representable::JSON
2
5
  module Collection
3
6
  include Representable::JSON
@@ -1,15 +1,13 @@
1
- require "representable/hash"
2
- require "representable/json/collection"
1
+ gem "multi_json", '>= 1.14.1'
2
+ require "multi_json"
3
3
 
4
- begin
5
- require "multi_json"
6
- rescue LoadError => _
7
- abort "Missing dependency 'multi_json' for Representable::JSON. See dependencies section in README.md for details."
8
- end
4
+ require "representable"
9
5
 
10
6
  module Representable
11
7
  # Brings #to_json and #from_json to your object.
12
8
  module JSON
9
+ autoload :Collection, "representable/json/collection"
10
+
13
11
  extend Hash::ClassMethods
14
12
  include Hash
15
13
 
@@ -43,5 +41,8 @@ module Representable
43
41
  def to_json(*args)
44
42
  MultiJson.dump to_hash(*args)
45
43
  end
44
+
45
+ alias_method :render, :to_json
46
+ alias_method :parse, :from_json
46
47
  end
47
48
  end
@@ -1,8 +1,11 @@
1
+ require 'representable/binding'
2
+
1
3
  module Representable
2
4
  module Object
3
5
  class Binding < Representable::Binding
4
6
  def self.build_for(definition) # TODO: remove default arg.
5
7
  return Collection.new(definition) if definition.array?
8
+
6
9
  new(definition)
7
10
  end
8
11
 
@@ -10,6 +13,7 @@ module Representable
10
13
  fragment = hash.send(as) # :getter? no, that's for parsing!
11
14
 
12
15
  return FragmentNotFound if fragment.nil? and typed?
16
+
13
17
  fragment
14
18
  end
15
19
 
@@ -31,4 +35,4 @@ module Representable
31
35
  end
32
36
  end
33
37
  end
34
- end
38
+ end
@@ -26,4 +26,4 @@ module Representable
26
26
  create_representation_with(nil, options, binding_builder)
27
27
  end
28
28
  end
29
- end
29
+ end
@@ -0,0 +1,19 @@
1
+ require "trailblazer/option"
2
+
3
+ module Representable
4
+ # Extend `Trailblazer::Option` to support static values as callables too.
5
+ class Option < ::Trailblazer::Option
6
+ def self.callable?(value)
7
+ [Proc, Symbol, Uber::Callable].any?{ |kind| value.is_a?(kind) }
8
+ end
9
+
10
+ def self.build(value)
11
+ return ->(*) { value } unless callable?(value) # Wrap static `value` into a proc.
12
+ super
13
+ end
14
+ end
15
+
16
+ def self.Option(value)
17
+ ::Representable::Option.build(value)
18
+ end
19
+ end
@@ -4,11 +4,12 @@ module Representable
4
4
  class Pipeline < Array
5
5
  Stop = Class.new
6
6
 
7
- # options is mutuable.
7
+ # options is mutable.
8
8
  def call(input, options)
9
9
  inject(input) do |memo, block|
10
10
  res = evaluate(block, memo, options)
11
- return(Stop)if Stop == res
11
+ return(Stop) if Stop == res
12
+
12
13
  res
13
14
  end
14
15
  end
@@ -2,7 +2,8 @@
2
2
  module Representable
3
3
  module Binding::Factories
4
4
  def pipeline_for(name, input, options)
5
- return yield unless proc = @definition[name]
5
+ return yield unless (proc = @definition[name])
6
+
6
7
  # proc.(self, options)
7
8
  instance_exec(input, options, &proc)
8
9
  end
@@ -11,6 +12,7 @@ module Representable
11
12
  def collect_for(item_functions)
12
13
  return [Collect[*item_functions]] if array?
13
14
  return [Collect::Hash[*item_functions]] if self[:hash]
15
+
14
16
  item_functions
15
17
  end
16
18
 
@@ -92,4 +94,4 @@ module Representable
92
94
  funcs << (self[:setter] ? Setter : SetValue)
93
95
  end
94
96
  end
95
- end
97
+ end
@@ -21,7 +21,7 @@ module Representable
21
21
  def self.apply!(options)
22
22
  return unless populator = options[:populator]
23
23
 
24
- options[:parse_pipeline] = ->(input, options) do
24
+ options[:parse_pipeline] = ->(_input, _opts) do
25
25
  pipeline = Pipeline[*parse_functions] # TODO: AssignFragment
26
26
  pipeline = Pipeline::Insert.(pipeline, SetValue, delete: true) # remove the setter function.
27
27
  pipeline = Pipeline::Insert.(pipeline, populator, replace: CreateObject::Populator) # let the actual populator do the job.
@@ -1,6 +1,7 @@
1
1
  module Representable::Represent
2
2
  def represent(represented, array_class=Array)
3
3
  return for_collection.prepare(represented) if represented.is_a?(array_class)
4
+
4
5
  prepare(represented)
5
6
  end
6
7
  end
@@ -3,7 +3,7 @@ module Representable
3
3
  options[:binding].evaluate_option(:getter, input, options)
4
4
  end
5
5
 
6
- GetValue = ->(input, options) { options[:binding].send(:exec_context, options).send(options[:binding].getter) }
6
+ GetValue = ->(_input, options) { options[:binding].send(:exec_context, options).public_send(options[:binding].getter) }
7
7
 
8
8
  Writer = ->(input, options) do
9
9
  options[:binding].evaluate_option(:writer, input, options)
@@ -37,6 +37,7 @@ module Representable
37
37
 
38
38
  Serialize = ->(input, options) do
39
39
  return if input.nil? # DISCUSS: how can we prevent that?
40
+
40
41
  binding, options = options[:binding], options[:options] # FIXME: rename to :local_options.
41
42
 
42
43
  options_for_nested = OptionsForNested.(options, binding)
@@ -51,4 +52,4 @@ module Representable
51
52
  # Warning: don't rely on AssignAs/AssignName, i am not sure if i leave that as functions.
52
53
  AssignAs = ->(input, options) { options[:as] = As.(input, options); input }
53
54
  AssignName = ->(input, options) { options[:as] = options[:binding].name; input }
54
- end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "3.0.0"
2
+ VERSION = "3.2.0"
3
3
  end
@@ -1,8 +1,14 @@
1
1
  require 'representable/binding'
2
- require 'representable/hash/binding.rb'
3
2
 
4
3
  module Representable
5
4
  module XML
5
+ module_function def Node(document, name, attributes={})
6
+ node = Nokogiri::XML::Node.new(name.to_s, document) # Java::OrgW3cDom::DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
7
+
8
+ attributes.each { |k, v| node[k] = v } # TODO: benchmark.
9
+ node
10
+ end
11
+
6
12
  class Binding < Representable::Binding
7
13
  def self.build_for(definition)
8
14
  return Collection.new(definition) if definition.array?
@@ -10,6 +16,7 @@ module Representable
10
16
  return AttributeHash.new(definition) if definition.hash? and definition[:use_attributes]
11
17
  return Attribute.new(definition) if definition[:attribute]
12
18
  return Content.new(definition) if definition[:content]
19
+
13
20
  new(definition)
14
21
  end
15
22
 
@@ -17,7 +24,7 @@ module Representable
17
24
  wrap_node = parent
18
25
 
19
26
  if wrap = self[:wrap]
20
- parent << wrap_node = node_for(parent, wrap)
27
+ parent << wrap_node = XML::Node(parent.document, wrap)
21
28
  end
22
29
 
23
30
  wrap_node << serialize_for(fragments, parent, as)
@@ -32,12 +39,15 @@ module Representable
32
39
 
33
40
  # Creates wrapped node for the property.
34
41
  def serialize_for(value, parent, as)
35
- node = node_for(parent, as)
36
- serialize_node(node, value)
42
+ node = XML::Node(parent.document, as) # node doesn't have attr="" attributes!!!
43
+ serialize_node(node, value, as)
37
44
  end
38
45
 
39
- def serialize_node(node, value)
40
- return value if typed?
46
+ def serialize_node(node, value, as)
47
+ if typed?
48
+ value.name = as if as != self[:name]
49
+ return value
50
+ end
41
51
 
42
52
  node.content = value
43
53
  node
@@ -60,11 +70,7 @@ module Representable
60
70
  def find_nodes(doc, as)
61
71
  selector = as
62
72
  selector = "#{self[:wrap]}/#{as}" if self[:wrap]
63
- nodes = doc.xpath(selector)
64
- end
65
-
66
- def node_for(parent, name)
67
- Nokogiri::XML::Node.new(name.to_s, parent.document)
73
+ doc.xpath(selector) # nodes
68
74
  end
69
75
 
70
76
  def content_for(node) # TODO: move this into a ScalarDecorator.
@@ -100,8 +106,8 @@ module Representable
100
106
  class Hash < Collection
101
107
  def serialize_for(value, parent, as)
102
108
  set_for(parent, value.collect do |k, v|
103
- node = node_for(parent, k)
104
- serialize_node(node, v)
109
+ node = XML::Node(parent.document, k)
110
+ serialize_node(node, v, as)
105
111
  end)
106
112
  end
107
113
 
@@ -0,0 +1,122 @@
1
+ module Representable::XML
2
+ # Experimental!
3
+ # Best explanation so far: http://books.xmlschemata.org/relaxng/relax-CHP-11-SECT-1.html
4
+ #
5
+ # Note: This module doesn't work with JRuby because Nokogiri uses a completely
6
+ # different implementation in Java which has other requirements that we couldn't fulfil.
7
+ # Please wait for Representable 4 where we replace Nokogiri with Oga.
8
+ module Namespace
9
+ def self.included(includer)
10
+ includer.extend(DSL)
11
+ end
12
+
13
+ module DSL
14
+ def namespace(namespace)
15
+ representable_attrs.options[:local_namespace] = namespace
16
+ representable_attrs.options[:namespace_mappings] ||= {}
17
+ representable_attrs.options[:namespace_mappings][namespace] = nil # this might get overwritten via #namespace_def later.
18
+ end
19
+
20
+ def namespace_def(mapping)
21
+ namespace_defs.merge!(mapping.invert)
22
+ end
23
+
24
+ # :private:
25
+ def namespace_defs
26
+ representable_attrs.options[:namespace_mappings] ||= {}
27
+ end
28
+
29
+ def property(name, options={})
30
+ uri = representable_attrs.options[:local_namespace] # per default, a property belongs to the local namespace.
31
+ options[:namespace] ||= uri # don't override if already set.
32
+
33
+ # a nested representer is automatically assigned "its" local namespace. It's like saying
34
+ # property :author, namespace: "http://ns/author" do ... end
35
+
36
+ super.tap do |dfn|
37
+ if dfn.typed? # FIXME: ouch, this should be doable with property's API to hook into the creation process.
38
+ dfn.merge!( namespace: dfn.representer_module.representable_attrs.options[:local_namespace] )
39
+
40
+ update_namespace_defs!(namespace_defs)
41
+ end
42
+ end
43
+ end
44
+
45
+ # :private:
46
+ # super ugly hack
47
+ # recursively injects the namespace_defs into all representers of this tree. will be done better in 4.0.
48
+ def update_namespace_defs!(namespace_defs)
49
+ representable_attrs.each do |dfn|
50
+ dfn.merge!(namespace_defs: namespace_defs) # this only helps with scalars
51
+
52
+ if dfn.typed?
53
+ representer = Class.new(dfn.representer_module) # don't pollute classes.
54
+ representer.update_namespace_defs!(namespace_defs)
55
+ dfn.merge!(extend: representer)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ module AsWithNamespace
62
+ def write(doc, fragment, as)
63
+ super(doc, fragment, prefixed(self, as))
64
+ end
65
+
66
+ # FIXME: this is shit, the NestedOptions is executed too late here!
67
+ def read(node, as)
68
+ super(node, prefixed(self, as))
69
+ end
70
+
71
+ private
72
+ def prefixed(dfn, as)
73
+ uri = dfn[:namespace] # this is generic behavior and per property
74
+ prefix = dfn[:namespace_defs][uri]
75
+ as = Namespace::Namespaced(prefix, as)
76
+ end
77
+ end
78
+
79
+ # FIXME: some "bug" in Representable's XML doesn't consider the container tag, so we could theoretically pick the
80
+ # wrong namespaced tag here :O
81
+ def from_node(node, options={})
82
+ super
83
+ end
84
+
85
+ def to_node(options={})
86
+ local_uri = representable_attrs.options[:local_namespace] # every decorator MUST have a local namespace.
87
+ prefix = self.class.namespace_defs[local_uri]
88
+
89
+ root_tag = [prefix, representation_wrap(options)].compact.join(":")
90
+
91
+ options = { wrap: root_tag }.merge(options)
92
+
93
+ # TODO: there should be an easier way to pass a set of options to all nested #to_node decorators.
94
+ representable_attrs.keys.each do |property|
95
+ options[property.to_sym] = { show_definition: false, namespaces: options[:namespaces] }
96
+ end
97
+
98
+ super(options).tap do |node|
99
+ add_namespace_definitions!(node, self.class.namespace_defs) unless options[:show_definition] == false
100
+ end
101
+ end
102
+
103
+ # "Physically" add `xmlns` attributes to `node`.
104
+ def add_namespace_definitions!(node, namespaces)
105
+ namespaces.each do |uri, prefix|
106
+ prefix = prefix.nil? ? nil : prefix.to_s
107
+ node.add_namespace_definition(prefix, uri)
108
+ end
109
+ end
110
+
111
+ def self.Namespaced(prefix, name)
112
+ [ prefix, name ].compact.join(":")
113
+ end
114
+
115
+ # FIXME: this is a PoC, we need a better API to inject code.
116
+ def representable_map(options, format)
117
+ super.tap do |map|
118
+ map.each { |bin| bin.extend(AsWithNamespace) unless bin.is_a?(Binding::Attribute) }
119
+ end
120
+ end
121
+ end
122
+ end
@@ -1,15 +1,14 @@
1
- require 'representable'
2
- require 'representable/xml/binding'
3
- require 'representable/xml/collection'
1
+ gem 'nokogiri', '> 1.10.8'
2
+ require 'nokogiri'
4
3
 
5
- begin
6
- require 'nokogiri'
7
- rescue LoadError => _
8
- abort "Missing dependency 'nokogiri' for Representable::XML. See dependencies section in README.md for details."
9
- end
4
+ require 'representable'
10
5
 
11
6
  module Representable
12
7
  module XML
8
+ autoload :Binding, 'representable/xml/binding'
9
+ autoload :Collection, 'representable/xml/collection'
10
+ autoload :Namespace, 'representable/xml/namespace'
11
+
13
12
  def self.included(base)
14
13
  base.class_eval do
15
14
  include Representable
@@ -46,16 +45,19 @@ module Representable
46
45
 
47
46
  # Returns a Nokogiri::XML object representing this object.
48
47
  def to_node(options={})
49
- options[:doc] ||= Nokogiri::XML::Document.new
48
+ options[:doc] = Nokogiri::XML::Document.new # DISCUSS: why do we need a fresh Document here?
50
49
  root_tag = options[:wrap] || representation_wrap(options)
51
50
 
52
- create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, options[:doc]), options, Binding)
51
+ create_representation_with(Node(options[:doc], root_tag.to_s), options, Binding)
53
52
  end
54
53
 
55
54
  def to_xml(*args)
56
55
  to_node(*args).to_s
57
56
  end
58
57
 
58
+ alias_method :render, :to_xml
59
+ alias_method :parse, :from_xml
60
+
59
61
  private
60
62
  def remove_namespaces?
61
63
  # TODO: make local Config easily extendable so you get Config#remove_ns? etc.
@@ -5,6 +5,7 @@ module Representable
5
5
  class Binding < Representable::Hash::Binding
6
6
  def self.build_for(definition)
7
7
  return Collection.new(definition) if definition.array?
8
+
8
9
  new(definition)
9
10
  end
10
11
 
@@ -1,8 +1,9 @@
1
- require 'representable/hash'
2
- require 'representable/yaml/binding'
1
+ require 'psych'
2
+ require 'representable'
3
3
 
4
4
  module Representable
5
5
  module YAML
6
+ autoload :Binding, 'representable/yaml/binding'
6
7
  include Hash
7
8
 
8
9
  def self.included(base)
@@ -38,5 +39,8 @@ module Representable
38
39
  doc.children << to_ast(*args)
39
40
  stream.to_yaml
40
41
  end
42
+
43
+ alias_method :render, :to_yaml
44
+ alias_method :parse, :from_yaml
41
45
  end
42
46
  end
data/lib/representable.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require "uber/delegates"
2
-
2
+ require "uber/callable"
3
3
  require "declarative/schema"
4
4
 
5
+ require "representable/option"
5
6
  require "representable/config"
6
7
  require "representable/definition"
7
8
  require "representable/declarative"
@@ -15,6 +16,17 @@ require "representable/for_collection"
15
16
  require "representable/represent"
16
17
 
17
18
  module Representable
19
+ autoload :Binding, 'representable/binding'
20
+ autoload :HashMethods, 'representable/hash_methods'
21
+ autoload :Decorator, 'representable/decorator'
22
+
23
+ autoload :Hash, 'representable/hash'
24
+ autoload :JSON, 'representable/json'
25
+ autoload :Object, 'representable/object'
26
+ autoload :YAML, 'representable/yaml'
27
+ autoload :XML, 'representable/xml'
28
+
29
+
18
30
  attr_writer :representable_attrs
19
31
 
20
32
  def self.included(base)
@@ -31,7 +43,7 @@ module Representable
31
43
  private
32
44
  # Reads values from +doc+ and sets properties accordingly.
33
45
  def update_properties_from(doc, options, format)
34
- propagated_options = normalize_options(options)
46
+ propagated_options = normalize_options(**options)
35
47
 
36
48
  representable_map!(doc, propagated_options, format, :uncompile_fragment)
37
49
  represented
@@ -39,25 +51,12 @@ private
39
51
 
40
52
  # Compiles the document going through all properties.
41
53
  def create_representation_with(doc, options, format)
42
- propagated_options = normalize_options(options)
54
+ propagated_options = normalize_options(**options)
43
55
 
44
56
  representable_map!(doc, propagated_options, format, :compile_fragment)
45
57
  doc
46
58
  end
47
59
 
48
- class Binding::Map < Array
49
- def call(method, options)
50
- each do |bin|
51
- options[:binding] = bin # this is so much faster than options.merge().
52
- bin.send(method, options)
53
- end
54
- end
55
-
56
- # TODO: Merge with Definitions.
57
- def <<(binding) # can be slow. this is compile time code.
58
- (existing = find { |bin| bin.name == binding.name }) ? self[index(existing)] = binding : super(binding)
59
- end
60
- end
61
60
 
62
61
  def representable_map(options, format)
63
62
  Binding::Map.new(representable_bindings_for(format, options))
@@ -73,9 +72,8 @@ private
73
72
  representable_attrs.collect {|definition| format.build(definition) }
74
73
  end
75
74
 
76
- def normalize_options(options)
77
- return options if options.any?
78
- {user_options: {}}.merge(options) # TODO: use keyword args once we drop 2.0.
75
+ def normalize_options(user_options: {}, **options)
76
+ { user_options: user_options }.merge(options)
79
77
  end
80
78
 
81
79
  # Prepares options for a particular nested representer.
@@ -95,8 +93,8 @@ private
95
93
  @representable_attrs ||= self.class.definitions
96
94
  end
97
95
 
98
- def representation_wrap(*args)
99
- representable_attrs.wrap_for(represented, *args)
96
+ def representation_wrap(options = {})
97
+ representable_attrs.wrap_for(represented, options)
100
98
  end
101
99
 
102
100
  def represented
@@ -117,8 +115,4 @@ private
117
115
  represented.extend(self)
118
116
  end
119
117
  end
120
-
121
- # require "representable/deprecations"
122
118
  end
123
-
124
- require 'representable/autoload'