conglomerate 0.10.0 → 0.11.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/README.md +18 -19
- data/lib/conglomerate.rb +14 -0
- data/lib/conglomerate/builder_call.rb +87 -0
- data/lib/conglomerate/builder_serializer.rb +78 -0
- data/lib/conglomerate/collection_builder.rb +13 -0
- data/lib/conglomerate/datum.rb +1 -1
- data/lib/conglomerate/datum_builder.rb +25 -0
- data/lib/conglomerate/ext/commands.rb +36 -0
- data/lib/conglomerate/item_builder.rb +11 -0
- data/lib/conglomerate/link_builder.rb +21 -0
- data/lib/conglomerate/mixin_ivar_helper.rb +53 -0
- data/lib/conglomerate/particle.rb +1 -1
- data/lib/conglomerate/particle_builder.rb +95 -0
- data/lib/conglomerate/query_builder.rb +23 -0
- data/lib/conglomerate/root_builder.rb +9 -0
- data/lib/conglomerate/template_builder.rb +16 -0
- data/lib/conglomerate/tree_serializer.rb +14 -3
- data/lib/conglomerate/version.rb +1 -1
- data/spec/command_ext_spec.rb +56 -0
- data/spec/particle_builder_spec.rb +249 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 299095559c8ba18987524ef7e2702c2f2ad341b6
|
4
|
+
data.tar.gz: 65a914ac9c9c2a31086c39d1577685347339b568
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2374f028927ee394aabeb148a0513c9dd2b1a59e9cd3c68a926c98c9bd1b12391ad13d19c39eccabdc7cf9e56dd446c9c0f1a31dbe2451bd2d430a348f0efa1d
|
7
|
+
data.tar.gz: 9a7c73782ab8de2f30131cc60f5449941159b3168b5c228e1af4810f12a49372cd18265e7a3170712c592bd0b288f6324bb0eb45c8fb3d76cdb180f482c57a18
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# Conglomerate
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/conglomerate)
|
4
|
-
[
|
4
|
+
[ ](https://www.codeship.io/projects/24758)
|
6
5
|
[](https://codeclimate.com/github/teamsnap/conglomerate)
|
7
6
|
[](https://coveralls.io/r/teamsnap/conglomerate?branch=master)
|
8
7
|
[](https://gemnasium.com/teamsnap/conglomerate)
|
@@ -36,20 +35,29 @@ Or install it yourself as:
|
|
36
35
|
```ruby
|
37
36
|
# Step 1: Create a serializer
|
38
37
|
class TeamSerializer
|
39
|
-
include Conglomerate
|
38
|
+
include Conglomerate::RootBuilder
|
40
39
|
|
41
40
|
href { teams_url }
|
42
|
-
item_href { |item| team_url(item.id) }
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
attribute :event_ids, :rel => :events { |item| event_url(item.event_ids.join(",")) }
|
42
|
+
item "Team" do |item|
|
43
|
+
href { team_url(item.id) }
|
47
44
|
|
48
|
-
|
45
|
+
datum :id
|
46
|
+
datum :name
|
47
|
+
datum :event_ids
|
49
48
|
|
50
|
-
|
49
|
+
link :events, :href => Proc.new { event_url(item.event_ids.join(",")) }
|
50
|
+
end
|
51
|
+
|
52
|
+
link :root, :href => Proc.new { root_url }
|
53
|
+
|
54
|
+
query :search, :href => Proc.new { search_items_url } do
|
55
|
+
datum :id
|
56
|
+
end
|
51
57
|
|
52
|
-
|
58
|
+
template do
|
59
|
+
datum :name
|
60
|
+
end
|
53
61
|
end
|
54
62
|
|
55
63
|
# Step 2: Serialize any object
|
@@ -107,15 +115,6 @@ end
|
|
107
115
|
]
|
108
116
|
}
|
109
117
|
],
|
110
|
-
"commands": [
|
111
|
-
{
|
112
|
-
"rel": "populate",
|
113
|
-
"href": "http://example.com/teams/populate",
|
114
|
-
"data": [
|
115
|
-
{"name": "id", "value": ""}
|
116
|
-
]
|
117
|
-
}
|
118
|
-
],
|
119
118
|
"template": {
|
120
119
|
"data": [
|
121
120
|
{"name": "name", "value": ""}
|
data/lib/conglomerate.rb
CHANGED
@@ -11,6 +11,20 @@ require_relative "conglomerate/query"
|
|
11
11
|
require_relative "conglomerate/command"
|
12
12
|
require_relative "conglomerate/collection"
|
13
13
|
require_relative "conglomerate/tree_deserializer"
|
14
|
+
require_relative "conglomerate/root"
|
15
|
+
|
16
|
+
require_relative "conglomerate/mixin_ivar_helper"
|
17
|
+
require_relative "conglomerate/builder_call"
|
18
|
+
|
19
|
+
require_relative "conglomerate/builder_serializer"
|
20
|
+
require_relative "conglomerate/particle_builder"
|
21
|
+
require_relative "conglomerate/link_builder"
|
22
|
+
require_relative "conglomerate/datum_builder"
|
23
|
+
require_relative "conglomerate/query_builder"
|
24
|
+
require_relative "conglomerate/template_builder"
|
25
|
+
require_relative "conglomerate/item_builder"
|
26
|
+
require_relative "conglomerate/collection_builder"
|
27
|
+
require_relative "conglomerate/root_builder"
|
14
28
|
|
15
29
|
module Conglomerate
|
16
30
|
def self.serialize(serializable)
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
class BuilderCall
|
3
|
+
attr_accessor :name, :opts, :block, :builder, :array, :iterates
|
4
|
+
|
5
|
+
def initialize(name: nil, opts: {}, block:, builder:, array:, iterates:)
|
6
|
+
self.name = name
|
7
|
+
self.opts = opts
|
8
|
+
self.block = block
|
9
|
+
self.builder = builder
|
10
|
+
self.array = array
|
11
|
+
self.iterates = iterates
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(context, objects, attrs, attr_name)
|
15
|
+
BuilderCallInstance.new(self, context, objects, attrs, attr_name).run
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
class BuilderCallInstance
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
attr_accessor :bc, :context, :objects, :attrs, :attr_name
|
24
|
+
|
25
|
+
delegate [:name, :opts, :block, :builder, :array, :iterates] => :bc
|
26
|
+
|
27
|
+
def initialize(bc, context, objects, attrs, attr_name)
|
28
|
+
self.bc = bc
|
29
|
+
self.context = context
|
30
|
+
self.objects = objects
|
31
|
+
self.attrs = attrs
|
32
|
+
self.attr_name = attr_name
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
return unless should_run?
|
37
|
+
|
38
|
+
if iterates
|
39
|
+
attrs[attr_name] ||= []
|
40
|
+
|
41
|
+
objects.each do |item|
|
42
|
+
attrs[attr_name] << invoke(item)
|
43
|
+
end
|
44
|
+
elsif array
|
45
|
+
attrs[attr_name] ||= []
|
46
|
+
attrs[attr_name] << invoke
|
47
|
+
else
|
48
|
+
attrs[attr_name] = invoke
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def invoke(item = nil)
|
53
|
+
serializer = builder.serializer
|
54
|
+
builder_klass = Class.new do
|
55
|
+
include serializer
|
56
|
+
end
|
57
|
+
|
58
|
+
if item
|
59
|
+
builder_klass.class_exec(item, &block) if block
|
60
|
+
else
|
61
|
+
builder_klass.class_eval(&block) if block
|
62
|
+
end
|
63
|
+
|
64
|
+
builder = builder_klass.new(objects, :name => name, :context => context)
|
65
|
+
|
66
|
+
attrs = {}
|
67
|
+
|
68
|
+
opts.each do |key, value|
|
69
|
+
attrs[key] = value_from_proc(value)
|
70
|
+
end
|
71
|
+
|
72
|
+
builder.build(attrs)
|
73
|
+
end
|
74
|
+
|
75
|
+
def value_from_proc(value)
|
76
|
+
value = context.instance_eval(&value) if value.respond_to?(:call)
|
77
|
+
value
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def should_run?
|
83
|
+
!opts[:if] || (opts[:if] && opts[:if].call)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module BuilderSerializer
|
3
|
+
include Conglomerate::MixinIvarHelper
|
4
|
+
|
5
|
+
mi_ivar_accessor :objects, :context, :_builder_name
|
6
|
+
|
7
|
+
def initialize(objects, name: nil, context: nil)
|
8
|
+
self.objects = [*objects].compact
|
9
|
+
self.context = context
|
10
|
+
self._builder_name = name.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(attrs = {})
|
14
|
+
internal_build(attrs)
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize
|
18
|
+
Conglomerate.serialize(build)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :object
|
24
|
+
|
25
|
+
def internal_build(attrs = {})
|
26
|
+
builder_values.each do |name|
|
27
|
+
value = get_class_value(name)
|
28
|
+
value = value_from_proc(value)
|
29
|
+
attrs[name.to_sym] = value if attrs[name.to_sym] == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
builder_builders.each do |b|
|
33
|
+
run_builder_calls(b, attrs)
|
34
|
+
end
|
35
|
+
|
36
|
+
self.object = builder_type.new(attrs)
|
37
|
+
|
38
|
+
object
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_builder_calls(b, attrs)
|
42
|
+
calls = builder_calls[b[:command]] || []
|
43
|
+
|
44
|
+
calls.each do |call|
|
45
|
+
call.run(context, objects, attrs, b[:name])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def builder_type
|
50
|
+
self.class.instance_variable_get("@_builder_type")
|
51
|
+
end
|
52
|
+
|
53
|
+
def builder_values
|
54
|
+
self.class.instance_variable_get("@_builder_values")
|
55
|
+
end
|
56
|
+
|
57
|
+
def builder_calls
|
58
|
+
self.class.instance_variable_get("@_builder_calls")
|
59
|
+
end
|
60
|
+
|
61
|
+
def builder_builders
|
62
|
+
self.class.instance_variable_get("@_builder_builders")
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_class_value(name)
|
66
|
+
self.class.instance_variable_get("@#{name}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def value_from_proc(value)
|
70
|
+
value = context.instance_eval(&value) if value.respond_to?(:call)
|
71
|
+
value
|
72
|
+
end
|
73
|
+
|
74
|
+
def context
|
75
|
+
instance_variable_get("@context")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module CollectionBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Collection
|
6
|
+
|
7
|
+
value :href
|
8
|
+
builder :item, Conglomerate::ItemBuilder, :name => :items, :array => true, :iterates => true
|
9
|
+
builder :link, Conglomerate::LinkBuilder, :name => :links, :array => true
|
10
|
+
builder :query, Conglomerate::QueryBuilder, :name => :queries, :array => true
|
11
|
+
builder :template, Conglomerate::TemplateBuilder, :name => :template
|
12
|
+
end
|
13
|
+
end
|
data/lib/conglomerate/datum.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module DatumBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Datum
|
6
|
+
|
7
|
+
module BuildOverride
|
8
|
+
def build(attrs = {})
|
9
|
+
item = objects.first
|
10
|
+
val = item.send(_builder_name) if item
|
11
|
+
|
12
|
+
attrs = {
|
13
|
+
:name => _builder_name,
|
14
|
+
:value => val
|
15
|
+
}.merge(attrs)
|
16
|
+
|
17
|
+
super(attrs)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
value :name
|
22
|
+
value :value
|
23
|
+
value :prompt
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
class Command
|
3
|
+
include Conglomerate::Particle
|
4
|
+
|
5
|
+
attribute :href, :type => String, :required => true
|
6
|
+
attribute :rel, :type => String, :required => true
|
7
|
+
attribute :name, :type => String
|
8
|
+
attribute :prompt, :type => String
|
9
|
+
|
10
|
+
array :data, :contains => Datum
|
11
|
+
end
|
12
|
+
|
13
|
+
class CommandBuilder
|
14
|
+
include Conglomerate::ParticleBuilder
|
15
|
+
|
16
|
+
builds Conglomerate::Command
|
17
|
+
|
18
|
+
module BuildOverride
|
19
|
+
def build(attrs = {})
|
20
|
+
instance_variable_set("@objects", nil)
|
21
|
+
super(attrs.merge({
|
22
|
+
:rel => _builder_name
|
23
|
+
}))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
value :href
|
28
|
+
value :rel
|
29
|
+
value :name
|
30
|
+
value :prompt
|
31
|
+
|
32
|
+
builder :datum, Conglomerate::DatumBuilder, :name => :data, :array => true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Conglomerate::CollectionBuilder.send(:builder, :command, Conglomerate::CommandBuilder, :name => :commands, :array => true)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module ItemBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Item
|
6
|
+
|
7
|
+
value :href
|
8
|
+
builder :datum, Conglomerate::DatumBuilder, :name => :data, :array => true
|
9
|
+
builder :link, Conglomerate::LinkBuilder, :name => :links, :array => true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module LinkBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Link
|
6
|
+
|
7
|
+
module BuildOverride
|
8
|
+
def build(attrs = {})
|
9
|
+
super(attrs.merge({
|
10
|
+
:rel => _builder_name
|
11
|
+
}))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
value :href
|
16
|
+
value :rel
|
17
|
+
value :name
|
18
|
+
value :render
|
19
|
+
value :prompt
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module MixinIvarHelper
|
3
|
+
def self.included(klass)
|
4
|
+
klass.send(:extend, ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def mc_ivar_writer(*names)
|
11
|
+
names.each do |name|
|
12
|
+
self.send(:define_singleton_method, "#{name}=") do |val|
|
13
|
+
instance_variable_set("@#{name}", val)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def mc_ivar_reader(*names)
|
19
|
+
names.each do |name|
|
20
|
+
self.send(:define_singleton_method,name) do
|
21
|
+
instance_variable_get("@#{name}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def mi_ivar_writer(*names)
|
27
|
+
names.each do |name|
|
28
|
+
self.send(:define_method, "#{name}=") do |val|
|
29
|
+
instance_variable_set("@#{name}", val)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def mi_ivar_reader(*names)
|
35
|
+
names.each do |name|
|
36
|
+
self.send(:define_method,name) do
|
37
|
+
instance_variable_get("@#{name}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def mc_ivar_accessor(*names)
|
43
|
+
mc_ivar_writer(*names)
|
44
|
+
mc_ivar_reader(*names)
|
45
|
+
end
|
46
|
+
|
47
|
+
def mi_ivar_accessor(*names)
|
48
|
+
mi_ivar_writer(*names)
|
49
|
+
mi_ivar_reader(*names)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -61,7 +61,7 @@ module Conglomerate
|
|
61
61
|
attr_metadata = self.class.attributes[attr]
|
62
62
|
|
63
63
|
if type = attr_metadata[:type]
|
64
|
-
raise "TypeMismatch" if !val.is_a?(type)
|
64
|
+
raise "TypeMismatch" if !val.is_a?(type) && val != nil
|
65
65
|
end
|
66
66
|
|
67
67
|
self.instance_variable_set("@#{attr}", val)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module ParticleBuilder
|
3
|
+
def self.included(object)
|
4
|
+
object.send(:extend, ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def objects
|
10
|
+
instance_variable_get("@objects")
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
instance_variable_get("@_builder_name").to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def self.extended(klass)
|
19
|
+
klass.instance_variable_set("@class_methods", Module.new)
|
20
|
+
klass.instance_variable_set("@values", [])
|
21
|
+
klass.instance_variable_set("@builders", [])
|
22
|
+
end
|
23
|
+
|
24
|
+
def serializer
|
25
|
+
mod = Module.new
|
26
|
+
mod.send(:include, Conglomerate::MixinIvarHelper)
|
27
|
+
mod.send(:mc_ivar_accessor, :class_methods, :values, :_builder_type, :_builder_calls, :_builder_builders, :build_override_mod)
|
28
|
+
|
29
|
+
mod.class_methods = class_methods
|
30
|
+
mod.values = values.dup
|
31
|
+
mod._builder_type = instance_variable_get("@type")
|
32
|
+
mod._builder_builders = builders.dup
|
33
|
+
|
34
|
+
begin
|
35
|
+
mod.build_override_mod = self::BuildOverride
|
36
|
+
rescue NameError
|
37
|
+
end
|
38
|
+
|
39
|
+
mod.define_singleton_method(:included) do |klass|
|
40
|
+
klass.send(:extend, class_methods)
|
41
|
+
klass.send(:include, Conglomerate::BuilderSerializer)
|
42
|
+
klass.send(:include, build_override_mod) if build_override_mod
|
43
|
+
klass.instance_variable_set("@_builder_values", values)
|
44
|
+
klass.instance_variable_set("@_builder_type", _builder_type)
|
45
|
+
klass.instance_variable_set("@_builder_calls", {})
|
46
|
+
klass.instance_variable_set("@_builder_builders", _builder_builders)
|
47
|
+
end
|
48
|
+
mod
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
include Conglomerate::MixinIvarHelper
|
54
|
+
mi_ivar_reader :values, :builders, :class_methods
|
55
|
+
|
56
|
+
def builds(type)
|
57
|
+
instance_variable_set("@type", type)
|
58
|
+
end
|
59
|
+
|
60
|
+
def value(name)
|
61
|
+
class_methods.send(:define_method, name) do |&block|
|
62
|
+
instance_variable_set("@#{name}", block)
|
63
|
+
end
|
64
|
+
|
65
|
+
values << name
|
66
|
+
end
|
67
|
+
|
68
|
+
def builder(command, builder, opts={})
|
69
|
+
name = opts[:name] || command
|
70
|
+
array = opts[:array] || false
|
71
|
+
iterates = opts[:iterates] || false
|
72
|
+
|
73
|
+
class_methods.send(:define_method, command) do |name=nil, opts={}, &block|
|
74
|
+
instance_variable_get("@_builder_calls")[command] ||= []
|
75
|
+
instance_variable_get("@_builder_calls")[command] << Conglomerate::BuilderCall.new({
|
76
|
+
:name => name,
|
77
|
+
:opts => opts,
|
78
|
+
:block => block,
|
79
|
+
:builder => builder,
|
80
|
+
:array => array,
|
81
|
+
:iterates => iterates
|
82
|
+
})
|
83
|
+
end
|
84
|
+
|
85
|
+
builders << {
|
86
|
+
:command => command,
|
87
|
+
:builder => builder,
|
88
|
+
:array => array,
|
89
|
+
:name => name,
|
90
|
+
:iterates => iterates
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module QueryBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Query
|
6
|
+
|
7
|
+
module BuildOverride
|
8
|
+
def build(attrs = {})
|
9
|
+
instance_variable_set("@objects", nil)
|
10
|
+
super(attrs.merge({
|
11
|
+
:rel => _builder_name
|
12
|
+
}))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
value :href
|
17
|
+
value :rel
|
18
|
+
value :name
|
19
|
+
value :prompt
|
20
|
+
|
21
|
+
builder :datum, Conglomerate::DatumBuilder, :name => :data, :array => true
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Conglomerate
|
2
|
+
module TemplateBuilder
|
3
|
+
include Conglomerate::ParticleBuilder
|
4
|
+
|
5
|
+
builds Conglomerate::Template
|
6
|
+
|
7
|
+
module BuildOverride
|
8
|
+
def build(attrs = {})
|
9
|
+
instance_variable_set("@objects", nil)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
builder :datum, Conglomerate::DatumBuilder, :name => :data, :array => true
|
15
|
+
end
|
16
|
+
end
|
@@ -9,10 +9,21 @@ module Conglomerate
|
|
9
9
|
serialize_particle(item)
|
10
10
|
elsif Conglomerate::Array === item
|
11
11
|
serialize_array(item)
|
12
|
-
elsif Array === item
|
12
|
+
elsif ::Array === item
|
13
13
|
serialize_array(item)
|
14
14
|
elsif item.is_a?(Numeric) || item.is_a?(String) || item.nil?
|
15
15
|
item
|
16
|
+
elsif item.respond_to?(:to_s)
|
17
|
+
case item
|
18
|
+
when DateTime
|
19
|
+
item.to_time.utc.iso8601.sub(/\+00:00$/, "Z")
|
20
|
+
when Time
|
21
|
+
item.utc.iso8601.sub(/\+00:00$/, "Z")
|
22
|
+
when Date
|
23
|
+
item.strftime("%Y-%m-%d")
|
24
|
+
else
|
25
|
+
item
|
26
|
+
end
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
@@ -26,7 +37,7 @@ module Conglomerate
|
|
26
37
|
attributes.inject({}) do |hash, (attr, attr_metadata)|
|
27
38
|
hash.tap do |h|
|
28
39
|
attribute = particle.send(attr)
|
29
|
-
attribute
|
40
|
+
attribute = attr_metadata[:default] if attribute == nil
|
30
41
|
|
31
42
|
unless attr_metadata[:cull] && cull_attribute(attribute)
|
32
43
|
h[attr.to_s] = serialize(attribute)
|
@@ -42,7 +53,7 @@ module Conglomerate
|
|
42
53
|
end
|
43
54
|
|
44
55
|
def cull_attribute(attribute)
|
45
|
-
|
56
|
+
(attribute == nil) || (Conglomerate::Array === attribute && attribute.empty?)
|
46
57
|
end
|
47
58
|
end
|
48
59
|
end
|
data/lib/conglomerate/version.rb
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require_relative "../lib/conglomerate"
|
3
|
+
require_relative "../lib/conglomerate/ext/commands"
|
4
|
+
|
5
|
+
class CommandExtTestSerializer
|
6
|
+
include Conglomerate::RootBuilder.serializer
|
7
|
+
|
8
|
+
collection do
|
9
|
+
command :populate do
|
10
|
+
prompt { "test 123" }
|
11
|
+
href { populate_items_url }
|
12
|
+
datum :id
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Conglomerate Command Ext" do
|
18
|
+
let(:object) do
|
19
|
+
double(
|
20
|
+
"Object",
|
21
|
+
:id => 1
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:context) do
|
26
|
+
double(
|
27
|
+
"Context",
|
28
|
+
:populate_items_url => "https://example.com/items/populate"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
let(:test_serializer) do
|
33
|
+
CommandExtTestSerializer.new(object, :context => context).serialize
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:test_collection) do
|
37
|
+
test_serializer["collection"]
|
38
|
+
end
|
39
|
+
|
40
|
+
context "#command" do
|
41
|
+
it "adds a command template to the collection" do
|
42
|
+
expect(test_collection["commands"]).to match_array(
|
43
|
+
[
|
44
|
+
{
|
45
|
+
"href" => "https://example.com/items/populate",
|
46
|
+
"rel" => "populate",
|
47
|
+
"prompt" => "test 123",
|
48
|
+
"data" => [
|
49
|
+
{"name" => "id", "value" => nil}
|
50
|
+
]
|
51
|
+
}
|
52
|
+
]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
require_relative "../lib/conglomerate"
|
3
|
+
|
4
|
+
class ConglomerateTestParticleSerializer
|
5
|
+
include Conglomerate::RootBuilder.serializer
|
6
|
+
|
7
|
+
collection do
|
8
|
+
href { test_url }
|
9
|
+
|
10
|
+
item "RSpec::Mocks::Double" do |item|
|
11
|
+
href { item_url(item.id) }
|
12
|
+
|
13
|
+
datum :description, :prompt => "awesome"
|
14
|
+
datum :id
|
15
|
+
datum :event_id
|
16
|
+
datum :roster_id
|
17
|
+
datum :team_ids
|
18
|
+
|
19
|
+
datum :event_date_time
|
20
|
+
datum :event_date
|
21
|
+
datum :event_time
|
22
|
+
|
23
|
+
datum :is_available
|
24
|
+
|
25
|
+
link :event, :href => Proc.new{ event_url(item.event_id) }
|
26
|
+
link :roster, :href => Proc.new{ roster_url(item.roster_id) }, :if => Proc.new{ item.roster_id }
|
27
|
+
link :teams, :href => Proc.new{ team_url(item.team_ids.join(",")) }
|
28
|
+
link :users, :href => Proc.new{ users_search_url(:object_id => item.id) }
|
29
|
+
end
|
30
|
+
|
31
|
+
link :events, :href => Proc.new { events_url }
|
32
|
+
query :search, :href => Proc.new { search_items_url } do
|
33
|
+
datum :id
|
34
|
+
end
|
35
|
+
|
36
|
+
template do
|
37
|
+
datum :repeats, :prompt => "true|false"
|
38
|
+
datum :description, :prompt => "awesome"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ConglomerateExtraTestParticleSerializer
|
44
|
+
include Conglomerate::RootBuilder.serializer
|
45
|
+
|
46
|
+
collection do
|
47
|
+
item do
|
48
|
+
datum :id
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class ConglomerateNullParticleSerializer
|
54
|
+
include Conglomerate::RootBuilder.serializer
|
55
|
+
|
56
|
+
collection do
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Conglomerate do
|
61
|
+
let(:object) do
|
62
|
+
double(
|
63
|
+
"Object",
|
64
|
+
:id => 1,
|
65
|
+
:description => "Tasty Burgers",
|
66
|
+
:event_date_time => DateTime.parse("1981-11-28T10:00:00+00:00"),
|
67
|
+
:event_date => Date.parse("1981-11-28"),
|
68
|
+
:event_time => Time.parse("1981-11-28T10:00:00+00:00"),
|
69
|
+
:event_id => 2,
|
70
|
+
:roster_id => nil,
|
71
|
+
:team_ids => [1,2],
|
72
|
+
:user_ids => [],
|
73
|
+
:is_available => false
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:context) do
|
78
|
+
request = double("Request", :original_url => "https://example.com/items")
|
79
|
+
double(
|
80
|
+
"Context",
|
81
|
+
:request => request,
|
82
|
+
:search_items_url => "https://example.com/items/search",
|
83
|
+
:populate_items_url => "https://example.com/items/populate",
|
84
|
+
).tap do |context|
|
85
|
+
allow(context).to receive(:item_url).with(1) {
|
86
|
+
"https://example.com/items/1"
|
87
|
+
}
|
88
|
+
allow(context).to receive(:event_url).with(2) {
|
89
|
+
"https://example.com/events/2"
|
90
|
+
}
|
91
|
+
allow(context).to receive(:events_url) {
|
92
|
+
"https://example.com/events"
|
93
|
+
}
|
94
|
+
allow(context).to receive(:team_url).with("1,2") {
|
95
|
+
"https://example.com/teams/1,2"
|
96
|
+
}
|
97
|
+
allow(context).to receive(:test_url) { "abc" }
|
98
|
+
allow(context).to receive(:users_search_url).with(:object_id => 1) {
|
99
|
+
"def"
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:test_serializer) do
|
105
|
+
ConglomerateTestParticleSerializer.new(object, :context => context).serialize
|
106
|
+
end
|
107
|
+
|
108
|
+
let(:extra_test_serializer) do
|
109
|
+
ConglomerateExtraTestParticleSerializer.new(object, :context => context).serialize
|
110
|
+
end
|
111
|
+
|
112
|
+
let(:null_serializer) do
|
113
|
+
ConglomerateNullParticleSerializer.new(object, :context => context).serialize
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:test_collection) { test_serializer["collection"] }
|
117
|
+
|
118
|
+
let(:extra_test_collection) { extra_test_serializer["collection"] }
|
119
|
+
|
120
|
+
let(:null_collection) { null_serializer["collection"] }
|
121
|
+
|
122
|
+
describe "#version" do
|
123
|
+
it "sets version to 1.0" do
|
124
|
+
expect(null_collection["version"]).to eq("1.0")
|
125
|
+
expect(test_collection["version"]).to eq("1.0")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#href" do
|
130
|
+
it "in context, uses the block to set the collection href" do
|
131
|
+
expect(test_collection["href"]).to eq("abc")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "isn't included if the href is nil" do
|
135
|
+
expect(null_collection["href"]).to be_nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#query" do
|
140
|
+
it "doesn't include any query templates if none are provided" do
|
141
|
+
expect(null_collection.keys).to_not include("queries")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "adds a query template to the collection" do
|
145
|
+
expect(test_collection["queries"]).to match_array(
|
146
|
+
[
|
147
|
+
{
|
148
|
+
"href" => "https://example.com/items/search",
|
149
|
+
"rel" => "search",
|
150
|
+
"data" => [
|
151
|
+
{"name" => "id", "value" => nil}
|
152
|
+
]
|
153
|
+
}
|
154
|
+
]
|
155
|
+
)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "#attribute(s)" do
|
160
|
+
context "items" do
|
161
|
+
it "skips items if there are no attributes" do
|
162
|
+
expect(null_collection.keys).to_not include("items")
|
163
|
+
end
|
164
|
+
|
165
|
+
it "doesn't have an items array if items is empty" do
|
166
|
+
test_serializer = ConglomerateTestParticleSerializer
|
167
|
+
.new(nil, :context => context)
|
168
|
+
.serialize
|
169
|
+
test_collection = test_serializer["collection"]
|
170
|
+
expect(test_collection.keys).to_not include("items")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "includes an items array if attributes and objects present" do
|
174
|
+
expect(test_collection["items"]).to eq(
|
175
|
+
[
|
176
|
+
{
|
177
|
+
"href" => "https://example.com/items/1",
|
178
|
+
"data" => [
|
179
|
+
{"name" => "description", "value" => "Tasty Burgers", "prompt" => "awesome"},
|
180
|
+
{"name" => "id", "value" => 1},
|
181
|
+
{"name" => "event_id", "value" => 2},
|
182
|
+
{"name" => "roster_id", "value" => nil},
|
183
|
+
{"name" => "team_ids", "value" => [1,2]},
|
184
|
+
{"name" => "event_date_time", "value" => "1981-11-28T10:00:00Z"},
|
185
|
+
{"name" => "event_date", "value" => "1981-11-28"},
|
186
|
+
{"name" => "event_time", "value" => "1981-11-28T10:00:00Z"},
|
187
|
+
{"name" => "is_available", "value" => false}
|
188
|
+
],
|
189
|
+
"links" => [
|
190
|
+
{"rel" => "event", "href" => "https://example.com/events/2"},
|
191
|
+
{"rel" => "teams", "href" => "https://example.com/teams/1,2"},
|
192
|
+
{"rel" => "users", "href" => "def"}
|
193
|
+
]
|
194
|
+
}
|
195
|
+
]
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "doesn't include links if there are none" do
|
200
|
+
expect(extra_test_collection["items"]).to eq(
|
201
|
+
[
|
202
|
+
{
|
203
|
+
"data" => [
|
204
|
+
{"name" => "id", "value" => 1}
|
205
|
+
]
|
206
|
+
}
|
207
|
+
]
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "template" do
|
213
|
+
it "skips template if there are no attributes for a template" do
|
214
|
+
expect(null_collection.keys).to_not include("template")
|
215
|
+
end
|
216
|
+
|
217
|
+
it "includes a valid template if attributes have them" do
|
218
|
+
expect(test_collection["template"]["data"]).to match_array(
|
219
|
+
[
|
220
|
+
{"name" => "description", "value" => nil, "prompt" => "awesome"},
|
221
|
+
{"name" => "repeats", "value" => nil, "prompt" => "true|false"}
|
222
|
+
]
|
223
|
+
)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "#link" do
|
229
|
+
it "skips links if there are none present" do
|
230
|
+
expect(null_collection.keys).to_not include("links")
|
231
|
+
end
|
232
|
+
|
233
|
+
it "adds links if they are present" do
|
234
|
+
expect(test_collection["links"]).to match_array(
|
235
|
+
[
|
236
|
+
{"rel" => "events", "href" => "https://example.com/events"}
|
237
|
+
]
|
238
|
+
)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "#item_link" do
|
243
|
+
it "adds links if they are present" do
|
244
|
+
expect(test_collection["items"][0]["links"]).to include(
|
245
|
+
{"rel" => "teams", "href" => "https://example.com/teams/1,2"}
|
246
|
+
)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: conglomerate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Emmons
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-06-
|
12
|
+
date: 2014-06-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -68,25 +68,39 @@ files:
|
|
68
68
|
- conglomerate.gemspec
|
69
69
|
- lib/conglomerate.rb
|
70
70
|
- lib/conglomerate/array.rb
|
71
|
+
- lib/conglomerate/builder_call.rb
|
72
|
+
- lib/conglomerate/builder_serializer.rb
|
71
73
|
- lib/conglomerate/collection.rb
|
74
|
+
- lib/conglomerate/collection_builder.rb
|
72
75
|
- lib/conglomerate/command.rb
|
73
76
|
- lib/conglomerate/datum.rb
|
77
|
+
- lib/conglomerate/datum_builder.rb
|
74
78
|
- lib/conglomerate/error.rb
|
79
|
+
- lib/conglomerate/ext/commands.rb
|
75
80
|
- lib/conglomerate/item.rb
|
81
|
+
- lib/conglomerate/item_builder.rb
|
76
82
|
- lib/conglomerate/link.rb
|
83
|
+
- lib/conglomerate/link_builder.rb
|
84
|
+
- lib/conglomerate/mixin_ivar_helper.rb
|
77
85
|
- lib/conglomerate/particle.rb
|
86
|
+
- lib/conglomerate/particle_builder.rb
|
78
87
|
- lib/conglomerate/query.rb
|
88
|
+
- lib/conglomerate/query_builder.rb
|
79
89
|
- lib/conglomerate/root.rb
|
90
|
+
- lib/conglomerate/root_builder.rb
|
80
91
|
- lib/conglomerate/serializer.rb
|
81
92
|
- lib/conglomerate/template.rb
|
93
|
+
- lib/conglomerate/template_builder.rb
|
82
94
|
- lib/conglomerate/tree_deserializer.rb
|
83
95
|
- lib/conglomerate/tree_serializer.rb
|
84
96
|
- lib/conglomerate/version.rb
|
85
97
|
- spec/collection_spec.rb
|
98
|
+
- spec/command_ext_spec.rb
|
86
99
|
- spec/command_spec.rb
|
87
100
|
- spec/conglomerate_spec.rb
|
88
101
|
- spec/datum_spec.rb
|
89
102
|
- spec/link_spec.rb
|
103
|
+
- spec/particle_builder_spec.rb
|
90
104
|
- spec/particle_spec.rb
|
91
105
|
- spec/query_spec.rb
|
92
106
|
- spec/spec_helper.rb
|
@@ -117,10 +131,12 @@ specification_version: 4
|
|
117
131
|
summary: A library to serialize Ruby objects into collection+json
|
118
132
|
test_files:
|
119
133
|
- spec/collection_spec.rb
|
134
|
+
- spec/command_ext_spec.rb
|
120
135
|
- spec/command_spec.rb
|
121
136
|
- spec/conglomerate_spec.rb
|
122
137
|
- spec/datum_spec.rb
|
123
138
|
- spec/link_spec.rb
|
139
|
+
- spec/particle_builder_spec.rb
|
124
140
|
- spec/particle_spec.rb
|
125
141
|
- spec/query_spec.rb
|
126
142
|
- spec/spec_helper.rb
|