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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 60707b16fa5c3be311bec710700f84d2014b9b1e
4
- data.tar.gz: aad0be8b2404c946d70f6ad35ad41fff0907e08d
3
+ metadata.gz: c694dae2bac88f952a49ce4b4c7b5663edbd0fb7
4
+ data.tar.gz: 4001ebb79a3a8da5bd4b6828a814c9f7d408d728
5
5
  SHA512:
6
- metadata.gz: 308b42d5847d369aaa86a12ea6d9d7fd3f253cafe041e89479a1a0d0395b060e5336e4581f4391663003b8d1b6a9b4b87def79ae79733753493d8552d50ba918
7
- data.tar.gz: 8ea6633725178c97272abb7873df54d977c5df6d618e81df211510ab5868b51fbf40d54a559fd5f8b921ae15fd96cab228297eafc5703d581ac27bc001932bcf
6
+ metadata.gz: c5eba0c2e611e055dd3129491d25144f303fa5f740a790afdffd7b7ae8181000c9668c71ea915005a91ce93c6f52e491cbaaa8432609b8f7a5212a32e7552439
7
+ data.tar.gz: 1d42ba345c4223f9dab0ae101f73e78a0829f6fda56ba4b8109bd9f8eebfe5dd498c70456c3706151b68652dea024b396a870700611ce72be0dfaa062dc662bc
@@ -6,3 +6,6 @@ Documentation:
6
6
 
7
7
  MethodLength:
8
8
  Max: 15
9
+
10
+ Style/TrivialAccessors:
11
+ AllowDSLWriters: true
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cot (0.4.2)
4
+ cot (0.4.3)
5
5
  activemodel
6
6
 
7
7
  GEM
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 three optional arguments, value, from and searchable.
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
- - default_attributes: Will add the keys/values to the object
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
@@ -4,6 +4,7 @@ require 'active_support/core_ext/hash/indifferent_access'
4
4
  require 'cot/version'
5
5
  require 'cot/frame_class_methods'
6
6
  require 'cot/frame'
7
+ require 'cot/collection_class_methods'
7
8
  require 'cot/collection'
8
9
  require 'cot/property'
9
10
  require 'cot/enum'
@@ -1,14 +1,19 @@
1
1
  module Cot
2
2
  class Collection < SimpleDelegator
3
- def initialize(klass, objects, options = {})
4
- options = { sub_key: options } unless options.is_a?(Hash)
5
- @options = options.with_indifferent_access
6
- @options[:default_attributes] = {} unless @options[:default_attributes].is_a?(Hash)
7
- @klass = klass
8
- @objects = objects
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
- @klass.new @options[:default_attributes].merge(payload.fetch(@options[:sub_key], {}))
75
+ self.class.klass.new @options[:default_attributes].merge(payload.fetch(@options[:sub_key], {}))
48
76
  else
49
- @klass.new @options[:default_attributes].merge(payload || {})
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
@@ -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
- # TODO: Have this key off a defined primary key instead of defaulting to id
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)
@@ -5,9 +5,19 @@ module Cot
5
5
  @args = params
6
6
  end
7
7
 
8
- def value(&block)
9
- return args[:value] unless block
10
- args[:value] = block
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|
@@ -1,3 +1,3 @@
1
1
  module Cot
2
- VERSION = '0.4.3'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -71,34 +71,97 @@ describe Cot::Collection do
71
71
  end
72
72
 
73
73
  context 'initialize' do
74
- it 'does not process the objects if they are already the correct class' do
75
- coll = Cot::Collection.new FakeDouble, [FakeDouble.new(fooy: :bar), FakeDouble.new(asdf: :fdas)]
76
- expect(coll.first).to be_kind_of FakeDouble
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
@@ -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: :id
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' }, id: 42)
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 'assigns sets the value' do
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(id: 42)
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.3
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-10-03 00:00:00.000000000 Z
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