cot 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +1 -1
- data/README.md +22 -3
- data/lib/cot.rb +1 -0
- data/lib/cot/collection.rb +37 -9
- data/lib/cot/collection_class_methods.rb +18 -0
- data/lib/cot/frame.rb +9 -3
- data/lib/cot/frame_class_methods.rb +17 -4
- data/lib/cot/property.rb +13 -3
- data/lib/cot/version.rb +1 -1
- data/spec/lib/cot/collection_spec.rb +90 -27
- data/spec/lib/cot/frame_spec.rb +21 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c694dae2bac88f952a49ce4b4c7b5663edbd0fb7
|
4
|
+
data.tar.gz: 4001ebb79a3a8da5bd4b6828a814c9f7d408d728
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5eba0c2e611e055dd3129491d25144f303fa5f740a790afdffd7b7ae8181000c9668c71ea915005a91ce93c6f52e491cbaaa8432609b8f7a5212a32e7552439
|
7
|
+
data.tar.gz: 1d42ba345c4223f9dab0ae101f73e78a0829f6fda56ba4b8109bd9f8eebfe5dd498c70456c3706151b68652dea024b396a870700611ce72be0dfaa062dc662bc
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -13,7 +13,7 @@ end
|
|
13
13
|
|
14
14
|
class ExampleObject < Cot::Frame
|
15
15
|
property :id
|
16
|
-
property :name, :searchable => true
|
16
|
+
property :name, :searchable => true, primary: true
|
17
17
|
property :company_name, :from => :companyName
|
18
18
|
property :item do
|
19
19
|
from :place
|
@@ -21,6 +21,11 @@ class ExampleObject < Cot::Frame
|
|
21
21
|
NestedClass.new params.merge parent_id: id
|
22
22
|
end
|
23
23
|
end
|
24
|
+
property :blank do
|
25
|
+
missing do
|
26
|
+
"Item #{id}"
|
27
|
+
end
|
28
|
+
end
|
24
29
|
enum :types do
|
25
30
|
entry :first
|
26
31
|
entry :third, value: 3
|
@@ -35,6 +40,14 @@ class ExampleCollection < Cot::Collection
|
|
35
40
|
end
|
36
41
|
end
|
37
42
|
|
43
|
+
# No initialize required, just pass in the objects
|
44
|
+
class ExampleDSLCollection < Cot::Collection
|
45
|
+
collected_class ExampleObject
|
46
|
+
sub_key :key
|
47
|
+
default_attributes default: :attributes
|
48
|
+
end
|
49
|
+
|
50
|
+
|
38
51
|
thingy = ExampleObject.new(id: :my_id, name: 'awesome name', createdOn: Time.now, place: {bar: 'this is nested.foo'})
|
39
52
|
thingy.id # 5
|
40
53
|
thingy.name # awesome name
|
@@ -45,6 +58,8 @@ thingy.item # NestedClass instance
|
|
45
58
|
thingy.item.foo # 'this is nested.foo'
|
46
59
|
thingy.created_at # what time it is now
|
47
60
|
thingy.defined_properties # [:id, :name, :created_at]
|
61
|
+
thingy.blank # 'Item 5'
|
62
|
+
thingy.exists? # 'awesome name'
|
48
63
|
|
49
64
|
collection = ExampleCollection.new [{ id: :my_id, name: 'awesome name', createdOn: Time.now }, { id: :my_id, name: 'awesome name', createdOn: Time.now }], { default_attributes: { default: :attribute }
|
50
65
|
collection.first.name # 'awesome name'
|
@@ -64,9 +79,11 @@ Frame provides some helpful methods:
|
|
64
79
|
- property
|
65
80
|
- The first parameter is the name of the property and it is added as a method to the object.
|
66
81
|
- You can pass additional options in two ways, first you can pass a hash of options to property and secondly you can pass a block to property.
|
67
|
-
- There are
|
82
|
+
- There are five optional arguments, value, from, missing, primary and searchable.
|
68
83
|
- From indicates that the property has an alternate key in the incoming/outgoing data.
|
69
84
|
- Searchable adds the property to the search mappings.
|
85
|
+
- Primary indicates that this is the primary key. Currently this only determines what exists? looks at.
|
86
|
+
- Missing takes a block and will execute the block if nothing was passed to the object on initialize. This block is evaluated in the context of the object. So you can for example have a string that says "Object #{id}"
|
70
87
|
- Value takes a block and overwrites the value of the property to be the result of the block
|
71
88
|
- This is useful for nested objects.
|
72
89
|
- The block is executed as part of the instance of the object, so you have access to other properties.
|
@@ -91,10 +108,12 @@ Frame provides some helpful methods:
|
|
91
108
|
- errors used to store any errors associated with the object
|
92
109
|
|
93
110
|
Collection provides the following methods:
|
111
|
+
- DSL
|
112
|
+
- If you don't need any special behavior in your initialize, you can use the DSL to declare the class, sub\_key and default\_attributes
|
94
113
|
- Initialization
|
95
114
|
- You can pass options to the collection when you initialize
|
96
115
|
- sub\_key: Uses the contents inside the sub\_key
|
97
|
-
-
|
116
|
+
- default\_attributes: Will add the keys/values to the object
|
98
117
|
- Instance Methods
|
99
118
|
- The following methods collate the results from members
|
100
119
|
- serializable\_hash
|
data/lib/cot.rb
CHANGED
data/lib/cot/collection.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
module Cot
|
2
2
|
class Collection < SimpleDelegator
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
extend CollectionClassMethods
|
4
|
+
|
5
|
+
# We take an array of params here and then parse them due to backwards compat crap
|
6
|
+
# Collection can take 1-3 parameters and there are two cases when it gets two
|
7
|
+
# 3: klass, objects, options
|
8
|
+
# 2: klass, objects
|
9
|
+
# 2: objects, options
|
10
|
+
# 1: objects
|
11
|
+
def initialize(*params)
|
12
|
+
parse_params(params)
|
9
13
|
|
14
|
+
puts self.class.klass.inspect
|
10
15
|
# If you pass in different types of things here we can't be friends
|
11
|
-
initialize_objects(objects) unless objects.first.is_a? klass
|
16
|
+
initialize_objects(@objects) unless @objects.first.is_a? self.class.klass
|
12
17
|
|
13
18
|
super @objects
|
14
19
|
end
|
@@ -40,13 +45,36 @@ module Cot
|
|
40
45
|
|
41
46
|
private
|
42
47
|
|
48
|
+
def parse_params(params)
|
49
|
+
until params.empty?
|
50
|
+
item = params.shift
|
51
|
+
if item.class == Class
|
52
|
+
self.class.klass = item
|
53
|
+
elsif item.class == Array
|
54
|
+
@objects = item
|
55
|
+
else
|
56
|
+
options = item
|
57
|
+
end
|
58
|
+
end
|
59
|
+
options ||= {}
|
60
|
+
parse_options(options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_options(options)
|
64
|
+
options = { sub_key: options } unless options.is_a?(Hash)
|
65
|
+
@options = options.with_indifferent_access
|
66
|
+
@options[:default_attributes] = {} unless @options[:default_attributes].is_a?(Hash)
|
67
|
+
self.class.set_default_values
|
68
|
+
@options.merge! self.class.options
|
69
|
+
end
|
70
|
+
|
43
71
|
def initialize_objects(objects)
|
44
72
|
@objects = []
|
45
73
|
@objects = objects.map do |payload|
|
46
74
|
if @options[:sub_key]
|
47
|
-
|
75
|
+
self.class.klass.new @options[:default_attributes].merge(payload.fetch(@options[:sub_key], {}))
|
48
76
|
else
|
49
|
-
|
77
|
+
self.class.klass.new @options[:default_attributes].merge(payload || {})
|
50
78
|
end
|
51
79
|
end
|
52
80
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CollectionClassMethods
|
2
|
+
attr_accessor :klass, :options
|
3
|
+
|
4
|
+
def collected_class(klass)
|
5
|
+
@klass = klass
|
6
|
+
end
|
7
|
+
|
8
|
+
[:sub_key, :default_attributes].each do |method_name|
|
9
|
+
define_method method_name do |value|
|
10
|
+
set_default_values
|
11
|
+
@options[method_name] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_default_values
|
16
|
+
@options ||= {}
|
17
|
+
end
|
18
|
+
end
|
data/lib/cot/frame.rb
CHANGED
@@ -17,15 +17,21 @@ module Cot
|
|
17
17
|
@data[k] = instance_exec(v, &block)
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
defined_properties.each do |prop|
|
22
|
+
if @data[prop].nil? && self.class.missing_blocks[prop]
|
23
|
+
block = self.class.missing_blocks[prop]
|
24
|
+
@data[prop] = instance_exec(self, &block)
|
25
|
+
end
|
26
|
+
end
|
20
27
|
end
|
21
28
|
|
22
29
|
def exists?
|
23
|
-
|
24
|
-
id
|
30
|
+
send self.class.primary_key
|
25
31
|
end
|
26
32
|
|
27
33
|
def defined_properties
|
28
|
-
self.class.attr_methods
|
34
|
+
self.class.attr_methods || []
|
29
35
|
end
|
30
36
|
|
31
37
|
def properties_mapping
|
@@ -5,6 +5,8 @@ module Cot
|
|
5
5
|
:inverted_search_mappings,
|
6
6
|
:search_mappings,
|
7
7
|
:value_blocks,
|
8
|
+
:missing_blocks,
|
9
|
+
:primary_key,
|
8
10
|
:mappings
|
9
11
|
|
10
12
|
def search_property(name, args = {})
|
@@ -30,18 +32,27 @@ module Cot
|
|
30
32
|
prop = Property.new args
|
31
33
|
prop.instance_eval(&block) if block
|
32
34
|
|
35
|
+
set_blocks(name, prop)
|
36
|
+
set_mappings(name, prop)
|
37
|
+
@primary_key = name if prop.primary?
|
38
|
+
|
39
|
+
define_property_methods name
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def set_blocks(name, prop)
|
33
45
|
@value_blocks[name] = prop.value if prop.value
|
46
|
+
@missing_blocks[name] = prop.missing if prop.missing
|
47
|
+
end
|
34
48
|
|
49
|
+
def set_mappings(name, prop)
|
35
50
|
key = prop.from
|
36
51
|
@mappings[key.to_sym] = name if key
|
37
52
|
@search_mappings[name] = key ? key : name if prop.searchable
|
38
53
|
attr_methods << name.to_sym
|
39
|
-
|
40
|
-
define_property_methods name
|
41
54
|
end
|
42
55
|
|
43
|
-
private
|
44
|
-
|
45
56
|
# Can't seem to get an intialize in for the class, so we need to set these
|
46
57
|
# before we do stuff for property
|
47
58
|
def set_default_values
|
@@ -49,6 +60,8 @@ module Cot
|
|
49
60
|
@attr_methods ||= []
|
50
61
|
@search_mappings ||= {}
|
51
62
|
@value_blocks ||= {}
|
63
|
+
@missing_blocks ||= {}
|
64
|
+
@primary_key ||= :id
|
52
65
|
end
|
53
66
|
|
54
67
|
def define_property_methods(name)
|
data/lib/cot/property.rb
CHANGED
@@ -5,9 +5,19 @@ module Cot
|
|
5
5
|
@args = params
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
8
|
+
def primary
|
9
|
+
args[:primary] = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def primary?
|
13
|
+
args[:primary]
|
14
|
+
end
|
15
|
+
|
16
|
+
[:missing, :value].each do |method_name|
|
17
|
+
define_method method_name do |&block|
|
18
|
+
return args[method_name] unless block
|
19
|
+
args[method_name] = block
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
23
|
[:from, :searchable].each do |method_name|
|
data/lib/cot/version.rb
CHANGED
@@ -71,34 +71,97 @@ describe Cot::Collection do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
context 'initialize' do
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
context 'when passing a class' do
|
75
|
+
context 'with options' do
|
76
|
+
it 'takes an optional sub_key option to pull the object out of the payload' do
|
77
|
+
coll = Cot::Collection.new FakeDouble,
|
78
|
+
[{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }],
|
79
|
+
sub_key: :inner
|
80
|
+
expect(coll.first).to be_kind_of FakeDouble
|
81
|
+
expect(coll.first.fooy).to eq :bar
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'takes an optional default_attributes option to add set attributes in every object.' do
|
85
|
+
coll = Cot::Collection.new FakeDouble, [{ fooy: :bar }, { asdf: :fdas }], default_attributes: { foo: :baz }
|
86
|
+
expect(coll).to all be_kind_of FakeDouble
|
87
|
+
expect(coll.map(&:foo).uniq).to eq [:baz]
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'support a legacy optional sub_key parameter to pull the object out of the payload' do
|
91
|
+
coll = Cot::Collection.new FakeDouble, [{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }], :inner
|
92
|
+
expect(coll.first).to be_kind_of FakeDouble
|
93
|
+
expect(coll.first.fooy).to eq :bar
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'without options' do
|
98
|
+
it 'does not process the objects if they are already the correct class' do
|
99
|
+
coll = Cot::Collection.new FakeDouble, [FakeDouble.new(fooy: :bar), FakeDouble.new(asdf: :fdas)]
|
100
|
+
expect(coll.first).to be_kind_of FakeDouble
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'creates new instances of the passed klass if the objects are not already the class' do
|
104
|
+
coll = Cot::Collection.new FakeDouble, [{ fooy: :bar }, { asdf: :fdas }]
|
105
|
+
expect(coll.first).to be_kind_of FakeDouble
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when not passing a class' do
|
111
|
+
before :each do
|
112
|
+
class MyCollection < Cot::Collection
|
113
|
+
collected_class FakeDouble
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with options' do
|
118
|
+
it 'takes an optional sub_key option to pull the object out of the payload' do
|
119
|
+
coll = MyCollection.new [{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }], sub_key: :inner
|
120
|
+
expect(coll.first).to be_kind_of FakeDouble
|
121
|
+
expect(coll.first.fooy).to eq :bar
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'takes an optional default_attributes option to add set attributes in every object.' do
|
125
|
+
coll = MyCollection.new [{ fooy: :bar }, { asdf: :fdas }], default_attributes: { foo: :baz }
|
126
|
+
expect(coll).to all be_kind_of FakeDouble
|
127
|
+
expect(coll.map(&:foo).uniq).to eq [:baz]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'options using dsl' do
|
132
|
+
it 'subkey works' do
|
133
|
+
class MyCollection
|
134
|
+
sub_key :inner
|
135
|
+
end
|
136
|
+
|
137
|
+
coll = MyCollection.new [{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }]
|
138
|
+
expect(coll.first).to be_kind_of FakeDouble
|
139
|
+
expect(coll.first.fooy).to eq :bar
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'takes an optional default_attributes option to add set attributes in every object.' do
|
143
|
+
class MyCollection
|
144
|
+
default_attributes foo: :baz
|
145
|
+
end
|
146
|
+
|
147
|
+
coll = MyCollection.new [{ fooy: :bar }, { asdf: :fdas }]
|
148
|
+
expect(coll).to all be_kind_of FakeDouble
|
149
|
+
expect(coll.map(&:foo).uniq).to eq [:baz]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'without options' do
|
154
|
+
it 'does not process the objects if they are already the correct class' do
|
155
|
+
coll = Cot::Collection.new [FakeDouble.new(fooy: :bar), FakeDouble.new(asdf: :fdas)]
|
156
|
+
expect(coll.first).to be_kind_of FakeDouble
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'creates new instances of the passed klass if the objects are not already the class' do
|
160
|
+
coll = Cot::Collection.new [{ fooy: :bar }, { asdf: :fdas }]
|
161
|
+
expect(coll.first).to be_kind_of FakeDouble
|
162
|
+
end
|
163
|
+
end
|
77
164
|
end
|
78
|
-
|
79
|
-
it 'creates new instances of the passed klass if the objects are not already the class' do
|
80
|
-
coll = Cot::Collection.new FakeDouble, [{ fooy: :bar }, { asdf: :fdas }]
|
81
|
-
expect(coll.first).to be_kind_of FakeDouble
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'takes an optional sub_key option to pull the object out of the payload' do
|
85
|
-
coll = Cot::Collection.new FakeDouble, [{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }], sub_key: :inner
|
86
|
-
expect(coll.first).to be_kind_of FakeDouble
|
87
|
-
expect(coll.first.fooy).to eq :bar
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'takes an optional default_attributes option to add set attributes in every object.' do
|
91
|
-
coll = Cot::Collection.new FakeDouble, [{ fooy: :bar }, { asdf: :fdas }], default_attributes: { foo: :baz }
|
92
|
-
expect(coll).to all be_kind_of FakeDouble
|
93
|
-
expect(coll.map(&:foo).uniq).to eq [:baz]
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'support a legacy optional sub_key parameter to pull the object out of the payload' do
|
97
|
-
coll = Cot::Collection.new FakeDouble, [{ inner: { fooy: :bar } }, { inner: { asdf: :fdas } }], :inner
|
98
|
-
expect(coll.first).to be_kind_of FakeDouble
|
99
|
-
expect(coll.first.fooy).to eq :bar
|
100
|
-
end
|
101
|
-
|
102
165
|
end
|
103
166
|
|
104
167
|
context 'update members' do
|
data/spec/lib/cot/frame_spec.rb
CHANGED
@@ -14,6 +14,10 @@ describe Cot::Frame do
|
|
14
14
|
its(:id) { should eq 5 }
|
15
15
|
its(:foo) { should eq 'this will be foo' }
|
16
16
|
|
17
|
+
it 'primary key defaults to id' do
|
18
|
+
expect(@foo.exists?).to be 5
|
19
|
+
end
|
20
|
+
|
17
21
|
context 'serializable_hash' do
|
18
22
|
its(:serializable_hash) { should be_kind_of Hash }
|
19
23
|
it 'has two keys' do
|
@@ -128,7 +132,12 @@ describe Cot::Frame do
|
|
128
132
|
end
|
129
133
|
end
|
130
134
|
class TestObject < Cot::Frame
|
131
|
-
property :my_id, from: :
|
135
|
+
property :my_id, from: :key, primary: true
|
136
|
+
property :blank do
|
137
|
+
missing do
|
138
|
+
"this was blank #{my_id}"
|
139
|
+
end
|
140
|
+
end
|
132
141
|
property :thing do
|
133
142
|
from :stuff
|
134
143
|
searchable true
|
@@ -137,7 +146,7 @@ describe Cot::Frame do
|
|
137
146
|
end
|
138
147
|
end
|
139
148
|
end
|
140
|
-
@foo = TestObject.new(stuff: { key: 'this will be in foo' },
|
149
|
+
@foo = TestObject.new(stuff: { key: 'this will be in foo' }, key: 42)
|
141
150
|
end
|
142
151
|
|
143
152
|
it 'adds to mappings' do
|
@@ -150,13 +159,21 @@ describe Cot::Frame do
|
|
150
159
|
expect(TestObject.search_mappings[:thing]).to be :stuff
|
151
160
|
end
|
152
161
|
|
153
|
-
it '
|
162
|
+
it 'stores missing' do
|
163
|
+
expect(@foo.blank).to eq 'this was blank 42'
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'sets the primary key' do
|
167
|
+
expect(@foo.exists?).to be 42
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'sets the value' do
|
154
171
|
expect(@foo.thing).to be_kind_of Foo
|
155
172
|
expect(@foo.thing.params[:passed]).to eq 42
|
156
173
|
end
|
157
174
|
|
158
175
|
it 'sets the value on []=' do
|
159
|
-
bar = TestObject.new(
|
176
|
+
bar = TestObject.new(key: 42)
|
160
177
|
bar.thing = { key: 'this will be in foo' }
|
161
178
|
expect(bar.thing).to be_kind_of Foo
|
162
179
|
expect(bar.thing.params[:passed]).to eq 42
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph Henrich
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- cot.gemspec
|
130
130
|
- lib/cot.rb
|
131
131
|
- lib/cot/collection.rb
|
132
|
+
- lib/cot/collection_class_methods.rb
|
132
133
|
- lib/cot/enum.rb
|
133
134
|
- lib/cot/frame.rb
|
134
135
|
- lib/cot/frame_class_methods.rb
|