opium 1.2.0 → 1.2.1

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: 3bbf318b07ca2a59bb917e4495abda45117aa588
4
- data.tar.gz: c85609101238b4c6142df1d421a90e33e9fbf38a
3
+ metadata.gz: ecd9332e3c5b659bcb498c22bd08e109dab91b84
4
+ data.tar.gz: e9f1feaa43921e23339c2ea17905ed6f9470e796
5
5
  SHA512:
6
- metadata.gz: 661189662f2ac5cdeb1b0dcb87d32ca291bdb4e888a108e7669153758160ced3f4aa06e961e625544e87c4e7d66a514e588607f0338f81793ee7029ee5266093
7
- data.tar.gz: 76ef297dfd71c71a063140c88f11b48bfb6326b6ca31d333155ac9d6cdbb0917c181d337a7e67adb2038af6261cf4387644ed98211208e932d2e25d9346a3b47
6
+ metadata.gz: 4aa3f212aa8ab6d3a23994909f05d34df0aa7b7b767fb192946a7a2ed1f764758ad594b3ea199bf086296460cc2aeebda4864c62b1dd7911bcdcf7e5b8aeb1fb
7
+ data.tar.gz: ba8ccb1d384ead2549edfa4d58f9c8d852812a1081d20b301aee0c82ff742f28cbd2bec45f5a652e2474047ed3c4c8c333b235c70473d322953ba4b89839968e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.2.1
2
+ ### New Features
3
+ - Fieldable should now be refactored to be more readable and less cluttered; .field, in particular, has been split up into a set of algorithmic steps.
4
+ - #45: If GlobalID is defined, it is automatically included into all models.
5
+
6
+ ### Resolved Issues
7
+ - #46: Relations should now more gracefully handle conversions from nil and Array.
8
+
1
9
  ## 1.2.0
2
10
  ### New Features
3
11
  - #33: Model associations.
@@ -17,6 +17,10 @@ module Opium
17
17
  end
18
18
  end
19
19
 
20
+ def contextual_default_value( context = nil)
21
+ type.to_ruby( default( context ) )
22
+ end
23
+
20
24
  def readonly?
21
25
  self.readonly == true
22
26
  end
@@ -13,31 +13,10 @@ module Opium
13
13
 
14
14
  module ClassMethods
15
15
  def field( name, options = {} )
16
- name = name.to_sym
17
- fields[name] = Field.new( name, options[:type] || Object, options[:default], options[:readonly] || false, options[:as] )
18
- ruby_canonical_field_names[name] = ruby_canonical_field_names[fields[name].name_to_parse] = name.to_s
19
- parse_canonical_field_names[name] = parse_canonical_field_names[fields[name].name_to_parse] = fields[name].name_to_parse.to_s
20
- class_eval do
21
- define_attribute_methods [name]
22
- define_method(name) do
23
- self.attributes[name]
24
- end
25
- end
26
- unless self.respond_to? "#{name}="
27
- class_eval do
28
- define_method("#{name}=") do |value|
29
- converted = self.class.fields[name].type.to_ruby(value)
30
- send( "#{name}_will_change!" ) unless self.attributes[name] == converted
31
- if self.class.fields[name].relation?
32
- converted.owner ||= self
33
- converted.metadata ||= self.class.relations[name]
34
- end
35
- self.attributes[name] = converted
36
- end
37
- send(:private, "#{name}=") if options[:readonly]
38
- end
16
+ create_field_from( name.to_sym, options ).tap do |field|
17
+ create_field_getter_for( field )
18
+ create_field_setter_for( field )
39
19
  end
40
- fields[name]
41
20
  end
42
21
 
43
22
  def has_field?( field_name )
@@ -47,19 +26,53 @@ module Opium
47
26
  alias_method :field?, :has_field?
48
27
 
49
28
  def fields
50
- @fields ||= ActiveSupport::HashWithIndifferentAccess.new
29
+ @fields ||= {}.with_indifferent_access
51
30
  end
52
31
 
53
32
  def ruby_canonical_field_names
54
- @ruby_canonical_field_names ||= ActiveSupport::HashWithIndifferentAccess.new
33
+ @ruby_canonical_field_names ||= {}.with_indifferent_access
55
34
  end
56
35
 
57
36
  def parse_canonical_field_names
58
- @parse_canonical_field_names ||= ActiveSupport::HashWithIndifferentAccess.new
37
+ @parse_canonical_field_names ||= {}.with_indifferent_access
59
38
  end
60
39
 
61
40
  def default_attributes( context = nil )
62
- fields.transform_values {|field| field.type.to_ruby field.default( context ) }.with_indifferent_access
41
+ fields.transform_values {|field| field.contextual_default_value( context ) }.with_indifferent_access
42
+ end
43
+
44
+ private
45
+
46
+ def create_field_from( name, options )
47
+ field = Field.new( name, options[:type] || Object, options[:default], options[:readonly] || false, options[:as] )
48
+ ruby_canonical_field_names[name] = ruby_canonical_field_names[field.name_to_parse] = name.to_s
49
+ parse_canonical_field_names[name] = parse_canonical_field_names[field.name_to_parse] = field.name_to_parse.to_s
50
+ fields[name] = field
51
+ end
52
+
53
+ def create_field_getter_for( field )
54
+ class_eval do
55
+ define_attribute_methods [field.name]
56
+ define_method(field.name) do
57
+ self.attributes[field.name]
58
+ end
59
+ end
60
+ end
61
+
62
+ def create_field_setter_for( field )
63
+ class_eval do
64
+ define_method("#{ field.name }=") do |value|
65
+ converted = field.type.to_ruby(value)
66
+ send( "#{ field.name }_will_change!" ) unless self.attributes[field.name] == converted
67
+ if field.relation?
68
+ converted = field.contextual_default_value( self ) unless converted
69
+ converted.owner ||= self
70
+ converted.metadata ||= self.class.relations[field.name]
71
+ end
72
+ self.attributes[field.name] = converted
73
+ end
74
+ send(:private, "#{ field.name }=") if field.readonly?
75
+ end
63
76
  end
64
77
  end
65
78
  end
@@ -5,46 +5,55 @@ module Opium
5
5
 
6
6
  class << self
7
7
  def to_parse( object )
8
- class_name =
9
- case object
10
- when Hash
11
- fetch_hash_key_from( object, 'class_name' ) || fetch_hash_key_from( object, 'model_name' )
12
- when String, Symbol
13
- object
14
- when is_descendant.curry[Opium::Model]
15
- object.model_name
16
- when self
17
- object.class_name
18
- else
19
- fail ArgumentError, "could not convert #{ object.inspect } to a parse Relation hash"
20
- end
8
+ class_name = determine_class_name_from( object, 'parse Relation hash' )
21
9
  fail ArgumentError, "could not determine class_name from #{ object.inspect }" unless class_name
22
10
  { __type: 'Relation', className: class_name }.with_indifferent_access
23
11
  end
24
12
 
25
13
  def to_ruby( object )
26
- return if object.nil?
14
+ return if object.nil? || object == []
27
15
  return object if object.is_a? self
28
- class_name =
16
+ class_name = determine_class_name_from( object, self.name )
17
+ new( class_name ).tap do |relation|
29
18
  case object
30
- when Hash
31
- fetch_hash_key_from( object, 'class_name' ) || fetch_hash_key_from( object, 'model_name' )
32
- when String, Symbol
33
- object
19
+ when String
34
20
  when is_descendant.curry[Opium::Model]
35
- object.model_name
36
- else
37
- fail ArgumentError, "could not convert #{ object.inspect } to a Opium::Model::Relation"
21
+ relation.push object
22
+ when contains_descendant.curry[Opium::Model]
23
+ object.select(&is_descendant.curry[Opium::Model]).each {|model| relation.push model}
38
24
  end
39
- new( class_name )
25
+ end
40
26
  end
41
27
 
42
28
  private
43
29
 
30
+ def determine_class_name_from( object, context )
31
+ case object
32
+ when Hash
33
+ fetch_hash_key_from( object, 'class_name' ) || fetch_hash_key_from( object, 'model_name' )
34
+ when String, Symbol
35
+ object
36
+ when is_descendant.curry[Opium::Model]
37
+ object.model_name
38
+ when contains_descendant.curry[Opium::Model]
39
+ object.select(&is_descendant.curry[Opium::Model]).first.model_name
40
+ when self
41
+ object.class_name
42
+ else
43
+ fail ArgumentError, "could not convert #{ object.inspect } to a #{ context }"
44
+ end
45
+ end
46
+
44
47
  def is_descendant
45
48
  @is_descendant ||= ->( expected_type, object ) { ( object.is_a?( Class ) ? object : object.class ) <= expected_type }
46
49
  end
47
50
 
51
+ def contains_descendant
52
+ @contains_descendant ||= ->( expected_type, object ) do
53
+ object.is_a?( Enumerable ) ? object.any?( &is_descendant.curry[expected_type] ) : is_descendant.curry[expected_type]
54
+ end
55
+ end
56
+
48
57
  def fetch_hash_key_from( hash, key )
49
58
  snake_case_key = key.to_s.underscore
50
59
  lower_camel_key = key.to_s.camelcase(:lower)
data/lib/opium/model.rb CHANGED
@@ -40,6 +40,7 @@ module Opium
40
40
  include Inheritable
41
41
  include Batchable
42
42
  include Relatable
43
+ include GlobalID if defined?( GlobalID )
43
44
  end
44
45
 
45
46
  def initialize( attributes = {} )
data/lib/opium/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Opium
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.1"
3
3
  end
@@ -161,6 +161,34 @@ describe Opium::Model::Relatable do
161
161
  it { expect( subject.comments ).to_not be_empty }
162
162
  it { expect( subject.comments.owner ).to eq subject }
163
163
  end
164
+
165
+ context "when setting a model's relation" do
166
+ subject { Article.new id: 'abcd1234' }
167
+ let(:result) { subject.comments = new_value; subject.comments }
168
+
169
+ context 'to nil' do
170
+ let(:new_value) { nil }
171
+
172
+ it { expect { result }.to_not raise_exception }
173
+ it { expect( result ).to be_a Opium::Model::Relation }
174
+ end
175
+
176
+ context 'to []' do
177
+ let(:new_value) { [] }
178
+
179
+ it { expect { result }.to_not raise_exception }
180
+ it { expect( result ).to be_a Opium::Model::Relation }
181
+ end
182
+
183
+ context 'to an array of models' do
184
+ let(:comment) { Comment.new }
185
+ let(:new_value) { [ comment ] }
186
+
187
+ it { expect { result }.to_not raise_exception }
188
+ it { expect( result ).to be_a Opium::Model::Relation }
189
+ it { expect( result ).to include( comment ) }
190
+ end
191
+ end
164
192
  end
165
193
 
166
194
  describe '.has_one' do
@@ -87,6 +87,22 @@ describe Opium::Model::Relation do
87
87
  it { expect( result ).to be_nil }
88
88
  end
89
89
 
90
+ context 'with an empty array' do
91
+ let(:convert_from) { [] }
92
+
93
+ it { expect { result }.to_not raise_exception }
94
+ it { expect( result ).to be_nil }
95
+ end
96
+
97
+ context 'with an array of RelatedClass' do
98
+ let(:related_object) { RelatedClass.new title: 'a related object' }
99
+ let(:convert_from) { [ related_object ] }
100
+
101
+ it { expect { result }.to_not raise_exception }
102
+ it { expect( result ).to be_a described_class }
103
+ it { expect( result ).to include( related_object ) }
104
+ end
105
+
90
106
  context 'with any unconvertable value' do
91
107
  let(:convert_from) { 42 }
92
108
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opium
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Bowers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-20 00:00:00.000000000 Z
11
+ date: 2015-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler