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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +23 -0
- data/CHANGES.md +33 -0
- data/Gemfile +4 -12
- data/LICENSE +1 -1
- data/README.md +12 -13
- data/Rakefile +1 -6
- data/TODO +1 -3
- data/TODO-4.0.md +72 -0
- data/lib/representable/binding.rb +32 -12
- data/lib/representable/cached.rb +1 -1
- data/lib/representable/coercion.rb +8 -6
- data/lib/representable/config.rb +13 -3
- data/lib/representable/debug.rb +23 -15
- data/lib/representable/declarative.rb +12 -7
- data/lib/representable/decorator.rb +1 -1
- data/lib/representable/definition.rb +7 -3
- data/lib/representable/deserializer.rb +5 -4
- data/lib/representable/for_collection.rb +1 -1
- data/lib/representable/hash/allow_symbols.rb +9 -11
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/hash/collection.rb +4 -2
- data/lib/representable/hash.rb +9 -2
- data/lib/representable/hash_methods.rb +3 -2
- data/lib/representable/insert.rb +1 -1
- data/lib/representable/json/collection.rb +3 -0
- data/lib/representable/json.rb +8 -7
- data/lib/representable/object/binding.rb +5 -1
- data/lib/representable/object.rb +1 -1
- data/lib/representable/option.rb +19 -0
- data/lib/representable/pipeline.rb +3 -2
- data/lib/representable/pipeline_factories.rb +4 -2
- data/lib/representable/populator.rb +1 -1
- data/lib/representable/represent.rb +1 -0
- data/lib/representable/serializer.rb +3 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +19 -13
- data/lib/representable/xml/namespace.rb +122 -0
- data/lib/representable/xml.rb +12 -10
- data/lib/representable/yaml/binding.rb +1 -0
- data/lib/representable/yaml.rb +6 -2
- data/lib/representable.rb +19 -25
- data/representable.gemspec +8 -9
- data/test/as_test.rb +7 -7
- data/test/binding_test.rb +14 -14
- data/test/cached_test.rb +59 -49
- data/test/class_test.rb +9 -9
- data/test/coercion_test.rb +33 -22
- data/test/config/inherit_test.rb +14 -14
- data/test/config_test.rb +20 -20
- data/test/decorator_scope_test.rb +4 -4
- data/test/decorator_test.rb +33 -20
- data/test/default_test.rb +8 -8
- data/test/defaults_options_test.rb +3 -3
- data/test/definition_test.rb +38 -40
- data/test/{example.rb → examples/example.rb} +0 -1
- data/test/examples/object.rb +1 -5
- data/test/exec_context_test.rb +8 -8
- data/test/features_test.rb +6 -6
- data/test/filter_test.rb +8 -8
- data/test/for_collection_test.rb +10 -10
- data/test/generic_test.rb +13 -13
- data/test/getter_setter_test.rb +5 -5
- data/test/hash_bindings_test.rb +1 -1
- data/test/hash_test.rb +45 -23
- data/test/heritage_test.rb +16 -13
- data/test/if_test.rb +9 -9
- data/test/include_exclude_test.rb +14 -14
- data/test/inherit_test.rb +18 -18
- data/test/inline_test.rb +24 -24
- data/test/instance_test.rb +31 -31
- data/test/is_representable_test.rb +10 -10
- data/test/json_test.rb +29 -7
- data/test/lonely_test.rb +31 -31
- data/test/nested_test.rb +13 -13
- data/test/object_test.rb +9 -9
- data/test/option_test.rb +36 -0
- data/test/parse_pipeline_test.rb +3 -5
- data/test/pipeline_test.rb +50 -50
- data/test/populator_test.rb +18 -18
- data/test/prepare_test.rb +4 -4
- data/test/private_options_test.rb +2 -2
- data/test/reader_writer_test.rb +2 -2
- data/test/render_nil_test.rb +2 -2
- data/test/represent_test.rb +14 -14
- data/test/representable_test.rb +34 -36
- data/test/schema_test.rb +8 -11
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +14 -14
- data/test/stringify_hash_test.rb +3 -3
- data/test/test_helper.rb +26 -14
- data/test/uncategorized_test.rb +10 -10
- data/test/user_options_test.rb +4 -4
- data/test/wrap_test.rb +19 -19
- data/test/xml_bindings_test.rb +0 -4
- data/test/xml_namespace_test.rb +186 -0
- data/test/xml_test.rb +103 -43
- data/test/yaml_test.rb +51 -26
- metadata +42 -35
- data/.travis.yml +0 -7
- data/lib/representable/TODO.getting_serious +0 -11
- data/lib/representable/autoload.rb +0 -10
- 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
|
-
|
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
|
-
|
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
|
19
|
-
|
20
|
-
|
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
|
@@ -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
|
data/lib/representable/hash.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/representable/insert.rb
CHANGED
data/lib/representable/json.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
|
2
|
-
require "
|
1
|
+
gem "multi_json", '>= 1.14.1'
|
2
|
+
require "multi_json"
|
3
3
|
|
4
|
-
|
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
|
data/lib/representable/object.rb
CHANGED
@@ -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
|
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] = ->(
|
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.
|
@@ -3,7 +3,7 @@ module Representable
|
|
3
3
|
options[:binding].evaluate_option(:getter, input, options)
|
4
4
|
end
|
5
5
|
|
6
|
-
GetValue = ->(
|
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,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 =
|
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 =
|
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
|
-
|
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
|
-
|
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 =
|
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
|
data/lib/representable/xml.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
require 'representable/xml/collection'
|
1
|
+
gem 'nokogiri', '> 1.10.8'
|
2
|
+
require 'nokogiri'
|
4
3
|
|
5
|
-
|
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]
|
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(
|
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.
|
data/lib/representable/yaml.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
require 'representable
|
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
|
-
|
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(
|
99
|
-
representable_attrs.wrap_for(represented,
|
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'
|