cistern 2.2.7 → 2.3.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.
@@ -5,23 +5,48 @@ module Cistern::Collection
5
5
  :keep_if, :pop, :shift, :delete_at, :compact
6
6
  ].to_set # :nodoc:
7
7
 
8
- def self.service_collection(service, klass, name)
9
- service.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
8
+ def self.cistern_collection(cistern, klass, name)
9
+ cistern.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
10
10
  def #{name}(attributes={})
11
- #{klass.name}.new(attributes.merge(service: self))
11
+ #{klass.name}.new(attributes.merge(cistern: self))
12
12
  end
13
13
  EOS
14
14
  end
15
15
 
16
- attr_accessor :records, :loaded, :service
16
+ attr_accessor :records, :loaded, :cistern
17
+
18
+ def service=(service)
19
+ Cistern.deprecation(
20
+ '#service= is deprecated. Please use #cistern=',
21
+ caller[0]
22
+ )
23
+ @cistern = service
24
+ end
25
+
26
+ def service
27
+ Cistern.deprecation(
28
+ '#service is deprecated. Please use #cistern',
29
+ caller[0]
30
+ )
31
+ @cistern
32
+ end
17
33
 
18
34
  module ClassMethods
19
35
  def model(new_model = nil)
20
36
  @_model ||= new_model
21
37
  end
22
38
 
39
+ # @deprecated Use {#cistern_method} instead
23
40
  def service_method(name = nil)
24
- @_service_method ||= name
41
+ Cistern.deprecation(
42
+ '#service_method is deprecated. Please use #cistern_method',
43
+ caller[0]
44
+ )
45
+ @_cistern_method ||= name
46
+ end
47
+
48
+ def cistern_method(name = nil)
49
+ @_cistern_method ||= name
25
50
  end
26
51
  end
27
52
 
@@ -80,7 +105,7 @@ module Cistern::Collection
80
105
  model.new(
81
106
  {
82
107
  collection: self,
83
- service: service
108
+ cistern: cistern,
84
109
  }.merge(attributes)
85
110
  )
86
111
  end
data/lib/cistern/model.rb CHANGED
@@ -7,21 +7,46 @@ module Cistern::Model
7
7
  klass.send(:extend, Cistern::Model::ClassMethods)
8
8
  end
9
9
 
10
- def self.service_model(service, klass, name)
11
- service.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
10
+ def self.cistern_model(cistern, klass, name)
11
+ cistern.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
12
12
  def #{name}(attributes={})
13
- #{klass.name}.new(attributes.merge(service: self))
13
+ #{klass.name}.new(attributes.merge(cistern: self))
14
14
  end
15
15
  EOS
16
16
  end
17
17
 
18
18
  module ClassMethods
19
+ # @deprecated Use {#cistern_method} instead
19
20
  def service_method(name = nil)
20
- @_service_method ||= name
21
+ Cistern.deprecation(
22
+ '#service_method is deprecated. Please use #cistern_method',
23
+ caller[0]
24
+ )
25
+ @_cistern_method ||= name
26
+ end
27
+
28
+ def cistern_method(name = nil)
29
+ @_cistern_method ||= name
21
30
  end
22
31
  end
23
32
 
24
- attr_accessor :collection, :service
33
+ attr_accessor :collection, :cistern
34
+
35
+ def service=(service)
36
+ Cistern.deprecation(
37
+ '#service= is deprecated. Please use #cistern=',
38
+ caller[0]
39
+ )
40
+ @cistern = service
41
+ end
42
+
43
+ def service
44
+ Cistern.deprecation(
45
+ '#service is deprecated. Please use #cistern',
46
+ caller[0]
47
+ )
48
+ @cistern
49
+ end
25
50
 
26
51
  def inspect
27
52
  Cistern.formatter.call(self)
@@ -31,8 +56,10 @@ module Cistern::Model
31
56
  merge_attributes(attributes)
32
57
  end
33
58
 
59
+ # Merge #attributes and call {#save}. Valid and change attributes are available in {#dirty_attributes}
60
+ # @param attributes [Hash]
34
61
  def update(attributes)
35
- merge_attributes(attributes)
62
+ stage_attributes(attributes)
36
63
  save
37
64
  end
38
65
 
@@ -67,15 +94,23 @@ module Cistern::Model
67
94
  end
68
95
  end
69
96
 
70
- def wait_for(timeout = service_class.timeout, interval = service_class.poll_interval, &block)
71
- service_class.wait_for(timeout, interval) { reload && block.call(self) }
97
+ def wait_for(timeout = cistern_class.timeout, interval = cistern_class.poll_interval, &block)
98
+ cistern_class.wait_for(timeout, interval) { reload && block.call(self) }
72
99
  end
73
100
 
74
- def wait_for!(timeout = service_class.timeout, interval = service_class.poll_interval, &block)
75
- service_class.wait_for!(timeout, interval) { reload && block.call(self) }
101
+ def wait_for!(timeout = cistern_class.timeout, interval = cistern_class.poll_interval, &block)
102
+ cistern_class.wait_for!(timeout, interval) { reload && block.call(self) }
76
103
  end
77
104
 
78
105
  def service_class
79
- service ? service.class : Cistern
106
+ Cistern.deprecation(
107
+ '#service_class is deprecated. Please use #cistern_class',
108
+ caller[0]
109
+ )
110
+ cistern ? cistern.class : Cistern
111
+ end
112
+
113
+ def cistern_class
114
+ cistern ? cistern.class : Cistern
80
115
  end
81
116
  end
@@ -1,31 +1,56 @@
1
1
  module Cistern::Request
2
- def self.service_request(service, klass, name)
3
- unless klass.name
2
+ def self.cistern_request(cistern, klass, name)
3
+ unless klass.name || klass.cistern_method
4
4
  fail ArgumentError, "can't turn anonymous class into a Cistern request"
5
5
  end
6
6
 
7
- service::Mock.module_eval <<-EOS, __FILE__, __LINE__
7
+ cistern::Mock.module_eval <<-EOS, __FILE__, __LINE__
8
8
  def #{name}(*args)
9
9
  #{klass}.new(self)._mock(*args)
10
10
  end
11
11
  EOS
12
12
 
13
- service::Real.module_eval <<-EOS, __FILE__, __LINE__
13
+ cistern::Real.module_eval <<-EOS, __FILE__, __LINE__
14
14
  def #{name}(*args)
15
15
  #{klass}.new(self)._real(*args)
16
16
  end
17
17
  EOS
18
18
  end
19
19
 
20
- attr_reader :service
20
+ def self.service_request(*args)
21
+ Cistern.deprecation(
22
+ '#service_request is deprecated. Please use #cistern_request',
23
+ caller[0]
24
+ )
25
+ cistern_request(*args)
26
+ end
27
+
28
+ attr_reader :cistern
29
+
30
+ def service
31
+ Cistern.deprecation(
32
+ '#service is deprecated. Please use #cistern',
33
+ caller[0]
34
+ )
35
+ @cistern
36
+ end
21
37
 
22
- def initialize(service)
23
- @service = service
38
+ def initialize(cistern)
39
+ @cistern = cistern
24
40
  end
25
41
 
26
42
  module ClassMethods
43
+ # @deprecated Use {#cistern_method} instead
27
44
  def service_method(name = nil)
28
- @_service_method ||= name
45
+ Cistern.deprecation(
46
+ '#service_method is deprecated. Please use #cistern_method',
47
+ caller[0]
48
+ )
49
+ @_cistern_method ||= name
50
+ end
51
+
52
+ def cistern_method(name = nil)
53
+ @_cistern_method ||= name
29
54
  end
30
55
  end
31
56
  end
@@ -1,8 +1,8 @@
1
1
  module Cistern::Singular
2
- def self.service_singular(service, klass, name)
3
- service.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
2
+ def self.cistern_singular(cistern, klass, name)
3
+ cistern.const_get(:Collections).module_eval <<-EOS, __FILE__, __LINE__
4
4
  def #{name}(attributes={})
5
- #{klass.name}.new(attributes.merge(service: self))
5
+ #{klass.name}.new(attributes.merge(cistern: self))
6
6
  end
7
7
  EOS
8
8
  end
@@ -13,7 +13,15 @@ module Cistern::Singular
13
13
  klass.send(:extend, Cistern::Model::ClassMethods)
14
14
  end
15
15
 
16
- attr_accessor :service
16
+ attr_accessor :cistern
17
+
18
+ def service
19
+ Cistern.deprecation(
20
+ '#service is deprecated. Please use #cistern',
21
+ caller[0]
22
+ )
23
+ @cistern
24
+ end
17
25
 
18
26
  def inspect
19
27
  Cistern.formatter.call(self)
@@ -1,3 +1,3 @@
1
1
  module Cistern
2
- VERSION = '2.2.7'
2
+ VERSION = '2.3.0'
3
3
  end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cistern::Attributes, 'requires' do
4
+ class RequireSpec < Sample::Model
5
+ identity :id
6
+ attribute :name, type: :string
7
+ attribute :type
8
+ end
9
+
10
+ it 'raises if required attributes are not present' do
11
+ expect {
12
+ Sample.new.require_spec.requires :name
13
+ }.to raise_exception(ArgumentError, /name is required/)
14
+
15
+ data = { name: '1' }
16
+ return_value = Sample.new.require_spec(data).requires :name
17
+
18
+ expect(return_value).to eq(data)
19
+
20
+ expect {
21
+ Sample.new.require_spec.requires :name, :type
22
+ }.to raise_exception(ArgumentError, /name and type are required/)
23
+
24
+ data = { name: '1', type: 'sample' }
25
+ return_values = Sample.new.require_spec(data).requires :name, :type
26
+ expect(return_values).to eq(data)
27
+ end
28
+
29
+ it 'raises if a required attribute attribute is not present' do
30
+ expect {
31
+ Sample.new.require_spec.requires_one :name, :type
32
+ }.to raise_exception(ArgumentError, /name or type are required/)
33
+
34
+ data = { name: '1' }
35
+ return_value = Sample.new.require_spec(data).requires_one :name, :type
36
+
37
+ expect(return_value).to eq(data)
38
+
39
+ data = { name: '1', type: 'sample' }
40
+ return_values = Sample.new.require_spec(data).requires_one :name, :type
41
+ expect(return_values).to eq(data)
42
+ end
43
+ end
44
+
45
+ describe Cistern::Attributes, 'parsing' do
46
+ class TypeSpec < Sample::Model
47
+ identity :id
48
+ attribute :name, type: :string
49
+ attribute :created_at, type: :time
50
+ attribute :flag, type: :boolean
51
+ attribute :list, type: :array
52
+ attribute :number, type: :integer
53
+ attribute :floater, type: :float
54
+ attribute :butternut_id, squash: %w(squash id), type: :integer
55
+ attribute :butternut_type, squash: %w(squash type)
56
+ attribute :squash
57
+ attribute :vegetable, aliases: 'squash'
58
+ attribute :custom, parser: lambda { |v, _| "X!#{v}" }
59
+ attribute :default, default: 'im a squash'
60
+ attribute :string_allow_nil, type: :string, allow_nil: true
61
+
62
+ attribute :same_alias_1, aliases: 'nested'
63
+ attribute :same_alias_2, aliases: 'nested'
64
+
65
+ attribute :same_alias_squashed_1, squash: %w(nested attr_1)
66
+ attribute :same_alias_squashed_2, squash: %w(nested attr_2)
67
+ attribute :same_alias_squashed_3, squash: %w(nested attr_2)
68
+ attribute :adam_attributes, aliases: 'attributes'
69
+
70
+ def save
71
+ requires :flag
72
+ end
73
+ end
74
+
75
+ it 'should parse string' do
76
+ expect(TypeSpec.new(name: 1).name).to eq('1')
77
+ expect(TypeSpec.new(name: "b").name).to eq('b')
78
+ expect(TypeSpec.new(name: nil).name).to eq("")
79
+ end
80
+
81
+ it 'should allow nils in string types' do
82
+ expect(TypeSpec.new(string_allow_nil: nil).string_allow_nil).to eq(nil)
83
+ end
84
+ it "should handle a 'attributes' aliased attribute" do
85
+ expect(TypeSpec.new(attributes: 'x').adam_attributes).to eq('x')
86
+ end
87
+
88
+ it 'should parse time' do
89
+ time = Time.now
90
+ created_at = TypeSpec.new(created_at: time.to_s).created_at
91
+ expect(created_at).to be_a(Time)
92
+ expect(created_at.to_i).to eq(time.to_i)
93
+ end
94
+
95
+ it 'should parse boolean' do
96
+ expect(TypeSpec.new(flag: 'false').flag).to be_falsey
97
+ expect(TypeSpec.new(flag: 'true').flag).to be_truthy
98
+ expect(TypeSpec.new(flag: false).flag).to be_falsey
99
+ expect(TypeSpec.new(flag: true).flag).to be_truthy
100
+ expect(TypeSpec.new(flag: '0').flag).to be_falsey
101
+ expect(TypeSpec.new(flag: '1').flag).to be_truthy
102
+ expect(TypeSpec.new(flag: 0).flag).to be_falsey
103
+ expect(TypeSpec.new(flag: 1).flag).to be_truthy
104
+ expect(TypeSpec.new(flag: false)).not_to be_flag
105
+ expect(TypeSpec.new(flag: true)).to be_flag
106
+ end
107
+
108
+ it 'should parse an array' do
109
+ expect(TypeSpec.new(list: []).list).to eq([])
110
+ expect(TypeSpec.new(list: 'item').list).to eq(['item'])
111
+ end
112
+
113
+ it 'should parse a float' do
114
+ expect(TypeSpec.new(floater: '0.01').floater).to eq(0.01)
115
+ expect(TypeSpec.new(floater: 0.01).floater).to eq(0.01)
116
+ end
117
+
118
+ it 'should use custom parser' do
119
+ expect(TypeSpec.new(custom: '15').custom).to eq('X!15')
120
+ end
121
+
122
+ it 'should squash, cast, alias an attribute and keep a vanilla reference' do
123
+ # vanilla squash
124
+ expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_type).to eq('fred')
125
+ expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => nil } }).butternut_type).to be_nil
126
+ expect(TypeSpec.new({ 'squash' => nil }).butternut_type).to be_nil
127
+
128
+ # composite processors: squash and cast
129
+ expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).butternut_id).to eq(12)
130
+ expect(TypeSpec.new({ 'squash' => { 'id' => nil, 'type' => 'fred' } }).butternut_id).to be_nil
131
+ expect(TypeSpec.new({ 'squash' => { 'type' => 'fred' } }).butternut_id).to be_nil
132
+
133
+ # override intermediate processing
134
+ expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).squash).to eq({ 'id' => '12', 'type' => 'fred' })
135
+
136
+ # alias of override
137
+ expect(TypeSpec.new({ 'squash' => { 'id' => '12', 'type' => 'fred' } }).vegetable).to eq({ 'id' => '12', 'type' => 'fred' })
138
+ end
139
+
140
+ it 'should set a default value' do
141
+ expect(TypeSpec.new.default).to eq('im a squash')
142
+ end
143
+
144
+ it 'should override a default value' do
145
+ expect(TypeSpec.new(default: 'now im a different squash').default).to eq('now im a different squash')
146
+ end
147
+
148
+ context 'allowing the same alias for multiple attributes' do
149
+ it 'should do so when not squashing' do
150
+ type_spec = TypeSpec.new({ 'nested' => 'bamboo' })
151
+ expect(type_spec.same_alias_1).to eq('bamboo')
152
+ expect(type_spec.same_alias_2).to eq('bamboo')
153
+ end
154
+
155
+ it 'should do so when squashing' do
156
+ type_spec = TypeSpec.new({ 'nested' => { 'attr_1' => 'bamboo', 'attr_2' => 'panda' } })
157
+ expect(type_spec.same_alias_squashed_1).to eq('bamboo')
158
+ expect(type_spec.same_alias_squashed_2).to eq('panda')
159
+ expect(type_spec.same_alias_squashed_3).to eq('panda')
160
+ end
161
+ end
162
+
163
+ it 'should slice out unaccounted for attributes' do
164
+ expect(TypeSpec.new({ 'something' => { 'id' => '12' } }).attributes.keys).not_to include('something')
165
+ end
166
+ end
@@ -1,7 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Cistern::Collection' do
4
- class SampleService < Cistern::Service
4
+ class SampleService
5
+ include Cistern::Client
5
6
  end
6
7
 
7
8
  class Drug < SampleService::Model
@@ -18,7 +19,7 @@ describe 'Cistern::Collection' do
18
19
  end
19
20
 
20
21
  class Tacs < SampleService::Collection
21
- service_method :toes
22
+ cistern_method :toes
22
23
  end
23
24
 
24
25
  it 'should generate a default collection method' do
@@ -64,4 +65,19 @@ describe 'Cistern::Collection' do
64
65
  it 'should ==' do
65
66
  Drugs.new.all == Drugs.new.all
66
67
  end
68
+
69
+ describe 'deprecation', :deprecated do
70
+ class DeprecatedCollectionService
71
+ include Cistern::Client
72
+ end
73
+
74
+ it 'responds to #service' do
75
+ class DeprecationCollection < DeprecatedCollectionService::Collection
76
+ service_method :deprecator
77
+ end
78
+
79
+ sample = DeprecatedCollectionService.new.deprecator
80
+ expect(sample.service).to eq(sample.cistern)
81
+ end
82
+ end
67
83
  end