compositor 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.pairs +1 -0
- data/README.md +49 -30
- data/compositor.gemspec +2 -0
- data/lib/compositor.rb +15 -9
- data/lib/compositor/base.rb +22 -10
- data/lib/compositor/composite.rb +19 -30
- data/lib/compositor/dsl.rb +11 -14
- data/lib/compositor/leaf.rb +6 -22
- data/lib/compositor/literal.rb +14 -0
- data/lib/compositor/{hash.rb → map.rb} +1 -1
- data/lib/compositor/version.rb +1 -1
- data/spec/compositor/base_spec.rb +13 -0
- data/spec/compositor/dsl_spec.rb +27 -0
- data/spec/compositor/hash_spec.rb +61 -0
- data/spec/compositor/leaf_spec.rb +34 -0
- data/spec/compositor/list_spec.rb +61 -0
- data/spec/compositor/literal_spec.rb +12 -0
- data/spec/compositor/performance_spec.rb +10 -11
- data/spec/support/sample_dsl.rb +25 -23
- metadata +33 -7
- data/spec/compositor/compositor_spec.rb +0 -168
data/.pairs
CHANGED
data/README.md
CHANGED
@@ -24,30 +24,40 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
For each model that needs a hash/json representation you need to create a ruby class that subclasses Composite::Leaf
|
28
|
-
adds some custom state that's important for rendering that object
|
27
|
+
For each model that needs a hash/json representation you need to create a ruby class that subclasses ```Composite::Leaf```,
|
28
|
+
adds some custom state that's important for rendering that object in addition to ```view_context```, and implement the ```#to_hash```
|
29
|
+
method.
|
30
|
+
|
31
|
+
The ```view_context``` variable is a reference to an object holding necessary helpers for generating JSON, for example
|
32
|
+
view_context is automatically available inside Rails controllers, and contains helper methods necessary to generate application URLs.
|
33
|
+
Outside of Rails application, ```view_context``` can be any other object holding application helpers or state. All
|
34
|
+
subclasses of ```Compositor::Leaf``` inherit view_context reference, and can use it to construct Hash representations.
|
35
|
+
|
36
|
+
We recommend you place your Compositor classes in eg ```app/compositors/*``` directory, that has one compositor
|
37
|
+
class per model class you will be rendering. Example below would be ```app/compositors/user.rb```, a compositor class
|
38
|
+
wrapping ```User``` model.
|
29
39
|
|
30
40
|
```ruby
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
# The actual class name "User" is converted into a DSL method named "user", shown later.
|
42
|
+
|
43
|
+
class UserCompositor < Compositor::Leaf
|
44
|
+
attr_accessor :user
|
45
|
+
|
46
|
+
def initialize(context, user, attrs = {})
|
47
|
+
super(context, attrs)
|
48
|
+
self.user = user
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_hash
|
52
|
+
{
|
53
|
+
id: user.id,
|
54
|
+
username: user.username,
|
55
|
+
location: user.location,
|
56
|
+
bio: user.bio,
|
57
|
+
url: user.url,
|
58
|
+
image_url: context.image_path(user.avatar),
|
59
|
+
...
|
60
|
+
}
|
51
61
|
end
|
52
62
|
end
|
53
63
|
```
|
@@ -56,30 +66,34 @@ This small class automatically registers "user" DSL method, which receives a use
|
|
56
66
|
important attributes.
|
57
67
|
|
58
68
|
Then this class can be merged with other similar "leaf" classes, or another "composite" class, such as
|
59
|
-
Composite::
|
69
|
+
Composite::Map or Composite::List to create a Hash or an Array as the top-level JSON data structure.
|
60
70
|
|
61
71
|
Once the tree of composite objects has been setup, calling #to_hash on the top level object quickly
|
62
72
|
generates hash by walking the tree and merging everything together.
|
63
73
|
|
74
|
+
In the example below, application defines also ```StoreCompositor```, ```ProductCompositor``` classes
|
75
|
+
that similar to ```UserCompositor``` return hash representations of each model object.
|
76
|
+
|
64
77
|
```ruby
|
65
78
|
|
66
|
-
|
67
|
-
|
68
|
-
store store, root: :store
|
69
|
-
user
|
70
|
-
list collection: products, root: :products do |p|
|
79
|
+
compositor = Compositor::DSL.create(context) do
|
80
|
+
map do
|
81
|
+
store @store, root: :store
|
82
|
+
user @user, root: :user
|
83
|
+
list collection: @products, root: :products do |p|
|
71
84
|
product p
|
72
85
|
end
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
76
|
-
puts
|
89
|
+
puts compositor.to_hash # =>
|
77
90
|
|
78
91
|
{
|
79
92
|
:store => {
|
80
93
|
id: 12354,
|
81
94
|
name: "amazon.com",
|
82
95
|
url: "http://www.amazon.com",
|
96
|
+
|
83
97
|
..
|
84
98
|
},
|
85
99
|
:user => {
|
@@ -87,7 +101,8 @@ generates hash by walking the tree and merging everything together.
|
|
87
101
|
username: "kigster",
|
88
102
|
location: "San Francisco",
|
89
103
|
bio: "",
|
90
|
-
url: ""
|
104
|
+
url: "",
|
105
|
+
image_url: "http://cdn-app.domain.com/kigster/avatar/200.jpg"
|
91
106
|
},
|
92
107
|
:products => {
|
93
108
|
[ id: 1234, :name => "Awesome Product", ... ],
|
@@ -96,6 +111,10 @@ generates hash by walking the tree and merging everything together.
|
|
96
111
|
}
|
97
112
|
```
|
98
113
|
|
114
|
+
The context is an object that can contain helpers, instance variables, or anything that can be used
|
115
|
+
within leaves. For example, you can pass in the view_context from the controller to get access to any
|
116
|
+
Rails routes or helpers.
|
117
|
+
|
99
118
|
## Contributing
|
100
119
|
|
101
120
|
1. Fork it
|
data/compositor.gemspec
CHANGED
data/lib/compositor.rb
CHANGED
@@ -3,22 +3,28 @@ require 'compositor/version'
|
|
3
3
|
module Compositor
|
4
4
|
end
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
unless "".respond_to?(:constantize)
|
7
|
+
class String
|
8
|
+
def constantize
|
9
|
+
camel_cased_word = self
|
10
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
11
|
+
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
15
|
+
end
|
14
16
|
end
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
unless "".respond_to?(:underscore)
|
20
|
+
class String
|
21
|
+
def underscore
|
22
|
+
self.gsub(/::/, '/').
|
18
23
|
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
19
24
|
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
20
25
|
tr("-", "_").
|
21
26
|
downcase
|
27
|
+
end
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
data/lib/compositor/base.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Compositor
|
2
2
|
class Base
|
3
3
|
attr_reader :attrs
|
4
|
-
attr_accessor :root, :
|
4
|
+
attr_accessor :root, :context
|
5
5
|
|
6
6
|
def initialize(view_context, attrs = {})
|
7
7
|
@attrs = attrs
|
8
|
-
self.
|
8
|
+
self.context = view_context
|
9
9
|
attrs.each_pair do |key, value|
|
10
10
|
self.send("#{key}=", value)
|
11
11
|
end
|
@@ -23,10 +23,6 @@ module Compositor
|
|
23
23
|
to_hash
|
24
24
|
end
|
25
25
|
|
26
|
-
def collection_to_generator(klazz, collection)
|
27
|
-
collection.map { |o| klazz.new(view_context) }
|
28
|
-
end
|
29
|
-
|
30
26
|
def to_json(options = {})
|
31
27
|
Oj.dump(to_hash)
|
32
28
|
end
|
@@ -40,13 +36,29 @@ module Compositor
|
|
40
36
|
end
|
41
37
|
|
42
38
|
def self.root_class_name(klazz)
|
43
|
-
klazz.name.gsub(
|
39
|
+
klazz.name.gsub(/(.*::)|(Compositor$)/, '').underscore
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.inherited(subclass)
|
43
|
+
method_name = root_class_name(subclass)
|
44
|
+
unless method_name.eql?("base") # check if it's already defined
|
45
|
+
Compositor::DSL.send(:define_method, method_name) do |*args, &block|
|
46
|
+
subclass.
|
47
|
+
new(@context, *args).
|
48
|
+
dsl(self, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def dsl
|
54
|
+
raise "Implement in subclasses"
|
44
55
|
end
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
48
|
-
require_relative 'leaf'
|
49
|
-
require_relative 'composite'
|
50
59
|
require_relative 'dsl'
|
60
|
+
require_relative 'composite'
|
61
|
+
require_relative 'leaf'
|
62
|
+
require_relative 'literal'
|
51
63
|
require_relative 'list'
|
52
|
-
require_relative '
|
64
|
+
require_relative 'map'
|
data/lib/compositor/composite.rb
CHANGED
@@ -2,8 +2,9 @@ module Compositor
|
|
2
2
|
class Composite < Compositor::Base
|
3
3
|
attr_accessor :collection, :renderer
|
4
4
|
|
5
|
-
def initialize(view_context,
|
5
|
+
def initialize(view_context, args = {})
|
6
6
|
super
|
7
|
+
@collection_set = true if args.has_key?(:collection)
|
7
8
|
self.collection ||= []
|
8
9
|
end
|
9
10
|
|
@@ -13,41 +14,29 @@ module Compositor
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
def map_collection
|
17
|
-
self.collection = self.collection.map do |item|
|
18
|
-
yield item
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
17
|
def composite?
|
23
18
|
true
|
24
19
|
end
|
25
20
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
composite.collection = []
|
38
|
-
args[:collection].each do |object|
|
39
|
-
self.instance_exec(object, &block)
|
40
|
-
end
|
41
|
-
elsif block
|
42
|
-
self.instance_eval &block
|
43
|
-
end
|
44
|
-
|
45
|
-
if original_generator
|
46
|
-
self.generator = original_generator
|
47
|
-
self.generator.collection << composite
|
48
|
-
end
|
21
|
+
def dsl(dsl, &block)
|
22
|
+
original_generator = dsl.generator
|
23
|
+
|
24
|
+
dsl.generator = self
|
25
|
+
|
26
|
+
if self.collection && @collection_set && block
|
27
|
+
# reset collection, we'll be mapping it via a block
|
28
|
+
unmapped_collection = collection
|
29
|
+
self.collection = []
|
30
|
+
unmapped_collection.each do |object|
|
31
|
+
dsl.instance_exec(object, &block)
|
49
32
|
end
|
33
|
+
elsif block
|
34
|
+
dsl.instance_eval &block
|
50
35
|
end
|
36
|
+
|
37
|
+
dsl.generator = original_generator if original_generator
|
38
|
+
|
39
|
+
dsl.generator.collection << self if dsl.generator != self
|
51
40
|
end
|
52
41
|
end
|
53
42
|
end
|
data/lib/compositor/dsl.rb
CHANGED
@@ -1,26 +1,23 @@
|
|
1
1
|
module Compositor
|
2
2
|
class DSL
|
3
|
-
attr_reader :
|
4
|
-
attr_accessor :
|
3
|
+
attr_reader :context
|
4
|
+
attr_accessor :generator
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.create(
|
11
|
-
dsl = new(
|
10
|
+
def self.create(context, &block)
|
11
|
+
dsl = new(context)
|
12
|
+
context.instance_variables.each do |variable|
|
13
|
+
dsl.instance_variable_set(variable, context.instance_variable_get(variable))
|
14
|
+
end
|
12
15
|
dsl.instance_eval &block if block
|
13
16
|
dsl
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
17
|
-
|
18
|
-
pagination_url: params[:pagination_url],
|
19
|
-
params: params[:api_params]
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_json
|
23
|
-
generator.to_json
|
19
|
+
def to_json(options = {})
|
20
|
+
generator.to_json(options)
|
24
21
|
end
|
25
22
|
|
26
23
|
def to_hash
|
data/lib/compositor/leaf.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
module Compositor
|
2
2
|
class Leaf < Compositor::Base
|
3
|
-
attr_accessor :object
|
4
|
-
|
5
|
-
def initialize(view_context, object = {}, args = {})
|
6
|
-
if object.is_a?(::Hash)
|
7
|
-
super(view_context, object)
|
8
|
-
else
|
9
|
-
super(view_context, {object: object}.merge!(args))
|
10
|
-
end
|
11
|
-
end
|
12
3
|
|
13
4
|
def root
|
14
5
|
if @root.is_a?(Symbol)
|
@@ -24,19 +15,12 @@ module Compositor
|
|
24
15
|
false
|
25
16
|
end
|
26
17
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
raise "Leaves should be called within composite" unless self.generator.composite?
|
34
|
-
self.generator.collection << leaf
|
35
|
-
else
|
36
|
-
self.generator = leaf
|
37
|
-
end
|
38
|
-
leaf
|
39
|
-
end
|
18
|
+
def dsl(dsl)
|
19
|
+
if dsl.generator
|
20
|
+
raise "Leaves should be called within composite" unless dsl.generator.composite?
|
21
|
+
dsl.generator.collection << self
|
22
|
+
else
|
23
|
+
dsl.generator = self
|
40
24
|
end
|
41
25
|
end
|
42
26
|
end
|
data/lib/compositor/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Compositor::Base do
|
4
|
+
describe "#root_class_name" do
|
5
|
+
it "returns the underscored class name" do
|
6
|
+
Compositor::Base.root_class_name(Object).should == "object"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns the underscored class name with compositor stripped out" do
|
10
|
+
Compositor::Base.root_class_name(DslStringCompositor).should == "dsl_string"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Compositor::DSL do
|
4
|
+
|
5
|
+
let(:context) { Object.new }
|
6
|
+
|
7
|
+
describe '#empty' do
|
8
|
+
it 'returns the default type' do
|
9
|
+
dsl = Compositor::DSL.create(context) do
|
10
|
+
end
|
11
|
+
|
12
|
+
nil.should == dsl.to_hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'instance variables' do
|
17
|
+
it 'allows instance variables from the view context to be accessed in the dsl evaluation' do
|
18
|
+
context.instance_eval do
|
19
|
+
@blah = 1
|
20
|
+
end
|
21
|
+
|
22
|
+
Compositor::DSL.create(context) do
|
23
|
+
raise "Instance variable should be available in DSL" unless @blah == 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Compositor::Map do
|
4
|
+
let(:context) { Object.new }
|
5
|
+
|
6
|
+
it 'returns the generated map' do
|
7
|
+
expected = {
|
8
|
+
tests: {
|
9
|
+
num1: {number: 1},
|
10
|
+
num2: {number: 2},
|
11
|
+
num3: {number: 3}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
dsl = Compositor::DSL.create(context)
|
16
|
+
dsl.map root: :tests do
|
17
|
+
dsl.dsl_int 1, root: :num1
|
18
|
+
dsl.dsl_int 2, root: :num2
|
19
|
+
dsl.dsl_int 3, root: :num3
|
20
|
+
end
|
21
|
+
|
22
|
+
expected.should == dsl.to_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns the generated deeply nested map without explicit receiver' do
|
26
|
+
expected = {
|
27
|
+
tests: {
|
28
|
+
num1: {number: 1},
|
29
|
+
num2: {number: 2},
|
30
|
+
num3: {number: 3},
|
31
|
+
stuff: [
|
32
|
+
{number: 10},
|
33
|
+
{number: 11}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
dsl = Compositor::DSL.create(context)
|
39
|
+
dsl.map root: :tests do
|
40
|
+
dsl_int 1, root: :num1
|
41
|
+
dsl_int 2, root: :num2
|
42
|
+
dsl_int 3, root: :num3
|
43
|
+
list :root => :stuff do
|
44
|
+
dsl_int 10
|
45
|
+
dsl_int 11
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
expected.should == dsl.to_hash
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#empty' do
|
53
|
+
it 'returns the default type' do
|
54
|
+
dsl = Compositor::DSL.create(context) do
|
55
|
+
map collection: [], root: false
|
56
|
+
end
|
57
|
+
|
58
|
+
{}.should == dsl.to_hash
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Compositor::Leaf do
|
4
|
+
let(:context) { Object.new }
|
5
|
+
|
6
|
+
describe '#create' do
|
7
|
+
it "defines #dsl_string method on the DSL class" do
|
8
|
+
dsl = Compositor::DSL.create(context)
|
9
|
+
dsl.should respond_to(:dsl_string)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows calling defined methods via a block" do
|
13
|
+
block_ran = false
|
14
|
+
Compositor::DSL.create(context) do |dsl|
|
15
|
+
dsl.dsl_string
|
16
|
+
block_ran = true
|
17
|
+
end
|
18
|
+
block_ran.should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns last generator called via block" do
|
22
|
+
dsl = Compositor::DSL.create(context) do |dsl|
|
23
|
+
dsl.dsl_string
|
24
|
+
end
|
25
|
+
|
26
|
+
{a: "b"}.should == dsl.to_hash
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns an instance of subclass" do
|
30
|
+
dsl = Compositor::DSL.create(context).dsl_string
|
31
|
+
dsl.should be_kind_of(DslStringCompositor)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Compositor::List do
|
4
|
+
let(:context) { Object.new }
|
5
|
+
|
6
|
+
it 'returns the generated array with the explicit receiver' do
|
7
|
+
integers = [1, 2, 3]
|
8
|
+
expected = {
|
9
|
+
tests: [
|
10
|
+
{number: 1},
|
11
|
+
{number: 2},
|
12
|
+
{number: 3}
|
13
|
+
]
|
14
|
+
}
|
15
|
+
|
16
|
+
dsl = Compositor::DSL.create(context)
|
17
|
+
dsl.list root: :tests, collection: integers do |item|
|
18
|
+
dsl.dsl_int item
|
19
|
+
end
|
20
|
+
|
21
|
+
dsl.to_hash.should == expected
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns the generated array with explicit receiver' do
|
25
|
+
integers = [1, 2, 3]
|
26
|
+
expected = {
|
27
|
+
tests: [
|
28
|
+
{number: 1},
|
29
|
+
{number: 2},
|
30
|
+
{number: 3}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
|
34
|
+
dsl = Compositor::DSL.create(context)
|
35
|
+
dsl.list root: :tests, collection: integers do |item|
|
36
|
+
dsl_int item
|
37
|
+
end
|
38
|
+
|
39
|
+
expected.should == dsl.to_hash
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#empty' do
|
43
|
+
it 'returns the default type' do
|
44
|
+
dsl = Compositor::DSL.create(context) do
|
45
|
+
list collection: [], root: false
|
46
|
+
end
|
47
|
+
|
48
|
+
[].should == dsl.to_hash
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'doesnt execute the block if an empty collection is passed' do
|
52
|
+
dsl = Compositor::DSL.create(context) do
|
53
|
+
list collection: [], root: false do |p|
|
54
|
+
raise "Shouldnt be a DSL" if p.instance_of?(Compositor::DSL)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
[].should == dsl.to_hash
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Performance' do
|
4
4
|
|
5
|
-
let(:
|
5
|
+
let(:context) { Object.new }
|
6
6
|
let(:permitted_dsl_performance_penalty) { 60 } # 60% slower is allowed, any slower is not.)
|
7
7
|
|
8
8
|
describe 'generating DSL' do
|
@@ -15,9 +15,9 @@ describe 'Performance' do
|
|
15
15
|
dsl = nil
|
16
16
|
output = Benchmark.measure do
|
17
17
|
10000.times do
|
18
|
-
dsl = Compositor::DSL.create(
|
19
|
-
|
20
|
-
dsl_string "hello"
|
18
|
+
dsl = Compositor::DSL.create(context) do |dsl|
|
19
|
+
map do
|
20
|
+
dsl_string string: "hello"
|
21
21
|
dsl_int 3
|
22
22
|
list collection: [1, 2, 3], root: :numbers do |number|
|
23
23
|
dsl_int number
|
@@ -30,15 +30,14 @@ describe 'Performance' do
|
|
30
30
|
|
31
31
|
@timing[:dsl] = output.to_s.to_f
|
32
32
|
|
33
|
-
cmp = nil
|
34
33
|
output = Benchmark.measure do
|
35
34
|
10000.times do
|
36
|
-
string =
|
37
|
-
int =
|
38
|
-
list = Compositor::List.new(
|
35
|
+
string = DslStringCompositor.new(context)
|
36
|
+
int = DslIntCompositor.new(context, 3)
|
37
|
+
list = Compositor::List.new(context,
|
39
38
|
root: :numbers,
|
40
|
-
collection: [1, 2, 3].map! { |n|
|
41
|
-
cmp = Compositor::
|
39
|
+
collection: [1, 2, 3].map! { |n| DslIntCompositor.new(context, n) })
|
40
|
+
cmp = Compositor::Map.new(context, collection: [string, int, list])
|
42
41
|
cmp.to_hash.should == {:a => "b", :number => 3, :numbers => [{:number => 1}, {:number => 2}, {:number => 3}]}
|
43
42
|
end
|
44
43
|
end
|
data/spec/support/sample_dsl.rb
CHANGED
@@ -1,33 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
def to_hash
|
4
|
-
{
|
5
|
-
a: "b"
|
6
|
-
}
|
7
|
-
end
|
8
|
-
end
|
1
|
+
class DslStringCompositor < Compositor::Leaf
|
2
|
+
attr_accessor :string
|
9
3
|
|
10
|
-
|
11
|
-
|
4
|
+
def to_hash
|
5
|
+
{
|
6
|
+
a: "b"
|
7
|
+
}
|
8
|
+
end
|
9
|
+
end
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
11
|
+
class DslIntCompositor < Compositor::Leaf
|
12
|
+
attr_accessor :number
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
{
|
20
|
-
number: @number
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end
|
14
|
+
def initialize(view_context, number, attrs = {})
|
15
|
+
super(view_context, {number: number}.merge!(attrs))
|
24
16
|
end
|
25
17
|
|
26
|
-
|
27
|
-
|
18
|
+
def to_hash
|
19
|
+
with_root_element do
|
28
20
|
{
|
29
|
-
|
21
|
+
number: @number
|
30
22
|
}
|
31
23
|
end
|
32
24
|
end
|
33
25
|
end
|
26
|
+
|
27
|
+
class DslObjectCompositor < Compositor::Leaf
|
28
|
+
attr_accessor :object
|
29
|
+
|
30
|
+
def to_hash
|
31
|
+
{
|
32
|
+
a: object
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: compositor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,8 +10,24 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-05-
|
13
|
+
date: 2013-05-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: oj
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
15
31
|
- !ruby/object:Gem::Dependency
|
16
32
|
name: rspec
|
17
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,14 +99,20 @@ files:
|
|
83
99
|
- lib/compositor/base.rb
|
84
100
|
- lib/compositor/composite.rb
|
85
101
|
- lib/compositor/dsl.rb
|
86
|
-
- lib/compositor/hash.rb
|
87
102
|
- lib/compositor/leaf.rb
|
88
103
|
- lib/compositor/list.rb
|
104
|
+
- lib/compositor/literal.rb
|
105
|
+
- lib/compositor/map.rb
|
89
106
|
- lib/compositor/renderer/base.rb
|
90
107
|
- lib/compositor/renderer/iterator.rb
|
91
108
|
- lib/compositor/renderer/merged.rb
|
92
109
|
- lib/compositor/version.rb
|
93
|
-
- spec/compositor/
|
110
|
+
- spec/compositor/base_spec.rb
|
111
|
+
- spec/compositor/dsl_spec.rb
|
112
|
+
- spec/compositor/hash_spec.rb
|
113
|
+
- spec/compositor/leaf_spec.rb
|
114
|
+
- spec/compositor/list_spec.rb
|
115
|
+
- spec/compositor/literal_spec.rb
|
94
116
|
- spec/compositor/performance_spec.rb
|
95
117
|
- spec/spec_helper.rb
|
96
118
|
- spec/support/sample_dsl.rb
|
@@ -114,14 +136,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
136
|
version: '0'
|
115
137
|
requirements: []
|
116
138
|
rubyforge_project:
|
117
|
-
rubygems_version: 1.8.
|
139
|
+
rubygems_version: 1.8.25
|
118
140
|
signing_key:
|
119
141
|
specification_version: 3
|
120
142
|
summary: Composite design pattern with a convenient DSL for building JSON/Hashes of
|
121
143
|
complex objects
|
122
144
|
test_files:
|
123
|
-
- spec/compositor/
|
145
|
+
- spec/compositor/base_spec.rb
|
146
|
+
- spec/compositor/dsl_spec.rb
|
147
|
+
- spec/compositor/hash_spec.rb
|
148
|
+
- spec/compositor/leaf_spec.rb
|
149
|
+
- spec/compositor/list_spec.rb
|
150
|
+
- spec/compositor/literal_spec.rb
|
124
151
|
- spec/compositor/performance_spec.rb
|
125
152
|
- spec/spec_helper.rb
|
126
153
|
- spec/support/sample_dsl.rb
|
127
|
-
has_rdoc:
|
@@ -1,168 +0,0 @@
|
|
1
|
-
require_relative '../spec_helper'
|
2
|
-
|
3
|
-
describe Compositor::DSL do
|
4
|
-
|
5
|
-
let(:view_context) { Object.new }
|
6
|
-
|
7
|
-
describe '#create' do
|
8
|
-
it "defines #dsl_string method on the DSL class" do
|
9
|
-
dsl = Compositor::DSL.create(view_context)
|
10
|
-
dsl.should respond_to(:dsl_string)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "allows calling defined methods via a block" do
|
14
|
-
block_ran = false
|
15
|
-
Compositor::DSL.create(view_context) do |dsl|
|
16
|
-
dsl.dsl_string
|
17
|
-
block_ran = true
|
18
|
-
end
|
19
|
-
block_ran.should be_true
|
20
|
-
end
|
21
|
-
|
22
|
-
it "returns last generator called via block" do
|
23
|
-
dsl = Compositor::DSL.create(view_context) do |dsl|
|
24
|
-
dsl.dsl_string
|
25
|
-
end
|
26
|
-
|
27
|
-
{a: "b"}.should == dsl.to_hash
|
28
|
-
end
|
29
|
-
|
30
|
-
it "returns an instance of subclass" do
|
31
|
-
dsl = Compositor::DSL.create(view_context).dsl_string
|
32
|
-
dsl.should be_kind_of(Compositor::Leaf::DslString)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe '#composite' do
|
37
|
-
it 'returns the generated array with the explicit receiver' do
|
38
|
-
integers = [1, 2, 3]
|
39
|
-
expected = {
|
40
|
-
tests: [
|
41
|
-
{number: 1},
|
42
|
-
{number: 2},
|
43
|
-
{number: 3}
|
44
|
-
]
|
45
|
-
}
|
46
|
-
|
47
|
-
dsl = Compositor::DSL.create(view_context)
|
48
|
-
dsl.list root: :tests, collection: integers do |item|
|
49
|
-
dsl.dsl_int item
|
50
|
-
end
|
51
|
-
|
52
|
-
dsl.to_hash.should == expected
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'returns the generated hash' do
|
56
|
-
expected = {
|
57
|
-
tests: {
|
58
|
-
num1: {number: 1},
|
59
|
-
num2: {number: 2},
|
60
|
-
num3: {number: 3}
|
61
|
-
}
|
62
|
-
}
|
63
|
-
|
64
|
-
dsl = Compositor::DSL.create(view_context)
|
65
|
-
dsl.hash root: :tests do
|
66
|
-
dsl.dsl_int 1, root: :num1
|
67
|
-
dsl.dsl_int 2, root: :num2
|
68
|
-
dsl.dsl_int 3, root: :num3
|
69
|
-
end
|
70
|
-
|
71
|
-
expected.should == dsl.to_hash
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns the generated deeply nested hash' do
|
75
|
-
expected = {
|
76
|
-
tests: {
|
77
|
-
num1: {number: 1},
|
78
|
-
num2: {number: 2},
|
79
|
-
num3: {number: 3},
|
80
|
-
stuff: [
|
81
|
-
{number: 10},
|
82
|
-
{number: 11}
|
83
|
-
]
|
84
|
-
}
|
85
|
-
}
|
86
|
-
|
87
|
-
dsl = Compositor::DSL.create(view_context)
|
88
|
-
dsl.hash root: :tests do
|
89
|
-
dsl.dsl_int 1, root: :num1
|
90
|
-
dsl.dsl_int 2, root: :num2
|
91
|
-
dsl.dsl_int 3, root: :num3
|
92
|
-
dsl.list :root => :stuff do
|
93
|
-
dsl.dsl_int 10
|
94
|
-
dsl.dsl_int 11
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
expected.should == dsl.to_hash
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'returns the generated deeply nested hash without explicit receiver' do
|
102
|
-
expected = {
|
103
|
-
tests: {
|
104
|
-
num1: {number: 1},
|
105
|
-
num2: {number: 2},
|
106
|
-
num3: {number: 3},
|
107
|
-
stuff: [
|
108
|
-
{number: 10},
|
109
|
-
{number: 11}
|
110
|
-
]
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
dsl = Compositor::DSL.create(view_context)
|
115
|
-
dsl.hash root: :tests do
|
116
|
-
dsl_int 1, root: :num1
|
117
|
-
dsl_int 2, root: :num2
|
118
|
-
dsl_int 3, root: :num3
|
119
|
-
list :root => :stuff do
|
120
|
-
dsl_int 10
|
121
|
-
dsl_int 11
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
expected.should == dsl.to_hash
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'returns the generated array with explicit receiver' do
|
129
|
-
integers = [1, 2, 3]
|
130
|
-
expected = {
|
131
|
-
tests: [
|
132
|
-
{number: 1},
|
133
|
-
{number: 2},
|
134
|
-
{number: 3}
|
135
|
-
]
|
136
|
-
}
|
137
|
-
|
138
|
-
dsl = Compositor::DSL.create(view_context)
|
139
|
-
dsl.list root: :tests, collection: integers do |item|
|
140
|
-
dsl_int item
|
141
|
-
end
|
142
|
-
|
143
|
-
expected.should == dsl.to_hash
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe '#empty' do
|
148
|
-
it 'returns the default type' do
|
149
|
-
dsl = Compositor::DSL.create(view_context) do
|
150
|
-
end
|
151
|
-
|
152
|
-
nil.should == dsl.to_hash
|
153
|
-
|
154
|
-
dsl = Compositor::DSL.create(view_context) do
|
155
|
-
hash collection: [], root: false
|
156
|
-
end
|
157
|
-
|
158
|
-
{}.should == dsl.to_hash
|
159
|
-
|
160
|
-
dsl = Compositor::DSL.create(view_context) do
|
161
|
-
list collection: [], root: false
|
162
|
-
end
|
163
|
-
|
164
|
-
[].should == dsl.to_hash
|
165
|
-
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|