representable 3.0.4 → 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 +14 -0
- data/Gemfile +4 -7
- data/LICENSE +1 -1
- data/README.md +12 -12
- data/Rakefile +1 -6
- 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 +8 -3
- data/lib/representable/debug.rb +23 -15
- data/lib/representable/declarative.rb +8 -3
- data/lib/representable/decorator.rb +1 -1
- data/lib/representable/definition.rb +7 -2
- data/lib/representable/deserializer.rb +4 -3
- 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 +6 -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/hash.rb +1 -0
- data/lib/representable/json.rb +5 -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 +2 -1
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +5 -6
- data/lib/representable/xml.rb +7 -10
- data/lib/representable/yaml/binding.rb +1 -0
- data/lib/representable/yaml.rb +3 -3
- data/lib/representable.rb +18 -25
- data/representable.gemspec +3 -3
- data/test/as_test.rb +4 -4
- data/test/binding_test.rb +10 -10
- data/test/cached_test.rb +19 -19
- data/test/class_test.rb +7 -7
- data/test/coercion_test.rb +33 -22
- data/test/config/inherit_test.rb +14 -14
- data/test/config_test.rb +18 -18
- data/test/decorator_scope_test.rb +3 -3
- data/test/decorator_test.rb +17 -17
- data/test/default_test.rb +7 -7
- data/test/definition_test.rb +32 -32
- data/test/{example.rb → examples/example.rb} +0 -0
- data/test/exec_context_test.rb +6 -6
- data/test/features_test.rb +3 -3
- data/test/filter_test.rb +6 -6
- data/test/for_collection_test.rb +2 -2
- data/test/generic_test.rb +3 -3
- data/test/getter_setter_test.rb +5 -5
- data/test/hash_test.rb +19 -19
- data/test/heritage_test.rb +4 -4
- data/test/if_test.rb +6 -6
- data/test/include_exclude_test.rb +12 -12
- data/test/inherit_test.rb +15 -15
- data/test/inline_test.rb +11 -11
- data/test/instance_test.rb +29 -29
- data/test/is_representable_test.rb +10 -10
- data/test/json_test.rb +7 -7
- data/test/lonely_test.rb +16 -16
- data/test/nested_test.rb +7 -7
- data/test/object_test.rb +7 -7
- data/test/option_test.rb +36 -0
- data/test/parse_pipeline_test.rb +3 -3
- data/test/pipeline_test.rb +43 -43
- data/test/populator_test.rb +15 -15
- data/test/prepare_test.rb +2 -2
- 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 +4 -4
- data/test/representable_test.rb +27 -27
- data/test/schema_test.rb +5 -5
- data/test/serialize_deserialize_test.rb +2 -2
- data/test/skip_test.rb +10 -10
- data/test/stringify_hash_test.rb +3 -3
- data/test/test_helper.rb +4 -2
- data/test/uncategorized_test.rb +10 -10
- data/test/user_options_test.rb +4 -4
- data/test/wrap_test.rb +11 -11
- data/test/xml_namespace_test.rb +1 -1
- data/test/xml_test.rb +6 -6
- data/test/yaml_test.rb +20 -20
- metadata +16 -11
- data/.travis.yml +0 -16
- data/lib/representable/autoload.rb +0 -14
- data/test/mongoid_test.rb +0 -31
|
@@ -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)
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
require 'representable/binding'
|
|
2
|
-
require 'representable/hash/binding.rb'
|
|
3
2
|
|
|
4
3
|
module Representable
|
|
5
4
|
module XML
|
|
6
|
-
module_function
|
|
7
|
-
def Node(document, name, attributes={})
|
|
5
|
+
module_function def Node(document, name, attributes={})
|
|
8
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.
|
|
9
7
|
|
|
10
8
|
attributes.each { |k, v| node[k] = v } # TODO: benchmark.
|
|
@@ -18,6 +16,7 @@ module Representable
|
|
|
18
16
|
return AttributeHash.new(definition) if definition.hash? and definition[:use_attributes]
|
|
19
17
|
return Attribute.new(definition) if definition[:attribute]
|
|
20
18
|
return Content.new(definition) if definition[:content]
|
|
19
|
+
|
|
21
20
|
new(definition)
|
|
22
21
|
end
|
|
23
22
|
|
|
@@ -25,7 +24,7 @@ module Representable
|
|
|
25
24
|
wrap_node = parent
|
|
26
25
|
|
|
27
26
|
if wrap = self[:wrap]
|
|
28
|
-
parent << wrap_node = XML::Node(parent, wrap)
|
|
27
|
+
parent << wrap_node = XML::Node(parent.document, wrap)
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
wrap_node << serialize_for(fragments, parent, as)
|
|
@@ -40,7 +39,7 @@ module Representable
|
|
|
40
39
|
|
|
41
40
|
# Creates wrapped node for the property.
|
|
42
41
|
def serialize_for(value, parent, as)
|
|
43
|
-
node = XML::Node(parent, as) # node doesn't have attr="" attributes!!!
|
|
42
|
+
node = XML::Node(parent.document, as) # node doesn't have attr="" attributes!!!
|
|
44
43
|
serialize_node(node, value, as)
|
|
45
44
|
end
|
|
46
45
|
|
|
@@ -107,7 +106,7 @@ module Representable
|
|
|
107
106
|
class Hash < Collection
|
|
108
107
|
def serialize_for(value, parent, as)
|
|
109
108
|
set_for(parent, value.collect do |k, v|
|
|
110
|
-
node = XML::Node(parent, k)
|
|
109
|
+
node = XML::Node(parent.document, k)
|
|
111
110
|
serialize_node(node, v, as)
|
|
112
111
|
end)
|
|
113
112
|
end
|
data/lib/representable/xml.rb
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
gem 'nokogiri', '> 1.10.8'
|
|
2
|
+
require 'nokogiri'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
require 'nokogiri'
|
|
5
|
-
rescue LoadError => _
|
|
6
|
-
abort "Missing dependency 'nokogiri' for Representable::XML. See dependencies section in README.md for details."
|
|
7
|
-
end
|
|
4
|
+
require 'representable'
|
|
8
5
|
|
|
9
6
|
module Representable
|
|
10
7
|
module XML
|
|
8
|
+
autoload :Binding, 'representable/xml/binding'
|
|
9
|
+
autoload :Collection, 'representable/xml/collection'
|
|
10
|
+
autoload :Namespace, 'representable/xml/namespace'
|
|
11
|
+
|
|
11
12
|
def self.included(base)
|
|
12
13
|
base.class_eval do
|
|
13
14
|
include Representable
|
|
@@ -71,7 +72,3 @@ module Representable
|
|
|
71
72
|
end
|
|
72
73
|
end
|
|
73
74
|
end
|
|
74
|
-
|
|
75
|
-
require "representable/xml/binding"
|
|
76
|
-
require "representable/xml/collection"
|
|
77
|
-
require "representable/xml/namespace"
|
data/lib/representable/yaml.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require 'psych'
|
|
2
|
-
require 'representable
|
|
3
|
-
require 'representable/yaml/binding'
|
|
2
|
+
require 'representable'
|
|
4
3
|
|
|
5
4
|
module Representable
|
|
6
5
|
module YAML
|
|
6
|
+
autoload :Binding, 'representable/yaml/binding'
|
|
7
7
|
include Hash
|
|
8
8
|
|
|
9
9
|
def self.included(base)
|
|
@@ -40,7 +40,7 @@ module Representable
|
|
|
40
40
|
stream.to_yaml
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
alias_method :render, :to_yaml
|
|
44
44
|
alias_method :parse, :from_yaml
|
|
45
45
|
end
|
|
46
46
|
end
|
data/lib/representable.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require "uber/delegates"
|
|
2
2
|
require "uber/callable"
|
|
3
|
-
require "declarative/option"
|
|
4
3
|
require "declarative/schema"
|
|
5
4
|
|
|
5
|
+
require "representable/option"
|
|
6
6
|
require "representable/config"
|
|
7
7
|
require "representable/definition"
|
|
8
8
|
require "representable/declarative"
|
|
@@ -16,6 +16,17 @@ require "representable/for_collection"
|
|
|
16
16
|
require "representable/represent"
|
|
17
17
|
|
|
18
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
|
+
|
|
19
30
|
attr_writer :representable_attrs
|
|
20
31
|
|
|
21
32
|
def self.included(base)
|
|
@@ -32,7 +43,7 @@ module Representable
|
|
|
32
43
|
private
|
|
33
44
|
# Reads values from +doc+ and sets properties accordingly.
|
|
34
45
|
def update_properties_from(doc, options, format)
|
|
35
|
-
propagated_options = normalize_options(options)
|
|
46
|
+
propagated_options = normalize_options(**options)
|
|
36
47
|
|
|
37
48
|
representable_map!(doc, propagated_options, format, :uncompile_fragment)
|
|
38
49
|
represented
|
|
@@ -40,25 +51,12 @@ private
|
|
|
40
51
|
|
|
41
52
|
# Compiles the document going through all properties.
|
|
42
53
|
def create_representation_with(doc, options, format)
|
|
43
|
-
propagated_options = normalize_options(options)
|
|
54
|
+
propagated_options = normalize_options(**options)
|
|
44
55
|
|
|
45
56
|
representable_map!(doc, propagated_options, format, :compile_fragment)
|
|
46
57
|
doc
|
|
47
58
|
end
|
|
48
59
|
|
|
49
|
-
class Binding::Map < Array
|
|
50
|
-
def call(method, options)
|
|
51
|
-
each do |bin|
|
|
52
|
-
options[:binding] = bin # this is so much faster than options.merge().
|
|
53
|
-
bin.send(method, options)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# TODO: Merge with Definitions.
|
|
58
|
-
def <<(binding) # can be slow. this is compile time code.
|
|
59
|
-
(existing = find { |bin| bin.name == binding.name }) ? self[index(existing)] = binding : super(binding)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
60
|
|
|
63
61
|
def representable_map(options, format)
|
|
64
62
|
Binding::Map.new(representable_bindings_for(format, options))
|
|
@@ -74,9 +72,8 @@ private
|
|
|
74
72
|
representable_attrs.collect {|definition| format.build(definition) }
|
|
75
73
|
end
|
|
76
74
|
|
|
77
|
-
def normalize_options(options)
|
|
78
|
-
|
|
79
|
-
{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)
|
|
80
77
|
end
|
|
81
78
|
|
|
82
79
|
# Prepares options for a particular nested representer.
|
|
@@ -96,8 +93,8 @@ private
|
|
|
96
93
|
@representable_attrs ||= self.class.definitions
|
|
97
94
|
end
|
|
98
95
|
|
|
99
|
-
def representation_wrap(
|
|
100
|
-
representable_attrs.wrap_for(represented,
|
|
96
|
+
def representation_wrap(options = {})
|
|
97
|
+
representable_attrs.wrap_for(represented, options)
|
|
101
98
|
end
|
|
102
99
|
|
|
103
100
|
def represented
|
|
@@ -118,8 +115,4 @@ private
|
|
|
118
115
|
represented.extend(self)
|
|
119
116
|
end
|
|
120
117
|
end
|
|
121
|
-
|
|
122
|
-
# require "representable/deprecations"
|
|
123
118
|
end
|
|
124
|
-
|
|
125
|
-
require 'representable/autoload'
|
data/representable.gemspec
CHANGED
|
@@ -19,16 +19,16 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
spec.license = "MIT"
|
|
21
21
|
|
|
22
|
-
spec.required_ruby_version = '>=
|
|
22
|
+
spec.required_ruby_version = '>= 2.4.0'
|
|
23
23
|
|
|
24
24
|
spec.add_dependency "uber", "< 0.2.0"
|
|
25
25
|
spec.add_dependency "declarative", "< 0.1.0"
|
|
26
|
-
spec.add_dependency "
|
|
26
|
+
spec.add_dependency "trailblazer-option", ">= 0.1.1", "< 0.2.0"
|
|
27
27
|
|
|
28
28
|
spec.add_development_dependency "rake"
|
|
29
29
|
spec.add_development_dependency "test_xml", ">= 0.1.6"
|
|
30
30
|
spec.add_development_dependency "minitest"
|
|
31
31
|
spec.add_development_dependency "virtus"
|
|
32
|
-
spec.add_development_dependency "
|
|
32
|
+
spec.add_development_dependency "dry-types"
|
|
33
33
|
spec.add_development_dependency "ruby-prof" if RUBY_ENGINE == "ruby" # mri
|
|
34
34
|
end
|
data/test/as_test.rb
CHANGED
|
@@ -17,7 +17,7 @@ class AsTest < MiniTest::Spec
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it { render(song).must_equal_document output }
|
|
20
|
-
it { parse(song, input).name.must_equal "Wie Es Geht" }
|
|
20
|
+
it { _(parse(song, input).name).must_equal "Wie Es Geht" }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ class AsTest < MiniTest::Spec
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it { render(song).must_equal_document({"Song" => "Revolution"}) }
|
|
30
|
-
it { parse(song, {"Song" => "Wie Es Geht"}).name.must_equal "Wie Es Geht" }
|
|
30
|
+
it { _(parse(song, {"Song" => "Wie Es Geht"}).name).must_equal "Wie Es Geht" }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
|
|
@@ -37,7 +37,7 @@ class AsTest < MiniTest::Spec
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
it { render(song, user_options:{volume: 1}).must_equal_document({"{:volume=>1}" => "Revolution"}) }
|
|
40
|
-
it { parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name.must_equal "Wie Es Geht" }
|
|
40
|
+
it { _(parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name).must_equal "Wie Es Geht" }
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
|
@@ -60,6 +60,6 @@ class AsXmlTest < MiniTest::Spec
|
|
|
60
60
|
|
|
61
61
|
it do
|
|
62
62
|
skip
|
|
63
|
-
representer.new(Album.new(Band.new("Offspring"))).to_xml.must_equal ""
|
|
63
|
+
_(representer.new(Album.new(Band.new("Offspring"))).to_xml).must_equal ""
|
|
64
64
|
end
|
|
65
65
|
end
|
data/test/binding_test.rb
CHANGED
|
@@ -8,16 +8,16 @@ class BindingTest < MiniTest::Spec
|
|
|
8
8
|
let(:binding) { Binding.new(render_nil_definition) }
|
|
9
9
|
|
|
10
10
|
# don't skip when present.
|
|
11
|
-
it { binding.skipable_empty_value?("Disconnect, Disconnect").must_equal false }
|
|
11
|
+
it { _(binding.skipable_empty_value?("Disconnect, Disconnect")).must_equal false }
|
|
12
12
|
|
|
13
13
|
# don't skip when it's nil and render_nil: true
|
|
14
|
-
it { binding.skipable_empty_value?(nil).must_equal false }
|
|
14
|
+
it { _(binding.skipable_empty_value?(nil)).must_equal false }
|
|
15
15
|
|
|
16
16
|
# skip when nil and :render_nil undefined.
|
|
17
|
-
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?(nil).must_equal true }
|
|
17
|
+
it { _(Binding.new(Representable::Definition.new(:song)).skipable_empty_value?(nil)).must_equal true }
|
|
18
18
|
|
|
19
19
|
# don't skip when nil and :render_nil undefined.
|
|
20
|
-
it { Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu").must_equal false }
|
|
20
|
+
it { _(Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu")).must_equal false }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
|
|
@@ -26,21 +26,21 @@ class BindingTest < MiniTest::Spec
|
|
|
26
26
|
let(:binding) { Binding.new(definition) }
|
|
27
27
|
|
|
28
28
|
# return value when value present.
|
|
29
|
-
it { binding.default_for("Black And Blue").must_equal "Black And Blue" }
|
|
29
|
+
it { _(binding.default_for("Black And Blue")).must_equal "Black And Blue" }
|
|
30
30
|
|
|
31
31
|
# return false when value false.
|
|
32
|
-
it { binding.default_for(false).must_equal false }
|
|
32
|
+
it { _(binding.default_for(false)).must_equal false }
|
|
33
33
|
|
|
34
34
|
# return default when value nil.
|
|
35
|
-
it { binding.default_for(nil).must_equal "Insider" }
|
|
35
|
+
it { _(binding.default_for(nil)).must_equal "Insider" }
|
|
36
36
|
|
|
37
37
|
# return nil when value nil and render_nil: true.
|
|
38
|
-
it { Binding.new(render_nil_definition).default_for(nil).must_be_nil }
|
|
38
|
+
it { _(Binding.new(render_nil_definition).default_for(nil)).must_be_nil }
|
|
39
39
|
|
|
40
40
|
# return nil when value nil and render_nil: true, even when :default is set" do
|
|
41
|
-
it { Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")).default_for(nil).must_be_nil }
|
|
41
|
+
it { _(Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")).default_for(nil)).must_be_nil }
|
|
42
42
|
|
|
43
43
|
# return nil if no :default
|
|
44
|
-
it { Binding.new(Representable::Definition.new(:song)).default_for(nil).must_be_nil }
|
|
44
|
+
it { _(Binding.new(Representable::Definition.new(:song)).default_for(nil)).must_be_nil }
|
|
45
45
|
end
|
|
46
46
|
end
|
data/test/cached_test.rb
CHANGED
|
@@ -64,7 +64,7 @@ class CachedTest < MiniTest::Spec
|
|
|
64
64
|
# album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
|
|
65
65
|
|
|
66
66
|
# makes sure options are passed correctly.
|
|
67
|
-
representer.to_hash(user_options: {volume: 9}).must_equal({"name"=>"Live And Dangerous",
|
|
67
|
+
_(representer.to_hash(user_options: {volume: 9})).must_equal({"name"=>"Live And Dangerous",
|
|
68
68
|
"songs"=>[{"title"=>"Jailbreak:{:volume=>9}"}, {"title"=>"Southbound:{:volume=>9}"}, {"title"=>"Emerald:{:volume=>9}"}]}) # called in Deserializer/Serializer
|
|
69
69
|
|
|
70
70
|
# representer becomes reusable as it is stateless.
|
|
@@ -81,22 +81,22 @@ class CachedTest < MiniTest::Spec
|
|
|
81
81
|
data = Profiler.profile { representer.to_hash }
|
|
82
82
|
|
|
83
83
|
# 3 songs get decorated.
|
|
84
|
-
data.must_match(/3\s*Representable::Function::Decorate#call/m)
|
|
84
|
+
_(data).must_match(/3\s*Representable::Function::Decorate#call/m)
|
|
85
85
|
# These weird Regexp bellow are a quick workaround to accomodate
|
|
86
86
|
# the different profiler result formats.
|
|
87
87
|
# - "3 <Class::Representable::Decorator>#prepare" -> At MRI Ruby
|
|
88
88
|
# - "3 Representable::Decorator.prepare" -> At JRuby
|
|
89
89
|
|
|
90
90
|
# 3 nested decorator is instantiated for 3 Songs, though.
|
|
91
|
-
data.must_match(/3\s*(<Class::)?Representable::Decorator\>?[\#.]prepare/m)
|
|
91
|
+
_(data).must_match(/3\s*(<Class::)?Representable::Decorator\>?[\#.]prepare/m)
|
|
92
92
|
# no Binding is instantiated at runtime.
|
|
93
|
-
data.wont_match "Representable::Binding#initialize"
|
|
93
|
+
_(data).wont_match "Representable::Binding#initialize"
|
|
94
94
|
# 2 mappers for Album, Song
|
|
95
95
|
# data.must_match "2 Representable::Mapper::Methods#initialize"
|
|
96
96
|
# title, songs, 3x title, composer
|
|
97
|
-
data.must_match(/8\s*Representable::Binding[#\.]render_pipeline/m)
|
|
98
|
-
data.wont_match "render_functions"
|
|
99
|
-
data.wont_match "Representable::Binding::Factories#render_functions"
|
|
97
|
+
_(data).must_match(/8\s*Representable::Binding[#\.]render_pipeline/m)
|
|
98
|
+
_(data).wont_match "render_functions"
|
|
99
|
+
_(data).wont_match "Representable::Binding::Factories#render_functions"
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -118,14 +118,14 @@ class CachedTest < MiniTest::Spec
|
|
|
118
118
|
|
|
119
119
|
AlbumRepresenter.new(album).from_hash(album_hash)
|
|
120
120
|
|
|
121
|
-
album.songs.size.must_equal 3
|
|
122
|
-
album.name.must_equal "Louder And Even More Dangerous"
|
|
123
|
-
album.songs[0].title.must_equal "Southbound"
|
|
124
|
-
album.songs[0].composer.name.must_equal "Lynott"
|
|
125
|
-
album.songs[1].title.must_equal "Jailbreak"
|
|
126
|
-
album.songs[1].composer.name.must_equal "Phil Lynott"
|
|
127
|
-
album.songs[2].title.must_equal "Emerald"
|
|
128
|
-
album.songs[2].composer.must_be_nil
|
|
121
|
+
_(album.songs.size).must_equal 3
|
|
122
|
+
_(album.name).must_equal "Louder And Even More Dangerous"
|
|
123
|
+
_(album.songs[0].title).must_equal "Southbound"
|
|
124
|
+
_(album.songs[0].composer.name).must_equal "Lynott"
|
|
125
|
+
_(album.songs[1].title).must_equal "Jailbreak"
|
|
126
|
+
_(album.songs[1].composer.name).must_equal "Phil Lynott"
|
|
127
|
+
_(album.songs[2].title).must_equal "Emerald"
|
|
128
|
+
_(album.songs[2].composer).must_be_nil
|
|
129
129
|
|
|
130
130
|
# TODO: test options.
|
|
131
131
|
end
|
|
@@ -139,12 +139,12 @@ class CachedTest < MiniTest::Spec
|
|
|
139
139
|
# only 2 nested decorators are instantiated, Song, and Artist.
|
|
140
140
|
# Didn't like the regexp?
|
|
141
141
|
# MRI and JRuby has different output formats. See note above.
|
|
142
|
-
data.must_match(/5\s*(<Class::)?Representable::Decorator>?[#\.]prepare/)
|
|
142
|
+
_(data).must_match(/5\s*(<Class::)?Representable::Decorator>?[#\.]prepare/)
|
|
143
143
|
# a total of 5 properties in the object graph.
|
|
144
|
-
data.wont_match "Representable::Binding#initialize"
|
|
144
|
+
_(data).wont_match "Representable::Binding#initialize"
|
|
145
145
|
|
|
146
|
-
data.wont_match "parse_functions" # no pipeline creation.
|
|
147
|
-
data.must_match(/10\s*Representable::Binding[#\.]parse_pipeline/)
|
|
146
|
+
_(data).wont_match "parse_functions" # no pipeline creation.
|
|
147
|
+
_(data).must_match(/10\s*Representable::Binding[#\.]parse_pipeline/)
|
|
148
148
|
# three mappers for Album, Song, composer
|
|
149
149
|
# data.must_match "3 Representable::Mapper::Methods#initialize"
|
|
150
150
|
# # 6 deserializers as the songs collection uses 2.
|
data/test/class_test.rb
CHANGED
|
@@ -20,8 +20,8 @@ class ClassTest < BaseTest
|
|
|
20
20
|
|
|
21
21
|
it "creates fresh instance and doesn't extend" do
|
|
22
22
|
song = representer.prepare(OpenStruct.new).from_hash({"song" => {"__name__" => "Captured"}}).song
|
|
23
|
-
song.must_be_instance_of RepresentingSong
|
|
24
|
-
song.name.must_equal "Captured"
|
|
23
|
+
_(song).must_be_instance_of RepresentingSong
|
|
24
|
+
_(song.name).must_equal "Captured"
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -33,8 +33,8 @@ class ClassTest < BaseTest
|
|
|
33
33
|
|
|
34
34
|
it "creates fresh instance and doesn't extend" do
|
|
35
35
|
song = representer.prepare(OpenStruct.new).from_hash({"song" => {"__name__" => "Captured"}}).song
|
|
36
|
-
song.must_be_instance_of RepresentingSong
|
|
37
|
-
song.name.must_equal "Captured"
|
|
36
|
+
_(song).must_be_instance_of RepresentingSong
|
|
37
|
+
_(song.name).must_equal "Captured"
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -69,7 +69,7 @@ class ClassTest < BaseTest
|
|
|
69
69
|
property :song, :class => lambda { |options| _klass.args=([options[:fragment],options[:user_options]]); _klass }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
it { representer.prepare(OpenStruct.new).from_hash({"song" => {"name" => "Captured"}}, user_options: {volume: true}).song.class.args.
|
|
72
|
+
it { _(representer.prepare(OpenStruct.new).from_hash({"song" => {"name" => "Captured"}}, user_options: {volume: true}).song.class.args).
|
|
73
73
|
must_equal([{"name"=>"Captured"}, {:volume=>true}]) }
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -90,7 +90,7 @@ class ClassTest < BaseTest
|
|
|
90
90
|
collection :songs, :class => lambda { |options| _klass.args=([options[:fragment],options[:index],options[:user_options]]); _klass }
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
it { representer.prepare(OpenStruct.new).from_hash({"songs" => [{"name" => "Captured"}]}, user_options: {volume: true}).songs.first.class.args.
|
|
93
|
+
it { _(representer.prepare(OpenStruct.new).from_hash({"songs" => [{"name" => "Captured"}]}, user_options: {volume: true}).songs.first.class.args).
|
|
94
94
|
must_equal([{"name"=>"Captured"}, 0, {:volume=>true}]) }
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -109,7 +109,7 @@ class ClassTest < BaseTest
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
it "allows returning arbitrary objects in #from_hash" do
|
|
112
|
-
representer.prepare(OpenStruct.new).from_hash({"song" => 1}).song.must_equal [1,2,3,4]
|
|
112
|
+
_(representer.prepare(OpenStruct.new).from_hash({"song" => 1}).song).must_equal [1,2,3,4]
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
end
|
data/test/coercion_test.rb
CHANGED
|
@@ -1,52 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'test_helper'
|
|
2
4
|
require 'representable/coercion'
|
|
3
5
|
|
|
4
|
-
class
|
|
6
|
+
class CoercionTest < MiniTest::Spec
|
|
5
7
|
representer! do
|
|
6
8
|
include Representable::Coercion
|
|
7
9
|
|
|
8
10
|
property :title # no coercion.
|
|
9
|
-
property :length, :
|
|
11
|
+
property :length, type: Representable::Coercion::Types::Params::Float
|
|
10
12
|
|
|
11
|
-
property :band, :
|
|
12
|
-
property :founded, :
|
|
13
|
+
property :band, class: OpenStruct do
|
|
14
|
+
property :founded, type: Representable::Coercion::Types::Params::Integer
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
collection :songs, :
|
|
16
|
-
property :ok, :
|
|
17
|
+
collection :songs, class: OpenStruct do
|
|
18
|
+
property :ok, type: Representable::Coercion::Types::Params::Bool
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
let(:album)
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
let(:album) do
|
|
23
|
+
OpenStruct.new(title: 'Dire Straits', length: 41.34,
|
|
24
|
+
band: OpenStruct.new(founded: '1977'),
|
|
25
|
+
songs: [OpenStruct.new(ok: 1), OpenStruct.new(ok: 0)])
|
|
26
|
+
end
|
|
23
27
|
|
|
24
|
-
it
|
|
28
|
+
it do
|
|
29
|
+
_(album.extend(representer).to_hash).must_equal({ 'title' => 'Dire Straits',
|
|
30
|
+
'length' => 41.34,
|
|
31
|
+
'band' => { 'founded' => 1977 },
|
|
32
|
+
'songs' => [{ 'ok' => true }, { 'ok' => false }] })
|
|
33
|
+
end
|
|
25
34
|
|
|
26
35
|
it {
|
|
27
36
|
album = OpenStruct.new
|
|
28
37
|
album.extend(representer)
|
|
29
|
-
album.from_hash({
|
|
38
|
+
album.from_hash({ 'title' => 'Dire Straits',
|
|
39
|
+
'length' => '41.34',
|
|
40
|
+
'band' => { 'founded' => '1977' },
|
|
41
|
+
'songs' => [{ 'ok' => 1 }, { 'ok' => 0 }] })
|
|
30
42
|
|
|
31
43
|
# it
|
|
32
|
-
album.length.must_equal 41.34
|
|
33
|
-
album.band.founded.must_equal 1977
|
|
34
|
-
album.songs[0].ok.must_equal true
|
|
44
|
+
_(album.length).must_equal 41.34
|
|
45
|
+
_(album.band.founded).must_equal 1977
|
|
46
|
+
_(album.songs[0].ok).must_equal true
|
|
35
47
|
}
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
describe "with user :parse_filter and :render_filter" do
|
|
49
|
+
describe 'with user :parse_filter and :render_filter' do
|
|
39
50
|
representer! do
|
|
40
51
|
include Representable::Coercion
|
|
41
52
|
|
|
42
|
-
property :length, :
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
property :length, type: Representable::Coercion::Types::Params::Float,
|
|
54
|
+
parse_filter: ->(input, _options) { "#{input}.1" }, # happens BEFORE coercer.
|
|
55
|
+
render_filter: ->(fragment, *) { "#{fragment}.1" }
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
# user's :parse_filter(s) are run before coercion.
|
|
48
|
-
it { OpenStruct.new.extend(representer).from_hash(
|
|
59
|
+
it { _(OpenStruct.new.extend(representer).from_hash('length' => '1').length).must_equal 1.1 }
|
|
49
60
|
# user's :render_filter(s) are run before coercion.
|
|
50
|
-
it { OpenStruct.new(:
|
|
61
|
+
it { _(OpenStruct.new(length: 1).extend(representer).to_hash).must_equal({ 'length' => 1.1 }) }
|
|
51
62
|
end
|
|
52
|
-
end
|
|
63
|
+
end
|