representable 3.1.1 → 3.3.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 +4 -4
- data/.github/workflows/ci.yml +17 -4
- data/.github/workflows/ci_jruby.yml +21 -0
- data/.github/workflows/ci_truffleruby.yml +19 -0
- data/CHANGES.md +9 -0
- data/Gemfile +1 -1
- data/README.md +8 -10
- data/lib/representable/hash/binding.rb +1 -0
- data/lib/representable/json.rb +14 -2
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/binding.rb +3 -10
- data/lib/representable/xml.rb +7 -0
- data/representable.gemspec +1 -3
- data/test/as_test.rb +8 -13
- data/test/benchmarking.rb +20 -25
- data/test/binding_test.rb +13 -5
- data/test/cached_test.rb +55 -35
- data/test/class_test.rb +46 -33
- data/test/coercion_test.rb +29 -19
- data/test/config/inherit_test.rb +12 -17
- data/test/config_test.rb +5 -7
- data/test/decorator_scope_test.rb +13 -11
- data/test/decorator_test.rb +11 -11
- data/test/default_test.rb +2 -2
- data/test/defaults_options_test.rb +37 -9
- data/test/definition_test.rb +10 -12
- data/test/examples/example.rb +43 -38
- data/test/examples/object.rb +7 -6
- data/test/exec_context_test.rb +18 -23
- data/test/features_test.rb +29 -10
- data/test/filter_test.rb +29 -20
- data/test/for_collection_test.rb +12 -15
- data/test/generic_test.rb +15 -22
- data/test/getter_setter_test.rb +11 -6
- data/test/hash_bindings_test.rb +21 -12
- data/test/hash_test.rb +53 -38
- data/test/heritage_test.rb +0 -1
- data/test/if_test.rb +25 -21
- data/test/include_exclude_test.rb +24 -16
- data/test/inherit_test.rb +114 -27
- data/test/inline_test.rb +42 -33
- data/test/instance_test.rb +151 -109
- data/test/is_representable_test.rb +19 -17
- data/test/json_test.rb +48 -46
- data/test/lonely_test.rb +35 -33
- data/test/nested_test.rb +16 -19
- data/test/object_test.rb +6 -6
- data/test/option_test.rb +10 -8
- data/test/parse_pipeline_test.rb +21 -15
- data/test/pipeline_test.rb +144 -108
- data/test/populator_test.rb +18 -15
- data/test/prepare_test.rb +15 -17
- data/test/private_options_test.rb +3 -2
- data/test/reader_writer_test.rb +4 -4
- data/test/realistic_benchmark.rb +21 -27
- data/test/render_nil_test.rb +1 -1
- data/test/represent_test.rb +14 -18
- data/test/representable_test.rb +86 -53
- data/test/schema_test.rb +51 -21
- data/test/serialize_deserialize_test.rb +14 -14
- data/test/skip_test.rb +39 -28
- data/test/stringify_hash_test.rb +29 -31
- data/test/test_helper.rb +31 -25
- data/test/test_helper_test.rb +7 -7
- data/test/uncategorized_test.rb +20 -15
- data/test/user_options_test.rb +12 -3
- data/test/wrap_test.rb +27 -17
- data/test/xml_assertions.rb +29 -0
- data/test/xml_bindings_test.rb +5 -8
- data/test/xml_namespace_test.rb +46 -39
- data/test/xml_test.rb +109 -88
- data/test/yaml_test.rb +73 -51
- metadata +11 -97
- data/lib/representable/virtus_coercion.rb +0 -38
- data/test/virtus_coercion_test.rb +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2bb7b8770886210c259064f3d2ba30d7fd3b4e50350e0a9cbd98640261ac3879
|
|
4
|
+
data.tar.gz: 664ee784de146034f31308b2207a121e0a1fbeea38ca3989fc09a1fe04016bc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ecad360b289f31fa0ed925369e3541cb580154c158c1f6b21dd0c0e9ebd98945fe1534292e882521ef3d6c0a24447f55a1b1267deb29282af0d7d1d0b8970c9
|
|
7
|
+
data.tar.gz: dce51b8db19df9005a598272847b55088a9f0ee91c6e4f5aed33075ac43bc28b8edac1a76a020be2e195c3358dc13b1a698ae78cb0d323ce2c3c4ede4a52c912
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
## This file is managed by Terraform.
|
|
2
|
+
## Do not modify this file directly, as it may be overwritten.
|
|
3
|
+
## Please open an issue instead.
|
|
1
4
|
name: CI
|
|
2
5
|
on: [push, pull_request]
|
|
3
6
|
jobs:
|
|
@@ -5,13 +8,23 @@ jobs:
|
|
|
5
8
|
strategy:
|
|
6
9
|
fail-fast: false
|
|
7
10
|
matrix:
|
|
8
|
-
|
|
9
|
-
ruby: [2.5, 2.6, 2.7, '3.0', head]
|
|
11
|
+
ruby: ["3.0", "3.1", "3.2", '3.3', '3.4', '4.0']
|
|
10
12
|
runs-on: ubuntu-latest
|
|
11
13
|
steps:
|
|
12
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v7
|
|
13
15
|
- uses: ruby/setup-ruby@v1
|
|
14
16
|
with:
|
|
15
17
|
ruby-version: ${{ matrix.ruby }}
|
|
16
|
-
bundler-cache: true
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
- run: bundle exec rake
|
|
20
|
+
|
|
21
|
+
test-head:
|
|
22
|
+
continue-on-error: true
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
- uses: ruby/setup-ruby@v1
|
|
27
|
+
with:
|
|
28
|
+
ruby-version: head
|
|
29
|
+
bundler-cache: true
|
|
17
30
|
- run: bundle exec rake
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## This file is managed by Terraform.
|
|
2
|
+
## Do not modify this file directly, as it may be overwritten.
|
|
3
|
+
## Please open an issue instead.
|
|
4
|
+
name: CI JRuby
|
|
5
|
+
on: [push, pull_request]
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
ruby: [jruby, jruby-head]
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
|
+
- uses: ruby/setup-ruby@v1
|
|
16
|
+
with:
|
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
- run: bundle exec rake
|
|
20
|
+
env:
|
|
21
|
+
JRUBY_OPTS: --profile.api
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## This file is managed by Terraform.
|
|
2
|
+
## Do not modify this file directly, as it may be overwritten.
|
|
3
|
+
## Please open an issue instead.
|
|
4
|
+
name: CI TruffleRuby
|
|
5
|
+
on: [push, pull_request]
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
ruby: [truffleruby, truffleruby-head]
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
|
+
- uses: ruby/setup-ruby@v1
|
|
16
|
+
with:
|
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
|
18
|
+
bundler-cache: true
|
|
19
|
+
- run: bundle exec rake
|
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
# 3.3.0
|
|
2
|
+
|
|
3
|
+
* Drop support for Ruby < 3.0.0.
|
|
4
|
+
* Fix deprecations with `MultiJson`.
|
|
5
|
+
|
|
6
|
+
# 3.2.0
|
|
7
|
+
* Drop support for Virtus Coercion
|
|
8
|
+
* Fix deprecation warning with Nokogiri 1.13.0
|
|
9
|
+
|
|
1
10
|
# 3.1.1
|
|
2
11
|
|
|
3
12
|
* Upgrade `trailblazer-option` to `0.1.1` which supports passing an empty `keyword_arguments`.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
# Representable
|
|
2
2
|
|
|
3
3
|
Representable maps Ruby objects to documents and back.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
[](http://trailblazer.to/newsletter/)
|
|
7
|
-
[](https://travis-ci.org/trailblazer/representable)
|
|
4
|
+

|
|
9
6
|
[](http://badge.fury.io/rb/representable)
|
|
10
7
|
|
|
8
|
+
|
|
11
9
|
In other words: Take an object and decorate it with a representer module. This will allow you to render a JSON, XML or YAML document from that object. But that's only half of it! You can also use representers to parse a document and create or populate an object.
|
|
12
10
|
|
|
13
|
-
Representable is helpful for all kind of mappings, rendering and parsing workflows. However, it is mostly useful in API code. Are you planning to write a real REST API with representable? Then check out the [Roar](
|
|
11
|
+
Representable is helpful for all kind of mappings, rendering and parsing workflows. However, it is mostly useful in API code. Are you planning to write a real REST API with representable? Then check out the [Roar](https://github.com/trailblazer/roar) gem first, save work and time and make the world a better place instead.
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
## Full Documentation
|
|
17
15
|
|
|
18
|
-
Representable comes with a rich set of options and semantics for parsing and rendering documents. Its [full documentation](
|
|
16
|
+
Representable comes with a rich set of options and semantics for parsing and rendering documents. Its [full documentation](https://trailblazer.to/2.1/docs/representable.html) can be found on the Trailblazer site.
|
|
19
17
|
|
|
20
18
|
## Example
|
|
21
19
|
|
|
@@ -64,7 +62,7 @@ song = SongRepresenter.new(song).from_json(%{ {"title":"Roxanne"} })
|
|
|
64
62
|
#=> #<Song title="Roxanne", track=nil>
|
|
65
63
|
```
|
|
66
64
|
|
|
67
|
-
Note that parsing hashes per default does [require string keys](
|
|
65
|
+
Note that parsing hashes per default does [require string keys](https://trailblazer.to/2.1/docs/representable.html#representable-api-symbol-keys) and does _not_ pick up symbol keys.
|
|
68
66
|
|
|
69
67
|
|
|
70
68
|
## Collections
|
|
@@ -137,7 +135,7 @@ end
|
|
|
137
135
|
|
|
138
136
|
Representable has many more features and can literally parse and render any kind of document to an arbitrary Ruby object graph.
|
|
139
137
|
|
|
140
|
-
Please check the [official documentation for more](
|
|
138
|
+
Please check the [official documentation for more](https://trailblazer.to/2.1/docs/representable.html#representable-api).
|
|
141
139
|
|
|
142
140
|
|
|
143
141
|
## Installation
|
|
@@ -172,4 +170,4 @@ Representable started as a heavily simplified fork of the ROXML gem. Big thanks
|
|
|
172
170
|
* Copyright (c) 2011-2020 Nick Sutterer <apotonick@gmail.com>
|
|
173
171
|
* ROXML is Copyright (c) 2004-2009 Ben Woosley, Zak Mandhro and Anders Engstrom.
|
|
174
172
|
|
|
175
|
-
Representable is released under the [MIT License](
|
|
173
|
+
Representable is released under the [MIT License](https://www.opensource.org/licenses/MIT).
|
data/lib/representable/json.rb
CHANGED
|
@@ -6,6 +6,18 @@ require "representable"
|
|
|
6
6
|
module Representable
|
|
7
7
|
# Brings #to_json and #from_json to your object.
|
|
8
8
|
module JSON
|
|
9
|
+
if Kernel.const_defined?(:MultiJson) # Ruby 3.0
|
|
10
|
+
module MultiJSON
|
|
11
|
+
def self.generate(*args)
|
|
12
|
+
MultiJson.dump(*args)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.parse(*args)
|
|
16
|
+
MultiJson.load(*args)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
9
21
|
autoload :Collection, "representable/json/collection"
|
|
10
22
|
|
|
11
23
|
extend Hash::ClassMethods
|
|
@@ -33,13 +45,13 @@ module Representable
|
|
|
33
45
|
|
|
34
46
|
# Parses the body as JSON and delegates to #from_hash.
|
|
35
47
|
def from_json(data, *args)
|
|
36
|
-
data =
|
|
48
|
+
data = MultiJSON.parse(data)
|
|
37
49
|
from_hash(data, *args)
|
|
38
50
|
end
|
|
39
51
|
|
|
40
52
|
# Returns a JSON string representing this object.
|
|
41
53
|
def to_json(*args)
|
|
42
|
-
|
|
54
|
+
MultiJSON.generate to_hash(*args)
|
|
43
55
|
end
|
|
44
56
|
|
|
45
57
|
alias_method :render, :to_json
|
|
@@ -2,13 +2,6 @@ require 'representable/binding'
|
|
|
2
2
|
|
|
3
3
|
module Representable
|
|
4
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
|
-
|
|
12
5
|
class Binding < Representable::Binding
|
|
13
6
|
def self.build_for(definition)
|
|
14
7
|
return Collection.new(definition) if definition.array?
|
|
@@ -24,7 +17,7 @@ module Representable
|
|
|
24
17
|
wrap_node = parent
|
|
25
18
|
|
|
26
19
|
if wrap = self[:wrap]
|
|
27
|
-
parent << wrap_node = XML::Node(parent, wrap)
|
|
20
|
+
parent << wrap_node = XML::Node(parent.document, wrap)
|
|
28
21
|
end
|
|
29
22
|
|
|
30
23
|
wrap_node << serialize_for(fragments, parent, as)
|
|
@@ -39,7 +32,7 @@ module Representable
|
|
|
39
32
|
|
|
40
33
|
# Creates wrapped node for the property.
|
|
41
34
|
def serialize_for(value, parent, as)
|
|
42
|
-
node = XML::Node(parent, as) # node doesn't have attr="" attributes!!!
|
|
35
|
+
node = XML::Node(parent.document, as) # node doesn't have attr="" attributes!!!
|
|
43
36
|
serialize_node(node, value, as)
|
|
44
37
|
end
|
|
45
38
|
|
|
@@ -106,7 +99,7 @@ module Representable
|
|
|
106
99
|
class Hash < Collection
|
|
107
100
|
def serialize_for(value, parent, as)
|
|
108
101
|
set_for(parent, value.collect do |k, v|
|
|
109
|
-
node = XML::Node(parent, k)
|
|
102
|
+
node = XML::Node(parent.document, k)
|
|
110
103
|
serialize_node(node, v, as)
|
|
111
104
|
end)
|
|
112
105
|
end
|
data/lib/representable/xml.rb
CHANGED
|
@@ -9,6 +9,13 @@ module Representable
|
|
|
9
9
|
autoload :Collection, 'representable/xml/collection'
|
|
10
10
|
autoload :Namespace, 'representable/xml/namespace'
|
|
11
11
|
|
|
12
|
+
module_function def Node(document, name, attributes={})
|
|
13
|
+
node = document.create_element(name.to_s)
|
|
14
|
+
|
|
15
|
+
attributes.each { |k, v| node[k] = v } # TODO: benchmark.
|
|
16
|
+
node
|
|
17
|
+
end
|
|
18
|
+
|
|
12
19
|
def self.included(base)
|
|
13
20
|
base.class_eval do
|
|
14
21
|
include Representable
|
data/representable.gemspec
CHANGED
|
@@ -19,16 +19,14 @@ 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 = '>= 3.0.0'
|
|
23
23
|
|
|
24
24
|
spec.add_dependency "uber", "< 0.2.0"
|
|
25
25
|
spec.add_dependency "declarative", "< 0.1.0"
|
|
26
26
|
spec.add_dependency "trailblazer-option", ">= 0.1.1", "< 0.2.0"
|
|
27
27
|
|
|
28
28
|
spec.add_development_dependency "rake"
|
|
29
|
-
spec.add_development_dependency "test_xml", ">= 0.1.6"
|
|
30
29
|
spec.add_development_dependency "minitest"
|
|
31
|
-
spec.add_development_dependency "virtus"
|
|
32
30
|
spec.add_development_dependency "dry-types"
|
|
33
31
|
spec.add_development_dependency "ruby-prof" if RUBY_ENGINE == "ruby" # mri
|
|
34
32
|
end
|
data/test/as_test.rb
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class AsTest <
|
|
3
|
+
class AsTest < Minitest::Spec
|
|
4
4
|
for_formats(
|
|
5
|
-
:hash => [Representable::Hash, {"title" => "Wie Es Geht"}, {"title" => "Revolution"}]
|
|
5
|
+
:hash => [Representable::Hash, {"title" => "Wie Es Geht"}, {"title" => "Revolution"}]
|
|
6
6
|
# :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
|
|
7
7
|
# :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
|
|
8
8
|
) do |format, mod, input, output|
|
|
9
|
-
|
|
10
9
|
let(:song) { representer.prepare(Song.new("Revolution")) }
|
|
11
10
|
let(:format) { format }
|
|
12
11
|
|
|
13
|
-
|
|
14
12
|
describe "as: with :symbol" do
|
|
15
13
|
representer!(:module => mod) do
|
|
16
14
|
property :name, :as => :title
|
|
@@ -20,32 +18,29 @@ class AsTest < MiniTest::Spec
|
|
|
20
18
|
it { _(parse(song, input).name).must_equal "Wie Es Geht" }
|
|
21
19
|
end
|
|
22
20
|
|
|
23
|
-
|
|
24
21
|
describe "as: with lambda" do
|
|
25
22
|
representer!(:module => mod) do
|
|
26
|
-
property :name, :as =>
|
|
23
|
+
property :name, :as => ->(*) { self.class.to_s }
|
|
27
24
|
end
|
|
28
25
|
|
|
29
26
|
it { render(song).must_equal_document({"Song" => "Revolution"}) }
|
|
30
27
|
it { _(parse(song, {"Song" => "Wie Es Geht"}).name).must_equal "Wie Es Geht" }
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
|
|
34
30
|
describe "lambda arguments" do
|
|
35
31
|
representer! do
|
|
36
|
-
property :name, :as =>
|
|
32
|
+
property :name, :as => ->(options) { options[:user_options].inspect }
|
|
37
33
|
end
|
|
38
34
|
|
|
39
|
-
it { render(song, user_options:{volume: 1}).must_equal_document({
|
|
40
|
-
it { _(parse(song, {
|
|
35
|
+
it { render(song, user_options: {volume: 1}).must_equal_document({{volume: 1}.inspect => "Revolution"}) }
|
|
36
|
+
it { _(parse(song, {{volume: 1}.inspect => "Wie Es Geht"}, user_options: {volume: 1}).name).must_equal "Wie Es Geht" }
|
|
41
37
|
end
|
|
42
38
|
end
|
|
43
39
|
end
|
|
44
40
|
|
|
45
|
-
|
|
46
41
|
# hash: to_hash(wrap: ) is representation_wrap
|
|
47
42
|
|
|
48
|
-
class AsXmlTest <
|
|
43
|
+
class AsXmlTest < Minitest::Spec
|
|
49
44
|
Band = Struct.new(:name, :label)
|
|
50
45
|
Album = Struct.new(:band)
|
|
51
46
|
Label = Struct.new(:name)
|
data/test/benchmarking.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "benchmark"
|
|
3
3
|
|
|
4
4
|
SONG_PROPERTIES = 1000.times.collect do |i|
|
|
5
5
|
"property_#{i}"
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
module SongRepresenter
|
|
10
9
|
include Representable::JSON
|
|
11
10
|
|
|
@@ -26,7 +25,7 @@ module AlbumRepresenter
|
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
def random_song
|
|
29
|
-
attrs = Hash[SONG_PROPERTIES.collect { |n| [n,n] }]
|
|
28
|
+
attrs = Hash[SONG_PROPERTIES.collect { |n| [n, n] }]
|
|
30
29
|
OpenStruct.new(attrs)
|
|
31
30
|
end
|
|
32
31
|
|
|
@@ -49,30 +48,26 @@ puts times.join("")
|
|
|
49
48
|
## 100 songs, 1000 attrs
|
|
50
49
|
# 0.470000 0.010000 0.480000 ( 0.483708)
|
|
51
50
|
|
|
52
|
-
|
|
53
51
|
### without binding cache:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
# 2.790000 0.030000 2.820000 ( 2.820190)
|
|
57
53
|
|
|
58
54
|
### with extend: on Song, with binding cache>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
55
|
+
# 2.490000 0.030000 2.520000 ( 2.517433) 2.4-3.0
|
|
56
|
+
### without skip?
|
|
57
|
+
# 2.030000 0.020000 2.050000 ( 2.050796) 2.1-2.3
|
|
58
|
+
|
|
59
|
+
### without :writer
|
|
60
|
+
# 2.270000 0.010000 2.280000 ( 2.284530 1.9-2.2
|
|
61
|
+
### without :render_filter
|
|
62
|
+
# 2.020000 0.000000 2.020000 ( 2.030234) 1.5-2.0
|
|
63
|
+
# ##without default_for and skipable?
|
|
64
|
+
# 1.730000 0.010000 1.740000 ( 1.735597 1.4-1.7
|
|
65
|
+
### without :serialize
|
|
66
|
+
# 1.780000 0.010000 1.790000 ( 1.786791) 1.4-1.7
|
|
67
|
+
### using decorator
|
|
68
|
+
# 1.400000 0.030000 1.430000 ( 1.434206) 1.4-1.6
|
|
69
|
+
### with prepare AFTER representable?
|
|
70
|
+
# 1.330000 0.010000 1.340000 ( 1.335900) 1.1-1.3
|
|
76
71
|
|
|
77
72
|
# representable 2.0
|
|
78
73
|
# 3.000000 0.020000 3.020000 ( 3.013031) 2.7-3.0
|
data/test/binding_test.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
|
-
class BindingTest <
|
|
3
|
+
class BindingTest < Minitest::Spec
|
|
4
4
|
Binding = Representable::Binding
|
|
5
5
|
let(:render_nil_definition) { Representable::Definition.new(:song, :render_nil => true) }
|
|
6
6
|
|
|
@@ -20,7 +20,6 @@ class BindingTest < MiniTest::Spec
|
|
|
20
20
|
it { _(Binding.new(Representable::Definition.new(:song)).skipable_empty_value?("Fatal Flu")).must_equal false }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
describe "#default_for" do
|
|
25
24
|
let(:definition) { Representable::Definition.new(:song, :default => "Insider") }
|
|
26
25
|
let(:binding) { Binding.new(definition) }
|
|
@@ -38,9 +37,18 @@ class BindingTest < MiniTest::Spec
|
|
|
38
37
|
it { _(Binding.new(render_nil_definition).default_for(nil)).must_be_nil }
|
|
39
38
|
|
|
40
39
|
# return nil when value nil and render_nil: true, even when :default is set" do
|
|
41
|
-
it {
|
|
40
|
+
it {
|
|
41
|
+
_(
|
|
42
|
+
Binding.new(
|
|
43
|
+
Representable::Definition.new(
|
|
44
|
+
:song, :render_nil => true,
|
|
45
|
+
:default => "The Quest"
|
|
46
|
+
)
|
|
47
|
+
).default_for(nil)
|
|
48
|
+
).must_be_nil
|
|
49
|
+
}
|
|
42
50
|
|
|
43
51
|
# return nil if no :default
|
|
44
52
|
it { _(Binding.new(Representable::Definition.new(:song)).default_for(nil)).must_be_nil }
|
|
45
53
|
end
|
|
46
|
-
end
|
|
54
|
+
end
|
data/test/cached_test.rb
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class Profiler
|
|
4
|
+
SUPPORTED_ENGINES = %w[ruby jruby].freeze
|
|
5
|
+
|
|
6
|
+
def self.available?
|
|
7
|
+
SUPPORTED_ENGINES.include?(RUBY_ENGINE)
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
def self.profile(&block)
|
|
5
11
|
case RUBY_ENGINE
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
when "ruby"
|
|
13
|
+
require "ruby-prof"
|
|
14
|
+
|
|
15
|
+
output = StringIO.new
|
|
16
|
+
profile_result = RubyProf::Profile.profile(&block)
|
|
17
|
+
printer = RubyProf::FlatPrinter.new(profile_result)
|
|
18
|
+
printer.print(output)
|
|
19
|
+
output.string
|
|
20
|
+
when "jruby"
|
|
21
|
+
require "jruby/profiler"
|
|
22
|
+
|
|
23
|
+
output_stream = java.io.ByteArrayOutputStream.new
|
|
24
|
+
print_stream = java.io.PrintStream.new(output_stream)
|
|
25
|
+
profile_result = JRuby::Profiler.profile(&block)
|
|
26
|
+
printer = JRuby::Profiler::FlatProfilePrinter.new(profile_result)
|
|
27
|
+
printer.printProfile(print_stream)
|
|
28
|
+
output_stream.toString
|
|
23
29
|
end
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
|
|
27
|
-
class CachedTest <
|
|
33
|
+
class CachedTest < Minitest::Spec
|
|
28
34
|
# TODO: also test with feature(Cached)
|
|
29
35
|
|
|
30
36
|
module Model
|
|
@@ -37,7 +43,7 @@ class CachedTest < MiniTest::Spec
|
|
|
37
43
|
include Representable::Hash
|
|
38
44
|
feature Representable::Cached
|
|
39
45
|
|
|
40
|
-
property :title, render_filter:
|
|
46
|
+
property :title, render_filter: ->(input, options) { "#{input}:#{options[:options][:user_options]}" }
|
|
41
47
|
property :composer, class: Model::Artist do
|
|
42
48
|
property :name
|
|
43
49
|
end
|
|
@@ -51,9 +57,13 @@ class CachedTest < MiniTest::Spec
|
|
|
51
57
|
collection :songs, decorator: SongRepresenter, class: Model::Song
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
|
|
55
60
|
describe "serialization" do
|
|
56
|
-
let(:album_hash)
|
|
61
|
+
let(:album_hash) do
|
|
62
|
+
{
|
|
63
|
+
"name" => "Louder And Even More Dangerous",
|
|
64
|
+
"songs" => [{"title" => "Southbound:#{{volume: 10}.inspect}"}, {"title" => "Jailbreak:#{{volume: 10}.inspect}"}]
|
|
65
|
+
}
|
|
66
|
+
end
|
|
57
67
|
|
|
58
68
|
let(:song) { Model::Song.new("Jailbreak") }
|
|
59
69
|
let(:song2) { Model::Song.new("Southbound") }
|
|
@@ -64,8 +74,15 @@ class CachedTest < MiniTest::Spec
|
|
|
64
74
|
# album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
|
|
65
75
|
|
|
66
76
|
# makes sure options are passed correctly.
|
|
67
|
-
_(representer.to_hash(user_options: {volume: 9})).must_equal(
|
|
68
|
-
|
|
77
|
+
_(representer.to_hash(user_options: {volume: 9})).must_equal(
|
|
78
|
+
{
|
|
79
|
+
"name" => "Live And Dangerous",
|
|
80
|
+
"songs" => [
|
|
81
|
+
{"title" => "Jailbreak:#{{volume: 9}.inspect}"}, {"title" => "Southbound:#{{volume: 9}.inspect}"},
|
|
82
|
+
{"title" => "Emerald:#{{volume: 9}.inspect}"}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
) # called in Deserializer/Serializer
|
|
69
86
|
|
|
70
87
|
# representer becomes reusable as it is stateless.
|
|
71
88
|
# representer.update!(album2)
|
|
@@ -76,6 +93,8 @@ class CachedTest < MiniTest::Spec
|
|
|
76
93
|
|
|
77
94
|
# profiling
|
|
78
95
|
it do
|
|
96
|
+
skip "no profiler available for #{RUBY_ENGINE}" unless Profiler.available?
|
|
97
|
+
|
|
79
98
|
representer.to_hash
|
|
80
99
|
|
|
81
100
|
data = Profiler.profile { representer.to_hash }
|
|
@@ -88,30 +107,29 @@ class CachedTest < MiniTest::Spec
|
|
|
88
107
|
# - "3 Representable::Decorator.prepare" -> At JRuby
|
|
89
108
|
|
|
90
109
|
# 3 nested decorator is instantiated for 3 Songs, though.
|
|
91
|
-
_(data).must_match(/3\s*(<Class::)?Representable::Decorator
|
|
110
|
+
_(data).must_match(/3\s*(<Class::)?Representable::Decorator>?[\#.]prepare/m)
|
|
92
111
|
# no Binding is instantiated at runtime.
|
|
93
112
|
_(data).wont_match "Representable::Binding#initialize"
|
|
94
113
|
# 2 mappers for Album, Song
|
|
95
114
|
# data.must_match "2 Representable::Mapper::Methods#initialize"
|
|
96
115
|
# title, songs, 3x title, composer
|
|
97
|
-
_(data).must_match(/8\s*Representable::Binding[
|
|
116
|
+
_(data).must_match(/8\s*Representable::Binding[#.]render_pipeline/m)
|
|
98
117
|
_(data).wont_match "render_functions"
|
|
99
118
|
_(data).wont_match "Representable::Binding::Factories#render_functions"
|
|
100
119
|
end
|
|
101
120
|
end
|
|
102
121
|
|
|
103
|
-
|
|
104
122
|
describe "deserialization" do
|
|
105
|
-
let(:album_hash)
|
|
123
|
+
let(:album_hash) do
|
|
106
124
|
{
|
|
107
|
-
"name"=>"Louder And Even More Dangerous",
|
|
108
|
-
"songs"=>[
|
|
109
|
-
{"title"=>"Southbound", "composer"=>{"name"=>"Lynott"}},
|
|
110
|
-
{"title"=>"Jailbreak", "composer"=>{"name"=>"Phil Lynott"}},
|
|
125
|
+
"name" => "Louder And Even More Dangerous",
|
|
126
|
+
"songs" => [
|
|
127
|
+
{"title" => "Southbound", "composer" => {"name"=>"Lynott"}},
|
|
128
|
+
{"title" => "Jailbreak", "composer" => {"name"=>"Phil Lynott"}},
|
|
111
129
|
{"title"=>"Emerald"}
|
|
112
130
|
]
|
|
113
131
|
}
|
|
114
|
-
|
|
132
|
+
end
|
|
115
133
|
|
|
116
134
|
it do
|
|
117
135
|
album = Model::Album.new
|
|
@@ -131,6 +149,8 @@ class CachedTest < MiniTest::Spec
|
|
|
131
149
|
end
|
|
132
150
|
|
|
133
151
|
it "xxx" do
|
|
152
|
+
skip "no profiler available for #{RUBY_ENGINE}" unless Profiler.available?
|
|
153
|
+
|
|
134
154
|
representer = AlbumRepresenter.new(Model::Album.new)
|
|
135
155
|
representer.from_hash(album_hash)
|
|
136
156
|
|
|
@@ -139,12 +159,12 @@ class CachedTest < MiniTest::Spec
|
|
|
139
159
|
# only 2 nested decorators are instantiated, Song, and Artist.
|
|
140
160
|
# Didn't like the regexp?
|
|
141
161
|
# MRI and JRuby has different output formats. See note above.
|
|
142
|
-
_(data).must_match(/5\s*(<Class::)?Representable::Decorator>?[
|
|
162
|
+
_(data).must_match(/5\s*(<Class::)?Representable::Decorator>?[#.]prepare/)
|
|
143
163
|
# a total of 5 properties in the object graph.
|
|
144
164
|
_(data).wont_match "Representable::Binding#initialize"
|
|
145
165
|
|
|
146
166
|
_(data).wont_match "parse_functions" # no pipeline creation.
|
|
147
|
-
_(data).must_match(/10\s*Representable::Binding[
|
|
167
|
+
_(data).must_match(/10\s*Representable::Binding[#.]parse_pipeline/)
|
|
148
168
|
# three mappers for Album, Song, composer
|
|
149
169
|
# data.must_match "3 Representable::Mapper::Methods#initialize"
|
|
150
170
|
# # 6 deserializers as the songs collection uses 2.
|
|
@@ -154,4 +174,4 @@ class CachedTest < MiniTest::Spec
|
|
|
154
174
|
# printer.print(STDOUT)
|
|
155
175
|
end
|
|
156
176
|
end
|
|
157
|
-
end
|
|
177
|
+
end
|