moribus 0.0.1
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 +15 -0
- data/.gitignore +35 -0
- data/.rspec +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.simplecov +42 -0
- data/.travis.yml +8 -0
- data/Gemfile +19 -0
- data/LICENSE +20 -0
- data/README.md +104 -0
- data/Rakefile +15 -0
- data/lib/colorized_text.rb +33 -0
- data/lib/moribus.rb +133 -0
- data/lib/moribus/aggregated_behavior.rb +80 -0
- data/lib/moribus/aggregated_cache_behavior.rb +76 -0
- data/lib/moribus/alias_association.rb +106 -0
- data/lib/moribus/extensions.rb +37 -0
- data/lib/moribus/extensions/delegate_associated.rb +48 -0
- data/lib/moribus/extensions/has_aggregated_extension.rb +94 -0
- data/lib/moribus/extensions/has_current_extension.rb +17 -0
- data/lib/moribus/macros.rb +120 -0
- data/lib/moribus/tracked_behavior.rb +91 -0
- data/lib/moribus/version.rb +3 -0
- data/moribus.gemspec +33 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +61 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/moribus/alias_association_spec.rb +88 -0
- data/spec/moribus/macros_spec.rb +7 -0
- data/spec/moribus_spec.rb +332 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/moribus_spec_model.rb +57 -0
- metadata +209 -0
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/404.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
23
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/422.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The change you wanted was rejected.</h1>
|
23
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/500.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>We're sorry, but something went wrong.</h1>
|
23
|
+
</div>
|
24
|
+
</body>
|
25
|
+
</html>
|
File without changes
|
@@ -0,0 +1,6 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
5
|
+
require File.expand_path('../../config/boot', __FILE__)
|
6
|
+
require 'rails/commands'
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Moribus::AliasAssociation do
|
4
|
+
before do
|
5
|
+
class SpecPost < MoribusSpecModel(:spec_author_id => :integer, :body => :string)
|
6
|
+
belongs_to :spec_author , :alias => :creator
|
7
|
+
has_many :spec_comments , :alias => :remarks
|
8
|
+
has_one :spec_post_info, :alias => :information
|
9
|
+
|
10
|
+
alias_association :author , :spec_author
|
11
|
+
alias_association :comments , :spec_comments
|
12
|
+
alias_association :post_info, :spec_post_info
|
13
|
+
end
|
14
|
+
|
15
|
+
class SpecAuthor < MoribusSpecModel(:name => :string)
|
16
|
+
has_many :spec_posts
|
17
|
+
end
|
18
|
+
|
19
|
+
class SpecPostInfo < MoribusSpecModel(:spec_post_id => :integer, :ip => :string)
|
20
|
+
belongs_to :spec_post, :alias => :note
|
21
|
+
end
|
22
|
+
|
23
|
+
class SpecComment < MoribusSpecModel(:spec_post_id => :integer, :body => :string)
|
24
|
+
belongs_to :spec_post
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
after do
|
29
|
+
MoribusSpecModel.cleanup!
|
30
|
+
end
|
31
|
+
|
32
|
+
before do
|
33
|
+
author = SpecAuthor.create(:name => 'John')
|
34
|
+
@post = author.spec_posts.create(:body => 'Post Body')
|
35
|
+
@post.spec_comments.create(:body => 'Fabulous!')
|
36
|
+
@post.create_spec_post_info(:ip => '127.0.0.1')
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "reflection aliasing" do
|
40
|
+
it "alias association name in reflections" do
|
41
|
+
SpecPost.reflect_on_association(:author).should_not be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not raise error when using aliased name in scopes" do
|
45
|
+
expect{
|
46
|
+
SpecPost.includes(:comments).first
|
47
|
+
}.to_not raise_error
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "association accessor alias methods" do
|
52
|
+
subject{ @post }
|
53
|
+
|
54
|
+
it{ should respond_to :author }
|
55
|
+
it{ should respond_to :author= }
|
56
|
+
it{ should respond_to :comments }
|
57
|
+
it{ should respond_to :comments= }
|
58
|
+
it{ should respond_to :post_info }
|
59
|
+
it{ should respond_to :post_info= }
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "singular association alias method" do
|
63
|
+
subject{ @post }
|
64
|
+
|
65
|
+
it{ should respond_to :build_author }
|
66
|
+
it{ should respond_to :create_author }
|
67
|
+
it{ should respond_to :create_author! }
|
68
|
+
|
69
|
+
it{ should respond_to :build_post_info }
|
70
|
+
it{ should respond_to :create_post_info }
|
71
|
+
it{ should respond_to :create_post_info! }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "collection association alias method" do
|
75
|
+
subject{ @post }
|
76
|
+
|
77
|
+
it{ should respond_to :comment_ids }
|
78
|
+
it{ should respond_to :comment_ids= }
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ":alias => alias_name shortcuts" do
|
82
|
+
subject{ @post }
|
83
|
+
|
84
|
+
it { should respond_to :creator }
|
85
|
+
it { should respond_to :remarks }
|
86
|
+
it { should respond_to :information }
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Moribus do
|
4
|
+
before do
|
5
|
+
class SpecStatus < MoribusSpecModel(:name => :string, :description => :string)
|
6
|
+
acts_as_enumerated
|
7
|
+
|
8
|
+
self.enumeration_model_updates_permitted = true
|
9
|
+
create!(:name => 'inactive', :description => 'Inactive')
|
10
|
+
create!(:name => 'active', :description => 'Active')
|
11
|
+
end
|
12
|
+
|
13
|
+
class SpecType < MoribusSpecModel(:name => :string, :description => :string)
|
14
|
+
acts_as_enumerated
|
15
|
+
|
16
|
+
self.enumeration_model_updates_permitted = true
|
17
|
+
create!(:name => 'important', :description => 'Important')
|
18
|
+
create!(:name => 'unimportant', :description => 'Unimportant')
|
19
|
+
end
|
20
|
+
|
21
|
+
class SpecSuffix < MoribusSpecModel(:name => :string, :description => :string)
|
22
|
+
acts_as_enumerated
|
23
|
+
|
24
|
+
self.enumeration_model_updates_permitted = true
|
25
|
+
create!(:name => 'none', :description => '')
|
26
|
+
create!(:name => 'jr', :description => 'Junior')
|
27
|
+
end
|
28
|
+
|
29
|
+
class SpecPersonName < MoribusSpecModel(:first_name => :string, :last_name => :string, :spec_suffix_id => :integer)
|
30
|
+
acts_as_aggregated
|
31
|
+
has_enumerated :spec_suffix, :default => ''
|
32
|
+
|
33
|
+
validates_presence_of :first_name, :last_name
|
34
|
+
|
35
|
+
# custom writer that additionally strips first name
|
36
|
+
def first_name=(value)
|
37
|
+
self[:first_name] = value.strip
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class SpecCustomerFeature < MoribusSpecModel(:feature_name => :string)
|
42
|
+
acts_as_aggregated :cache_by => :feature_name
|
43
|
+
end
|
44
|
+
|
45
|
+
class SpecCustomerInfo < MoribusSpecModel( :spec_customer_id => :integer!,
|
46
|
+
:spec_person_name_id => :integer,
|
47
|
+
:spec_status_id => :integer,
|
48
|
+
:spec_type_id => :integer,
|
49
|
+
:is_current => :boolean,
|
50
|
+
:lock_version => :integer,
|
51
|
+
:created_at => :datetime,
|
52
|
+
:updated_at => :datetime,
|
53
|
+
:previous_id => :integer )
|
54
|
+
attr :custom_field
|
55
|
+
|
56
|
+
belongs_to :spec_customer, :inverse_of => :spec_customer_info, :touch => true
|
57
|
+
has_aggregated :spec_person_name
|
58
|
+
has_enumerated :spec_status
|
59
|
+
has_enumerated :spec_type
|
60
|
+
|
61
|
+
acts_as_tracked :preceding_key => :previous_id
|
62
|
+
end
|
63
|
+
|
64
|
+
class SpecCustomer < MoribusSpecModel(:spec_status_id => :integer)
|
65
|
+
has_one_current :spec_customer_info, :inverse_of => :spec_customer
|
66
|
+
has_enumerated :spec_status, :default => 'inactive'
|
67
|
+
|
68
|
+
delegate_associated :spec_person_name, :custom_field, :spec_type, :to => :spec_customer_info
|
69
|
+
end
|
70
|
+
|
71
|
+
class SpecCustomerEmail < MoribusSpecModel(:spec_customer_id => :integer, :email => :string, :is_current => :boolean, :status => :string)
|
72
|
+
connection.add_index table_name, [:email, :is_current], :unique => true
|
73
|
+
|
74
|
+
belongs_to :spec_customer
|
75
|
+
|
76
|
+
acts_as_tracked
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
after do
|
81
|
+
MoribusSpecModel.cleanup!
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "common behavior" do
|
85
|
+
before do
|
86
|
+
@info = SpecCustomerInfo.create(
|
87
|
+
:spec_customer_id => 1,
|
88
|
+
:spec_person_name_id => 1,
|
89
|
+
:is_current => true,
|
90
|
+
:created_at => 5.days.ago,
|
91
|
+
:updated_at => 5.days.ago
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should revert changes if exception is raised" do
|
96
|
+
old_id = @info.id
|
97
|
+
old_updated_at = @info.updated_at
|
98
|
+
old_created_at = @info.created_at
|
99
|
+
suppress(Exception) do
|
100
|
+
expect {
|
101
|
+
@info.update_attributes :spec_customer_id => nil, :spec_person_name_id => 2
|
102
|
+
}.not_to change(SpecCustomerInfo, :count)
|
103
|
+
end
|
104
|
+
@info.new_record?.should be_false
|
105
|
+
@info.id.should == old_id
|
106
|
+
@info.updated_at.should == old_updated_at
|
107
|
+
@info.created_at.should == old_created_at
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'Aggregated' do
|
112
|
+
context "definition" do
|
113
|
+
it "should raise an error on an unknown option" do
|
114
|
+
expect{
|
115
|
+
Class.new(ActiveRecord::Base).class_eval do
|
116
|
+
acts_as_aggregated :invalid_key => :error
|
117
|
+
end
|
118
|
+
}.to raise_error(ArgumentError)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should raise an error when including AggregatedCacheBehavior without AggregatedBehavior" do
|
122
|
+
expect{
|
123
|
+
Class.new(ActiveRecord::Base).class_eval do
|
124
|
+
include Moribus::AggregatedCacheBehavior
|
125
|
+
end
|
126
|
+
}.to raise_error(Moribus::AggregatedCacheBehavior::NotAggregatedError)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
before do
|
131
|
+
@existing = SpecPersonName.create! :first_name => 'John', :last_name => 'Smith'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should not duplicate records" do
|
135
|
+
expect {
|
136
|
+
SpecPersonName.create :first_name => ' John ', :last_name => 'Smith'
|
137
|
+
}.not_to change(SpecPersonName, :count)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should lookup self and replace id with existing on create" do
|
141
|
+
name = SpecPersonName.new :first_name => 'John', :last_name => 'Smith'
|
142
|
+
name.save
|
143
|
+
name.id.should == @existing.id
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should create a new record if lookup fails" do
|
147
|
+
expect {
|
148
|
+
SpecPersonName.create :first_name => 'Alice', :last_name => 'Smith'
|
149
|
+
}.to change(SpecPersonName, :count).by(1)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should lookup self and replace id with existing on update" do
|
153
|
+
name = SpecPersonName.create :first_name => 'Alice', :last_name => 'Smith'
|
154
|
+
name.update_attributes :first_name => 'John'
|
155
|
+
name.id.should == @existing.id
|
156
|
+
end
|
157
|
+
|
158
|
+
context "with caching" do
|
159
|
+
before do
|
160
|
+
@existing = SpecCustomerFeature.create(:feature_name => 'Pays')
|
161
|
+
SpecCustomerFeature.clear_cache
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should lookup the existing value and add it to the cache" do
|
165
|
+
feature = SpecCustomerFeature.new :feature_name => @existing.feature_name
|
166
|
+
expect{ feature.save }.to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
167
|
+
feature.id.should == @existing.id
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should add the freshly-created record to the cache" do
|
171
|
+
expect{ SpecCustomerFeature.create(:feature_name => 'Fraud') }.to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should freeze the cached object" do
|
175
|
+
feature = SpecCustomerFeature.create(:feature_name => 'Cancelled')
|
176
|
+
SpecCustomerFeature.aggregated_records_cache[feature.feature_name].should be_frozen
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should cache the clone of the record, not the record itself" do
|
180
|
+
feature = SpecCustomerFeature.create(:feature_name => 'Returned')
|
181
|
+
SpecCustomerFeature.aggregated_records_cache[feature.feature_name].object_id.should_not == feature.object_id
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'Tracked' do
|
187
|
+
before do
|
188
|
+
@customer = SpecCustomer.create
|
189
|
+
@info = @customer.create_spec_customer_info :spec_person_name_id => 1
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should create a new current record if updated" do
|
193
|
+
expect {
|
194
|
+
@info.update_attributes(:spec_person_name_id => 2)
|
195
|
+
}.to change(SpecCustomerInfo, :count).by(1)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should replace itself with new id" do
|
199
|
+
old_id = @info.id
|
200
|
+
@info.update_attributes(:spec_person_name_id => 2)
|
201
|
+
@info.id.should_not == old_id
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should set is_current record to false for superseded record" do
|
205
|
+
old_id = @info.id
|
206
|
+
@info.update_attributes(:spec_person_name_id => 2)
|
207
|
+
SpecCustomerInfo.find(old_id).is_current.should be_false
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should set previous_id to the id of the previous record" do
|
211
|
+
old_id = @info.id
|
212
|
+
@info.update_attributes(:spec_person_name_id => 2)
|
213
|
+
@info.previous_id.should == old_id
|
214
|
+
end
|
215
|
+
|
216
|
+
it "assigning a new current record should change is_current to false for previous one" do
|
217
|
+
new_info = SpecCustomerInfo.new :spec_person_name_id => 2, :is_current => true
|
218
|
+
@customer.spec_customer_info = new_info
|
219
|
+
new_info.spec_customer_id.should == @customer.id
|
220
|
+
@info.is_current.should be_false
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should not crash on superseding with 'is_current' conditional constraint" do
|
224
|
+
email = SpecCustomerEmail.create(:spec_customer => @customer, :email => 'foo@bar.com', :status => 'unverified', :is_current => true)
|
225
|
+
expect{ email.update_attributes(:status => 'verified') }.not_to raise_error
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'updated_at and created_at' do
|
229
|
+
let(:first_time) { Time.zone.parse('2012-07-16 00:00:00') }
|
230
|
+
let(:second_time) { Time.zone.parse('2012-07-17 08:10:15') }
|
231
|
+
|
232
|
+
before { Timecop.freeze(first_time) }
|
233
|
+
after { Timecop.return }
|
234
|
+
|
235
|
+
it "should be updated on change" do
|
236
|
+
info = @customer.create_spec_customer_info :spec_person_name_id => 1
|
237
|
+
info.updated_at.should == first_time
|
238
|
+
info.created_at.should == first_time
|
239
|
+
|
240
|
+
Timecop.freeze(second_time)
|
241
|
+
info.spec_person_name_id = 2
|
242
|
+
info.save!
|
243
|
+
info.updated_at.should == second_time
|
244
|
+
info.created_at.should == second_time
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe "Optimistic Locking" do
|
249
|
+
before do
|
250
|
+
@info1 = @customer.reload.spec_customer_info
|
251
|
+
@info2 = @customer.reload.spec_customer_info
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should raise stale object error" do
|
255
|
+
@info1.update_attributes(:spec_person_name_id => 3)
|
256
|
+
|
257
|
+
expect{ @info2.update_attributes(:spec_person_name_id => 4) }.to raise_error(ActiveRecord::StaleObjectError)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should not fail if no locking_column present" do
|
261
|
+
email = SpecCustomerEmail.create(:spec_customer_id => 1, :email => 'foo@bar.com')
|
262
|
+
expect{ email.update_attributes(:email => 'foo2@bar.com') }.not_to raise_error
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe 'with Aggregated' do
|
267
|
+
before do
|
268
|
+
@info.spec_person_name = SpecPersonName.create(:first_name => 'John', :last_name => 'Smith')
|
269
|
+
@info.save
|
270
|
+
@info.reload
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should supersede when nested record changes" do
|
274
|
+
old_id = @info.id
|
275
|
+
@customer.spec_customer_info.spec_person_name.first_name = 'Alice'
|
276
|
+
expect{ @customer.save }.to change(@info, :spec_person_name_id)
|
277
|
+
@info.id.should_not == old_id
|
278
|
+
@info.is_current.should == true
|
279
|
+
SpecCustomerInfo.find(old_id).is_current.should be_false
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe 'Delegations' do
|
285
|
+
before do
|
286
|
+
@customer = SpecCustomer.create(
|
287
|
+
:spec_customer_info_attributes => {
|
288
|
+
:spec_person_name_attributes => {:first_name => ' John ', :last_name => 'Smith'} } )
|
289
|
+
@info = @customer.spec_customer_info
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should have delegated column information" do
|
293
|
+
@customer.column_for_attribute(:first_name).should_not be_nil
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should not delegate special methods" do
|
297
|
+
@customer.should_not respond_to(:reset_first_name)
|
298
|
+
@customer.should_not respond_to(:first_name_was)
|
299
|
+
@customer.should_not respond_to(:first_name_before_type_cast)
|
300
|
+
@customer.should_not respond_to(:first_name_will_change!)
|
301
|
+
@customer.should_not respond_to(:first_name_changed?)
|
302
|
+
@customer.should_not respond_to(:lock_version)
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should delegate methods to aggregated parts" do
|
306
|
+
@info.should respond_to(:first_name)
|
307
|
+
@info.should respond_to(:first_name=)
|
308
|
+
@info.should respond_to(:spec_suffix)
|
309
|
+
@info.last_name.should == 'Smith'
|
310
|
+
end
|
311
|
+
|
312
|
+
it "should delegate methods to representation" do
|
313
|
+
@customer.should respond_to(:first_name)
|
314
|
+
@customer.should respond_to(:first_name=)
|
315
|
+
@customer.should respond_to(:spec_suffix)
|
316
|
+
@customer.last_name.should == 'Smith'
|
317
|
+
@customer.should respond_to(:custom_field)
|
318
|
+
@customer.should respond_to(:custom_field=)
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should properly delegate enumerated attributes' do
|
322
|
+
@customer.should respond_to(:spec_type)
|
323
|
+
@customer.should respond_to(:spec_type=)
|
324
|
+
@customer.spec_type = :important
|
325
|
+
@customer.spec_type.should === :important
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should raise NoMethodError if unknown method received" do
|
329
|
+
expect{ @customer.impossibru }.to raise_error(NoMethodError)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|