HornsAndHooves-moribus 0.4.4 → 0.5.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 +5 -5
- data/.simplecov +1 -1
- data/README.md +4 -4
- data/lib/moribus/tracked_behavior.rb +13 -1
- data/lib/moribus/version.rb +1 -1
- data/spec/moribus/aggregated_behavior_spec.rb +131 -0
- data/spec/moribus/alias_association_spec.rb +14 -14
- data/spec/moribus/extensions/delegate_associated_spec.rb +136 -0
- data/spec/moribus/extensions/has_current_extension_spec.rb +40 -0
- data/spec/moribus/macros_spec.rb +95 -1
- data/spec/moribus_spec.rb +98 -215
- data/spec/support/moribus_spec_model.rb +6 -4
- data/tags +47 -9
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 69f7a6978f05898888e7ef3e11471ea7606eb5211ffd7f098158bd67eec829a3
|
|
4
|
+
data.tar.gz: 6ac6db71051f8800f447e3dcd827abee37181754aa2f32e9ffddfd9e698392e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eec5927604715080d2fd3ae72b71da0dfc4169de6949507904426f87e7a75768e4b953e07ce3efa46dfd0109fb66b743fa8e86abbb961d9ec2b971fbff480a16
|
|
7
|
+
data.tar.gz: 60f7975b1ad8f4818fade8a5a8f9c68d89d9244d586f6d89b6033a53fe47b7aa748378b777c9f5caa345dd90286a997f9f89d3f585901ae9f9a2c3a98427b611
|
data/.simplecov
CHANGED
|
@@ -12,7 +12,7 @@ SimpleCov.start do
|
|
|
12
12
|
# Fail the build when coverage is weak:
|
|
13
13
|
at_exit do
|
|
14
14
|
SimpleCov.result.format!
|
|
15
|
-
threshold, actual =
|
|
15
|
+
threshold, actual = 99.75, SimpleCov.result.covered_percent
|
|
16
16
|
if actual < threshold
|
|
17
17
|
msg = "\nLow coverage: "
|
|
18
18
|
msg << red("#{actual}%")
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Moribus is a set of tools for managing complex graphs of ActiveRecord objects
|
|
|
6
6
|
for which there are many inbound foreign keys, attributes and associations with
|
|
7
7
|
high rates of change, and business demands for well-tracked change history.
|
|
8
8
|
|
|
9
|
-
##AggregatedBehavior
|
|
9
|
+
## AggregatedBehavior
|
|
10
10
|
|
|
11
11
|
AggregatedBehavior implements a pattern in which an object's identity
|
|
12
12
|
is modeled apart from its attributes and outbound associations. This enables
|
|
@@ -26,7 +26,7 @@ to change. This is useful for objects with many inbound foreign keys and
|
|
|
26
26
|
high-traffic attributes/associations, such as statuses. Without this pattern
|
|
27
27
|
it would be difficult to avoid many StaleObjectErrors.
|
|
28
28
|
|
|
29
|
-
##TrackedBehavior
|
|
29
|
+
## TrackedBehavior
|
|
30
30
|
|
|
31
31
|
TrackedBehavior implements history tracking on the stack of objects
|
|
32
32
|
representing the identity object's attributes and outbound associations.
|
|
@@ -36,7 +36,7 @@ and will be saved as a new record with new attribute values and the
|
|
|
36
36
|
'is_current' column as 'true'. Thus, under the hood, new attributes
|
|
37
37
|
will supersede old attributes, leaving the old record as historical.
|
|
38
38
|
|
|
39
|
-
##Macros, Associations and Combination
|
|
39
|
+
## Macros, Associations and Combination
|
|
40
40
|
|
|
41
41
|
Despite the fact that Moribus may be used by models on its own,
|
|
42
42
|
its main purpose is to be used within associations, and in conjunction
|
|
@@ -105,6 +105,6 @@ rake spec
|
|
|
105
105
|
|
|
106
106
|
## Copyright
|
|
107
107
|
|
|
108
|
-
Copyright (c) 2014 HornsAndHooves.
|
|
108
|
+
Copyright (c) 2014-2018 HornsAndHooves.
|
|
109
109
|
|
|
110
110
|
Copyright (c) 2013 TMX Credit.
|
|
@@ -75,8 +75,20 @@ module Moribus
|
|
|
75
75
|
result[parent_key] = read_attribute(parent_key)
|
|
76
76
|
result
|
|
77
77
|
end
|
|
78
|
+
relation = klass.unscoped.where(criteria)
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
sql = <<-SQL
|
|
81
|
+
#{relation.select("COUNT(#{lock_column_name}) AS value, 0 sort_order").to_sql}
|
|
82
|
+
UNION
|
|
83
|
+
#{relation.select("MAX(#{lock_column_name}) AS value, 1 sort_order").to_sql}
|
|
84
|
+
ORDER BY sort_order
|
|
85
|
+
SQL
|
|
86
|
+
|
|
87
|
+
result = klass.connection.execute(sql)
|
|
88
|
+
current_count = result[0]["value"]
|
|
89
|
+
current_max = result[1]["value"]
|
|
90
|
+
|
|
91
|
+
lock_value = (current_count.to_s == "0" ? 0 : current_max.to_i + 1)
|
|
80
92
|
|
|
81
93
|
write_attribute(lock_column_name, lock_value)
|
|
82
94
|
end
|
data/lib/moribus/version.rb
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Moribus::AggregatedBehavior do
|
|
4
|
+
before do
|
|
5
|
+
class SpecSuffix < MoribusSpecModel(name: :string, description: :string)
|
|
6
|
+
acts_as_enumerated
|
|
7
|
+
|
|
8
|
+
self.enumeration_model_updates_permitted = true
|
|
9
|
+
create!(name: "none", description: "")
|
|
10
|
+
create!(name: "jr" , description: "Junior")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class SpecPersonName < MoribusSpecModel(first_name: :string,
|
|
14
|
+
last_name: :string,
|
|
15
|
+
spec_suffix_id: :integer
|
|
16
|
+
)
|
|
17
|
+
acts_as_aggregated
|
|
18
|
+
has_enumerated :spec_suffix, default: ""
|
|
19
|
+
|
|
20
|
+
validates_presence_of :first_name, :last_name
|
|
21
|
+
|
|
22
|
+
# custom writer that additionally strips first name
|
|
23
|
+
def first_name=(value)
|
|
24
|
+
self[:first_name] = value.strip
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class SpecCustomerFeature < MoribusSpecModel(feature_name: :string)
|
|
29
|
+
acts_as_aggregated cache_by: :feature_name
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
after do
|
|
34
|
+
MoribusSpecModel.cleanup!
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "Aggregated" do
|
|
38
|
+
context "definition" do
|
|
39
|
+
it "raises an error on an unknown option" do
|
|
40
|
+
expect{
|
|
41
|
+
Class.new(ActiveRecord::Base).class_eval do
|
|
42
|
+
acts_as_aggregated invalid_key: :error
|
|
43
|
+
end
|
|
44
|
+
}.to raise_error(ArgumentError)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "raises an error when including AggregatedCacheBehavior without AggregatedBehavior" do
|
|
48
|
+
expect{
|
|
49
|
+
Class.new(ActiveRecord::Base).class_eval do
|
|
50
|
+
include Moribus::AggregatedCacheBehavior
|
|
51
|
+
end
|
|
52
|
+
}.to raise_error(Moribus::AggregatedCacheBehavior::NotAggregatedError)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
before do
|
|
57
|
+
@existing = SpecPersonName.create! first_name: "John", last_name: "Smith"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "doesn't duplicate records" do
|
|
61
|
+
expect {
|
|
62
|
+
SpecPersonName.create first_name: " John ", last_name: "Smith"
|
|
63
|
+
}.not_to change(SpecPersonName, :count)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "looks up self and replaces id with existing on create" do
|
|
67
|
+
name = SpecPersonName.new first_name: "John", last_name: "Smith"
|
|
68
|
+
name.save
|
|
69
|
+
expect(name.id).to eq @existing.id
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "creates a new record if lookup fails" do
|
|
73
|
+
expect {
|
|
74
|
+
SpecPersonName.create first_name: "Alice", last_name: "Smith"
|
|
75
|
+
}.to change(SpecPersonName, :count).by(1)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "looks up self and replaces id with existing on update" do
|
|
79
|
+
name = SpecPersonName.create first_name: "Alice", last_name: "Smith"
|
|
80
|
+
name.update_attributes first_name: "John"
|
|
81
|
+
expect(name.id).to eq @existing.id
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "calls super in save if any aggregated behaviour non content columns wasn't changed" do
|
|
85
|
+
name = SpecPersonName.create first_name: "Alice", last_name: "Smith"
|
|
86
|
+
expect {
|
|
87
|
+
name.save
|
|
88
|
+
}.to change(SpecPersonName, :count).by(0)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "raises the expected error when 'save!' fails" do
|
|
92
|
+
name = SpecPersonName.create first_name: "Alice", last_name: "Smith"
|
|
93
|
+
name.last_name = nil
|
|
94
|
+
expect {
|
|
95
|
+
name.save!
|
|
96
|
+
}.to raise_error(ActiveRecord::RecordNotSaved)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "with caching" do
|
|
100
|
+
before do
|
|
101
|
+
@existing = SpecCustomerFeature.create(feature_name: "Pays")
|
|
102
|
+
SpecCustomerFeature.clear_cache
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "looks up the existing value and adds it to the cache" do
|
|
106
|
+
feature = SpecCustomerFeature.new feature_name: @existing.feature_name
|
|
107
|
+
|
|
108
|
+
expect{ feature.save }.
|
|
109
|
+
to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
|
110
|
+
|
|
111
|
+
expect(feature.id).to eq @existing.id
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "adds the freshly-created record to the cache" do
|
|
115
|
+
expect{ SpecCustomerFeature.create(feature_name: "Fraud") }.
|
|
116
|
+
to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "freezes the cached object" do
|
|
120
|
+
feature = SpecCustomerFeature.create(feature_name: "Cancelled")
|
|
121
|
+
expect(SpecCustomerFeature.aggregated_records_cache[feature.feature_name]).to be_frozen
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "caches the clone of the record, not the record itself" do
|
|
125
|
+
feature = SpecCustomerFeature.create(feature_name: "Returned")
|
|
126
|
+
expect(SpecCustomerFeature.aggregated_records_cache[feature.feature_name].object_id).
|
|
127
|
+
not_to eq feature.object_id
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "spec_helper"
|
|
2
2
|
|
|
3
3
|
describe Moribus::AliasAssociation do
|
|
4
4
|
before do
|
|
5
|
-
class SpecPost < MoribusSpecModel(:
|
|
6
|
-
belongs_to :spec_author , :
|
|
7
|
-
has_many :spec_comments , :
|
|
8
|
-
has_one :spec_post_info, :
|
|
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
9
|
|
|
10
10
|
alias_association :author , :spec_author
|
|
11
11
|
alias_association :comments , :spec_comments
|
|
12
12
|
alias_association :post_info, :spec_post_info
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
class SpecAuthor < MoribusSpecModel(:
|
|
15
|
+
class SpecAuthor < MoribusSpecModel(name: :string)
|
|
16
16
|
has_many :spec_posts
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
class SpecPostInfo < MoribusSpecModel(:
|
|
20
|
-
belongs_to :spec_post, :
|
|
19
|
+
class SpecPostInfo < MoribusSpecModel(spec_post_id: :integer, ip: :string)
|
|
20
|
+
belongs_to :spec_post, alias: :note
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
class SpecComment < MoribusSpecModel(:
|
|
23
|
+
class SpecComment < MoribusSpecModel(spec_post_id: :integer, body: :string)
|
|
24
24
|
belongs_to :spec_post
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -30,10 +30,10 @@ describe Moribus::AliasAssociation do
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
before do
|
|
33
|
-
author = SpecAuthor.create(:
|
|
34
|
-
@post
|
|
35
|
-
@post.spec_comments.create(:
|
|
36
|
-
@post.create_spec_post_info(:
|
|
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
37
|
end
|
|
38
38
|
|
|
39
39
|
describe "reflection aliasing" do
|
|
@@ -41,7 +41,7 @@ describe Moribus::AliasAssociation do
|
|
|
41
41
|
expect(SpecPost.reflect_on_association(:author)).not_to be_nil
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
it "
|
|
44
|
+
it "does not raise error when using aliased name in scopes" do
|
|
45
45
|
expect{
|
|
46
46
|
SpecPost.includes(:comments).first
|
|
47
47
|
}.not_to raise_error
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Moribus::Extensions::DelegateAssociated 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,
|
|
30
|
+
middle_name: :string,
|
|
31
|
+
last_name: :string,
|
|
32
|
+
spec_suffix_id: :integer
|
|
33
|
+
)
|
|
34
|
+
acts_as_aggregated non_content_columns: :middle_name
|
|
35
|
+
has_enumerated :spec_suffix, default: ""
|
|
36
|
+
|
|
37
|
+
validates_presence_of :first_name, :last_name
|
|
38
|
+
|
|
39
|
+
# custom writer that additionally strips first name
|
|
40
|
+
def first_name=(value)
|
|
41
|
+
self[:first_name] = value.strip
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class SpecCustomerInfo < MoribusSpecModel(
|
|
46
|
+
spec_customer_id: :integer!,
|
|
47
|
+
spec_person_name_id: :integer,
|
|
48
|
+
spec_status_id: :integer,
|
|
49
|
+
spec_type_id: :integer,
|
|
50
|
+
is_current: :boolean,
|
|
51
|
+
lock_version: :integer,
|
|
52
|
+
created_at: :datetime,
|
|
53
|
+
updated_at: :datetime,
|
|
54
|
+
previous_id: :integer
|
|
55
|
+
)
|
|
56
|
+
attr :custom_field
|
|
57
|
+
|
|
58
|
+
belongs_to :spec_customer, inverse_of: :spec_customer_info, touch: true
|
|
59
|
+
has_aggregated :spec_person_name
|
|
60
|
+
has_enumerated :spec_status
|
|
61
|
+
has_enumerated :spec_type
|
|
62
|
+
|
|
63
|
+
acts_as_tracked by: :spec_customer, preceding_key: :previous_id
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class SpecCustomer < MoribusSpecModel(spec_status_id: :integer)
|
|
67
|
+
has_one_current :spec_customer_info, inverse_of: :spec_customer
|
|
68
|
+
has_one_current :spec_customer_info_with_type, inverse_of: :spec_customer
|
|
69
|
+
has_enumerated :spec_status, default: "inactive"
|
|
70
|
+
|
|
71
|
+
delegate_associated :spec_person_name, :custom_field, :spec_type, to: :spec_customer_info
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
after do
|
|
76
|
+
MoribusSpecModel.cleanup!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
let!(:customer) do
|
|
80
|
+
SpecCustomer.create(
|
|
81
|
+
spec_customer_info_attributes: {
|
|
82
|
+
spec_person_name_attributes: { first_name: " John ", last_name: "Smith" }
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
let(:info) { customer.spec_customer_info }
|
|
88
|
+
|
|
89
|
+
describe "Delegations" do
|
|
90
|
+
it "has delegated column information" do
|
|
91
|
+
expect(customer.column_for_attribute(:first_name)).not_to be_nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "does not delegate special methods" do
|
|
95
|
+
expect(customer).not_to respond_to(:reset_first_name)
|
|
96
|
+
expect(customer).not_to respond_to(:first_name_was)
|
|
97
|
+
expect(customer).not_to respond_to(:first_name_before_type_cast)
|
|
98
|
+
expect(customer).not_to respond_to(:first_name_will_change!)
|
|
99
|
+
expect(customer).not_to respond_to(:first_name_changed?)
|
|
100
|
+
expect(customer).not_to respond_to(:lock_version)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "delegates methods to aggregated parts" do
|
|
104
|
+
expect(info).to respond_to(:first_name)
|
|
105
|
+
expect(info).to respond_to(:first_name=)
|
|
106
|
+
expect(info).to respond_to(:spec_suffix)
|
|
107
|
+
expect(info.last_name).to eq "Smith"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "delegates methods to representation" do
|
|
111
|
+
expect(customer).to respond_to(:first_name)
|
|
112
|
+
expect(customer).to respond_to(:first_name=)
|
|
113
|
+
expect(customer).to respond_to(:spec_suffix)
|
|
114
|
+
expect(customer.last_name).to eq "Smith"
|
|
115
|
+
expect(customer).to respond_to(:custom_field)
|
|
116
|
+
expect(customer).to respond_to(:custom_field=)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "properly delegates enumerated attributes" do
|
|
120
|
+
expect(customer).to respond_to(:spec_type)
|
|
121
|
+
expect(customer).to respond_to(:spec_type=)
|
|
122
|
+
customer.spec_type = :important
|
|
123
|
+
expect(customer.spec_type === :important).to eq true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "raises NoMethodError if unknown method is received" do
|
|
127
|
+
expect{ customer.impossibru }.to raise_error(NoMethodError)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "#column_for_attribute" do
|
|
132
|
+
it "returns class db column if it exists" do
|
|
133
|
+
expect(customer.column_for_attribute(:id)).not_to be_nil
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Moribus::Extensions::HasCurrentExtension do
|
|
4
|
+
before do
|
|
5
|
+
class SpecCustomerInfo < MoribusSpecModel(
|
|
6
|
+
spec_customer_id: :integer!,
|
|
7
|
+
is_current: :boolean,
|
|
8
|
+
lock_version: :integer,
|
|
9
|
+
created_at: :datetime,
|
|
10
|
+
updated_at: :datetime,
|
|
11
|
+
previous_id: :integer
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
belongs_to :spec_customer, inverse_of: :spec_customer_info, touch: true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class SpecCustomer < MoribusSpecModel(spec_status_id: :integer)
|
|
18
|
+
has_one_current :spec_customer_info, inverse_of: :spec_customer, dependent: :destroy
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after do
|
|
23
|
+
MoribusSpecModel.cleanup!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
let(:customer) { SpecCustomer.create }
|
|
27
|
+
let!(:info) { SpecCustomerInfo.create(spec_customer: customer, is_current: true) }
|
|
28
|
+
|
|
29
|
+
describe ".remove_target!" do
|
|
30
|
+
before do
|
|
31
|
+
allow(customer.spec_customer_info).to receive(:new_record?).and_return(true)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "sets 'is_current' flag of overridden record to false for new record" do
|
|
35
|
+
old_info = customer.spec_customer_info
|
|
36
|
+
customer.spec_customer_info = SpecCustomerInfo.new
|
|
37
|
+
expect(old_info.is_current).to be_falsey
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/spec/moribus/macros_spec.rb
CHANGED
|
@@ -1,7 +1,101 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "spec_helper"
|
|
2
2
|
|
|
3
3
|
# Some of the Moribus::Macros methods are tested in
|
|
4
4
|
# spec/lib/moribus_spec.rb.
|
|
5
5
|
describe Moribus::Macros do
|
|
6
|
+
before do
|
|
7
|
+
class SpecCustomer < MoribusSpecModel(spec_status_id: :integer)
|
|
8
|
+
has_one_current :spec_customer_info,
|
|
9
|
+
-> { where(spec_customer_id: 3) }, inverse_of: :spec_customer
|
|
10
|
+
end
|
|
6
11
|
|
|
12
|
+
class SpecCustomerInfo < MoribusSpecModel(
|
|
13
|
+
spec_customer_id: :integer!,
|
|
14
|
+
is_current: :boolean,
|
|
15
|
+
lock_version: :integer,
|
|
16
|
+
created_at: :datetime,
|
|
17
|
+
updated_at: :datetime,
|
|
18
|
+
previous_id: :integer
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
belongs_to :spec_customer, inverse_of: :spec_customer_info, touch: true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class SpecCustomerOrder < MoribusSpecModel(:spec_status_id => :integer)
|
|
25
|
+
has_one_current :spec_customer_info_order,
|
|
26
|
+
-> { order(:created_at) }, :inverse_of => :spec_customer_order
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class SpecCustomerInfoOrder < MoribusSpecModel(
|
|
30
|
+
spec_customer_order_id: :integer!,
|
|
31
|
+
is_current: :boolean,
|
|
32
|
+
lock_version: :integer,
|
|
33
|
+
created_at: :datetime,
|
|
34
|
+
updated_at: :datetime,
|
|
35
|
+
previous_id: :integer
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
belongs_to :spec_customer_order, inverse_of: :spec_customer_info_order, touch: true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
after do
|
|
43
|
+
MoribusSpecModel.cleanup!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe ".has_one_current" do
|
|
47
|
+
let!(:customer) { SpecCustomer.create(id: 3, spec_status_id: 2) }
|
|
48
|
+
let!(:customer_order) { SpecCustomerOrder.create(id: 9, spec_status_id: 1) }
|
|
49
|
+
|
|
50
|
+
let!(:info1) do
|
|
51
|
+
SpecCustomerInfo.create(
|
|
52
|
+
spec_customer_id: 1,
|
|
53
|
+
is_current: true,
|
|
54
|
+
created_at: 5.days.ago,
|
|
55
|
+
updated_at: 5.days.ago
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
let!(:info2) do
|
|
60
|
+
SpecCustomerInfo.create(
|
|
61
|
+
spec_customer_id: 3,
|
|
62
|
+
is_current: true,
|
|
63
|
+
created_at: 5.days.ago,
|
|
64
|
+
updated_at: 5.days.ago
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
let!(:info_order1) do
|
|
69
|
+
SpecCustomerInfoOrder.create(
|
|
70
|
+
spec_customer_order_id: 9,
|
|
71
|
+
is_current: true,
|
|
72
|
+
created_at: 5.days.ago,
|
|
73
|
+
updated_at: 5.days.ago
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
let!(:info_order2) do
|
|
78
|
+
SpecCustomerInfoOrder.create(
|
|
79
|
+
spec_customer_order_id: 11,
|
|
80
|
+
is_current: true,
|
|
81
|
+
created_at: 5.days.ago,
|
|
82
|
+
updated_at: 5.days.ago
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "merges has_one_current scope with has_one if given" do
|
|
87
|
+
expect(customer.spec_customer_info ).to eq(info2)
|
|
88
|
+
expect(customer_order.spec_customer_info_order).to eq(info_order1)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe ".normalize_reflection" do
|
|
93
|
+
subject do
|
|
94
|
+
SpecCustomer.send(:normalize_reflection, { spec_customer_info: :key_value }, :spec_customer_info)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "returns reflection with symbol key for rails 4.1" do
|
|
98
|
+
is_expected.to eq(:key_value)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
7
101
|
end
|
data/spec/moribus_spec.rb
CHANGED
|
@@ -2,36 +2,37 @@ require "spec_helper"
|
|
|
2
2
|
|
|
3
3
|
describe Moribus do
|
|
4
4
|
before do
|
|
5
|
-
class SpecStatus < MoribusSpecModel(:
|
|
5
|
+
class SpecStatus < MoribusSpecModel(name: :string, description: :string)
|
|
6
6
|
acts_as_enumerated
|
|
7
7
|
|
|
8
8
|
self.enumeration_model_updates_permitted = true
|
|
9
|
-
create!(:
|
|
10
|
-
create!(:
|
|
9
|
+
create!(name: "inactive", description: "Inactive")
|
|
10
|
+
create!(name: "active" , description: "Active")
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
class SpecType < MoribusSpecModel(:
|
|
13
|
+
class SpecType < MoribusSpecModel(name: :string, description: :string)
|
|
14
14
|
acts_as_enumerated
|
|
15
15
|
|
|
16
16
|
self.enumeration_model_updates_permitted = true
|
|
17
|
-
create!(:
|
|
18
|
-
create!(:
|
|
17
|
+
create!(name: "important" , description: "Important")
|
|
18
|
+
create!(name: "unimportant", description: "Unimportant")
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
class SpecSuffix < MoribusSpecModel(:
|
|
21
|
+
class SpecSuffix < MoribusSpecModel(name: :string, description: :string)
|
|
22
22
|
acts_as_enumerated
|
|
23
23
|
|
|
24
24
|
self.enumeration_model_updates_permitted = true
|
|
25
|
-
create!(:
|
|
26
|
-
create!(:
|
|
25
|
+
create!(name: "none", description: "")
|
|
26
|
+
create!(name: "jr" , description: "Junior")
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
class SpecPersonName < MoribusSpecModel(:
|
|
30
|
-
:
|
|
31
|
-
:
|
|
29
|
+
class SpecPersonName < MoribusSpecModel(first_name: :string,
|
|
30
|
+
middle_name: :string,
|
|
31
|
+
last_name: :string,
|
|
32
|
+
spec_suffix_id: :integer
|
|
32
33
|
)
|
|
33
|
-
acts_as_aggregated
|
|
34
|
-
has_enumerated :spec_suffix, :
|
|
34
|
+
acts_as_aggregated non_content_columns: :middle_name
|
|
35
|
+
has_enumerated :spec_suffix, default: ""
|
|
35
36
|
|
|
36
37
|
validates_presence_of :first_name, :last_name
|
|
37
38
|
|
|
@@ -41,63 +42,63 @@ describe Moribus do
|
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
class SpecCustomerFeature < MoribusSpecModel(:
|
|
45
|
-
acts_as_aggregated :
|
|
45
|
+
class SpecCustomerFeature < MoribusSpecModel(feature_name: :string)
|
|
46
|
+
acts_as_aggregated cache_by: :feature_name
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
class SpecCustomerInfo < MoribusSpecModel(
|
|
49
|
-
:
|
|
50
|
-
:
|
|
51
|
-
:
|
|
52
|
-
:
|
|
53
|
-
:
|
|
54
|
-
:
|
|
55
|
-
:
|
|
56
|
-
:
|
|
57
|
-
:
|
|
50
|
+
spec_customer_id: :integer!,
|
|
51
|
+
spec_person_name_id: :integer,
|
|
52
|
+
spec_status_id: :integer,
|
|
53
|
+
spec_type_id: :integer,
|
|
54
|
+
is_current: :boolean,
|
|
55
|
+
lock_version: :integer,
|
|
56
|
+
created_at: :datetime,
|
|
57
|
+
updated_at: :datetime,
|
|
58
|
+
previous_id: :integer
|
|
58
59
|
)
|
|
59
60
|
attr :custom_field
|
|
60
61
|
|
|
61
|
-
belongs_to :spec_customer, :
|
|
62
|
+
belongs_to :spec_customer, inverse_of: :spec_customer_info, touch: true
|
|
62
63
|
has_aggregated :spec_person_name
|
|
63
64
|
has_enumerated :spec_status
|
|
64
65
|
has_enumerated :spec_type
|
|
65
66
|
|
|
66
|
-
acts_as_tracked :
|
|
67
|
+
acts_as_tracked by: :spec_customer, preceding_key: :previous_id
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
class SpecCustomerInfoWithType < MoribusSpecModel(
|
|
70
|
-
:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
spec_customer_id: :integer!,
|
|
72
|
+
spec_status_id: :integer,
|
|
73
|
+
spec_type_id: :integer,
|
|
74
|
+
is_current: :boolean,
|
|
75
|
+
lock_version: :integer,
|
|
76
|
+
created_at: :datetime,
|
|
77
|
+
updated_at: :datetime
|
|
77
78
|
)
|
|
78
79
|
attr :custom_field
|
|
79
80
|
|
|
80
|
-
belongs_to :spec_customer, :
|
|
81
|
+
belongs_to :spec_customer, inverse_of: :spec_customer_info_with_type, touch: true
|
|
81
82
|
has_enumerated :spec_status
|
|
82
83
|
has_enumerated :spec_type
|
|
83
84
|
|
|
84
85
|
acts_as_tracked :by => [:spec_customer, :spec_type]
|
|
85
86
|
end
|
|
86
87
|
|
|
87
|
-
class SpecCustomer < MoribusSpecModel(:
|
|
88
|
-
has_one_current :spec_customer_info, :
|
|
89
|
-
has_one_current :spec_customer_info_with_type, :
|
|
90
|
-
has_enumerated :spec_status, :
|
|
88
|
+
class SpecCustomer < MoribusSpecModel(spec_status_id: :integer)
|
|
89
|
+
has_one_current :spec_customer_info, inverse_of: :spec_customer
|
|
90
|
+
has_one_current :spec_customer_info_with_type, inverse_of: :spec_customer
|
|
91
|
+
has_enumerated :spec_status, default: "inactive"
|
|
91
92
|
|
|
92
|
-
delegate_associated :spec_person_name, :custom_field, :spec_type, :
|
|
93
|
+
delegate_associated :spec_person_name, :custom_field, :spec_type, to: :spec_customer_info
|
|
93
94
|
end
|
|
94
95
|
|
|
95
|
-
class SpecCustomerEmail < MoribusSpecModel(:
|
|
96
|
-
:
|
|
97
|
-
:
|
|
98
|
-
:
|
|
96
|
+
class SpecCustomerEmail < MoribusSpecModel(spec_customer_id: :integer,
|
|
97
|
+
email: :string,
|
|
98
|
+
is_current: :boolean,
|
|
99
|
+
status: :string
|
|
99
100
|
)
|
|
100
|
-
connection.add_index table_name, [:email, :is_current], :
|
|
101
|
+
connection.add_index table_name, [:email, :is_current], unique: true
|
|
101
102
|
|
|
102
103
|
belongs_to :spec_customer
|
|
103
104
|
|
|
@@ -112,22 +113,22 @@ describe Moribus do
|
|
|
112
113
|
describe "common behavior" do
|
|
113
114
|
before do
|
|
114
115
|
@info = SpecCustomerInfo.create(
|
|
115
|
-
:
|
|
116
|
-
:
|
|
117
|
-
:
|
|
118
|
-
:
|
|
119
|
-
:
|
|
116
|
+
spec_customer_id: 1,
|
|
117
|
+
spec_person_name_id: 1,
|
|
118
|
+
is_current: true,
|
|
119
|
+
created_at: 5.days.ago,
|
|
120
|
+
updated_at: 5.days.ago
|
|
120
121
|
)
|
|
121
122
|
end
|
|
122
123
|
|
|
123
|
-
it "
|
|
124
|
+
it "reverts changes if exception is raised" do
|
|
124
125
|
old_id = @info.id
|
|
125
126
|
old_updated_at = @info.updated_at
|
|
126
127
|
old_created_at = @info.created_at
|
|
127
128
|
|
|
128
129
|
suppress(Exception) do
|
|
129
130
|
expect {
|
|
130
|
-
@info.update_attributes :
|
|
131
|
+
@info.update_attributes spec_customer_id: nil, spec_person_name_id: 2
|
|
131
132
|
}.not_to change(SpecCustomerInfo, :count)
|
|
132
133
|
end
|
|
133
134
|
|
|
@@ -138,130 +139,46 @@ describe Moribus do
|
|
|
138
139
|
|
|
139
140
|
expect(@info.changed_attributes[:updated_at]).to eq(nil)
|
|
140
141
|
expect(@info.changed_attributes[:created_at]).to eq(nil)
|
|
141
|
-
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
describe "Aggregated" do
|
|
146
|
-
context "definition" do
|
|
147
|
-
it "should raise an error on an unknown option" do
|
|
148
|
-
expect{
|
|
149
|
-
Class.new(ActiveRecord::Base).class_eval do
|
|
150
|
-
acts_as_aggregated :invalid_key => :error
|
|
151
|
-
end
|
|
152
|
-
}.to raise_error(ArgumentError)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
it "should raise an error when including AggregatedCacheBehavior without AggregatedBehavior" do
|
|
156
|
-
expect{
|
|
157
|
-
Class.new(ActiveRecord::Base).class_eval do
|
|
158
|
-
include Moribus::AggregatedCacheBehavior
|
|
159
|
-
end
|
|
160
|
-
}.to raise_error(Moribus::AggregatedCacheBehavior::NotAggregatedError)
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
before do
|
|
165
|
-
@existing = SpecPersonName.create! :first_name => "John", :last_name => "Smith"
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it "doesn't duplicate records" do
|
|
169
|
-
expect {
|
|
170
|
-
SpecPersonName.create :first_name => " John ", :last_name => "Smith"
|
|
171
|
-
}.not_to change(SpecPersonName, :count)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it "looks up self and replaces id with existing on create" do
|
|
175
|
-
name = SpecPersonName.new :first_name => "John", :last_name => "Smith"
|
|
176
|
-
name.save
|
|
177
|
-
expect(name.id).to eq @existing.id
|
|
178
142
|
end
|
|
179
143
|
|
|
180
|
-
it "
|
|
181
|
-
expect
|
|
182
|
-
|
|
183
|
-
}.to change(SpecPersonName, :count).by(1)
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
it "looks up self and replaces id with existing on update" do
|
|
187
|
-
name = SpecPersonName.create :first_name => "Alice", :last_name => "Smith"
|
|
188
|
-
name.update_attributes :first_name => "John"
|
|
189
|
-
expect(name.id).to eq @existing.id
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
it "raises the expected error when 'save!' fails" do
|
|
193
|
-
name = SpecPersonName.create :first_name => "Alice", :last_name => "Smith"
|
|
194
|
-
name.last_name = nil
|
|
195
|
-
expect {
|
|
196
|
-
name.save!
|
|
197
|
-
}.to raise_error(ActiveRecord::RecordNotSaved)
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
context "with caching" do
|
|
201
|
-
before do
|
|
202
|
-
@existing = SpecCustomerFeature.create(:feature_name => "Pays")
|
|
203
|
-
SpecCustomerFeature.clear_cache
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it "looks up the existing value and adds it to the cache" do
|
|
207
|
-
feature = SpecCustomerFeature.new :feature_name => @existing.feature_name
|
|
208
|
-
|
|
209
|
-
expect{ feature.save }.
|
|
210
|
-
to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
|
211
|
-
|
|
212
|
-
expect(feature.id).to eq @existing.id
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
it "adds the freshly-created record to the cache" do
|
|
216
|
-
expect{ SpecCustomerFeature.create(:feature_name => "Fraud") }.
|
|
217
|
-
to change(SpecCustomerFeature.aggregated_records_cache, :length).by(1)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
it "freezes the cached object" do
|
|
221
|
-
feature = SpecCustomerFeature.create(:feature_name => "Cancelled")
|
|
222
|
-
expect(SpecCustomerFeature.aggregated_records_cache[feature.feature_name]).to be_frozen
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
it "caches the clone of the record, not the record itself" do
|
|
226
|
-
feature = SpecCustomerFeature.create(:feature_name => "Returned")
|
|
227
|
-
expect(SpecCustomerFeature.aggregated_records_cache[feature.feature_name].object_id).
|
|
228
|
-
not_to eq feature.object_id
|
|
229
|
-
end
|
|
144
|
+
it "has module included class check methods" do
|
|
145
|
+
expect(SpecCustomerFeature.acts_as_aggregated?).to be_truthy
|
|
146
|
+
expect(SpecCustomerEmail.acts_as_tracked?).to be_truthy
|
|
230
147
|
end
|
|
231
148
|
end
|
|
232
149
|
|
|
233
150
|
describe "Tracked" do
|
|
234
151
|
before do
|
|
235
152
|
@customer = SpecCustomer.create
|
|
236
|
-
@info = @customer.create_spec_customer_info :
|
|
153
|
+
@info = @customer.create_spec_customer_info spec_person_name_id: 1
|
|
237
154
|
end
|
|
238
155
|
|
|
239
156
|
it "creates a new current record if updated" do
|
|
240
157
|
expect {
|
|
241
|
-
@info.update_attributes(:
|
|
158
|
+
@info.update_attributes(spec_person_name_id: 2)
|
|
242
159
|
}.to change(SpecCustomerInfo, :count).by(1)
|
|
243
160
|
end
|
|
244
161
|
|
|
245
162
|
it "replaces itself with new id" do
|
|
246
163
|
old_id = @info.id
|
|
247
|
-
@info.update_attributes(:
|
|
164
|
+
@info.update_attributes(spec_person_name_id: 2)
|
|
248
165
|
expect(@info.id).not_to eq old_id
|
|
249
166
|
end
|
|
250
167
|
|
|
251
168
|
it "sets is_current record to false for superseded record" do
|
|
252
169
|
old_id = @info.id
|
|
253
|
-
@info.update_attributes(:
|
|
170
|
+
@info.update_attributes(spec_person_name_id: 2)
|
|
254
171
|
expect(SpecCustomerInfo.find(old_id).is_current).to eq false
|
|
255
172
|
end
|
|
256
173
|
|
|
257
174
|
it "sets previous_id to the id of the previous record" do
|
|
258
175
|
old_id = @info.id
|
|
259
|
-
@info.update_attributes(:
|
|
176
|
+
@info.update_attributes(spec_person_name_id: 2)
|
|
260
177
|
expect(@info.previous_id).to eq old_id
|
|
261
178
|
end
|
|
262
179
|
|
|
263
180
|
it "changes is_current to false for previous one when assigning a new current record" do
|
|
264
|
-
new_info = SpecCustomerInfo.new :
|
|
181
|
+
new_info = SpecCustomerInfo.new spec_person_name_id: 2, is_current: true
|
|
265
182
|
@customer.spec_customer_info = new_info
|
|
266
183
|
expect(new_info.spec_customer_id).to eq @customer.id
|
|
267
184
|
@info.reload
|
|
@@ -269,12 +186,12 @@ describe Moribus do
|
|
|
269
186
|
end
|
|
270
187
|
|
|
271
188
|
it "does not crash on superseding with 'is_current' conditional constraint" do
|
|
272
|
-
email = SpecCustomerEmail.create( :
|
|
273
|
-
:
|
|
274
|
-
:
|
|
275
|
-
:
|
|
189
|
+
email = SpecCustomerEmail.create( spec_customer: @customer,
|
|
190
|
+
email: "foo@bar.com",
|
|
191
|
+
status: "unverified",
|
|
192
|
+
is_current: true
|
|
276
193
|
)
|
|
277
|
-
expect{ email.update_attributes(:
|
|
194
|
+
expect{ email.update_attributes(status: "verified") }.not_to raise_error
|
|
278
195
|
end
|
|
279
196
|
|
|
280
197
|
describe "updated_at and created_at" do
|
|
@@ -285,7 +202,7 @@ describe Moribus do
|
|
|
285
202
|
after { Timecop.return }
|
|
286
203
|
|
|
287
204
|
it "is updated on change" do
|
|
288
|
-
info = @customer.create_spec_customer_info :
|
|
205
|
+
info = @customer.create_spec_customer_info spec_person_name_id: 1
|
|
289
206
|
expect(info.updated_at).to eq first_time
|
|
290
207
|
expect(info.created_at).to eq first_time
|
|
291
208
|
|
|
@@ -304,9 +221,9 @@ describe Moribus do
|
|
|
304
221
|
end
|
|
305
222
|
|
|
306
223
|
it "raises a stale object error" do
|
|
307
|
-
@info1.update_attributes(:
|
|
224
|
+
@info1.update_attributes(spec_person_name_id: 3)
|
|
308
225
|
|
|
309
|
-
expect{ @info2.update_attributes(:
|
|
226
|
+
expect{ @info2.update_attributes(spec_person_name_id: 4) }.
|
|
310
227
|
to raise_error(ActiveRecord::StaleObjectError,
|
|
311
228
|
"Attempted to update_current (version #{@info2.lock_version}) a stale object: SpecCustomerInfo")
|
|
312
229
|
end
|
|
@@ -315,17 +232,30 @@ describe Moribus do
|
|
|
315
232
|
spec_customer_info = @customer.spec_customer_info
|
|
316
233
|
|
|
317
234
|
expect {
|
|
318
|
-
spec_customer_info.update_attributes(:
|
|
235
|
+
spec_customer_info.update_attributes(spec_person_name_id: 3)
|
|
319
236
|
}.to change { spec_customer_info.lock_version }.from(0).to(1)
|
|
320
237
|
|
|
321
238
|
expect {
|
|
322
|
-
spec_customer_info.update_attributes(:
|
|
239
|
+
spec_customer_info.update_attributes(spec_person_name_id: 4)
|
|
323
240
|
}.to change { spec_customer_info.lock_version }.from(1).to(2)
|
|
324
241
|
end
|
|
325
242
|
|
|
243
|
+
it "does not fail to update if a lock version growth is for any reason not monotonic" do
|
|
244
|
+
spec_customer_info = @customer.spec_customer_info
|
|
245
|
+
|
|
246
|
+
spec_customer_info.update_attributes(spec_person_name_id: 3)
|
|
247
|
+
spec_customer_info.update_attributes(spec_person_name_id: 4)
|
|
248
|
+
|
|
249
|
+
SpecCustomerInfo.where(spec_customer_id: @customer.id, lock_version: 1).delete_all
|
|
250
|
+
|
|
251
|
+
expect {
|
|
252
|
+
spec_customer_info.update_attributes(spec_person_name_id: 5)
|
|
253
|
+
}.to change { spec_customer_info.lock_version }.from(2).to(3)
|
|
254
|
+
end
|
|
255
|
+
|
|
326
256
|
it "does not fail if no locking_column is present" do
|
|
327
|
-
email = SpecCustomerEmail.create(:
|
|
328
|
-
expect{ email.update_attributes(:
|
|
257
|
+
email = SpecCustomerEmail.create(spec_customer_id: 1, email: "foo@bar.com")
|
|
258
|
+
expect{ email.update_attributes(email: "foo2@bar.com") }.not_to raise_error
|
|
329
259
|
end
|
|
330
260
|
|
|
331
261
|
it "updates lock_version column based on parent relation" do
|
|
@@ -333,29 +263,29 @@ describe Moribus do
|
|
|
333
263
|
spec_customer_info = @customer.spec_customer_info
|
|
334
264
|
|
|
335
265
|
expect {
|
|
336
|
-
spec_customer_info.update_attributes(:
|
|
266
|
+
spec_customer_info.update_attributes(spec_person_name_id: 3)
|
|
337
267
|
}.to change { spec_customer_info.lock_version }.from(0).to(1)
|
|
338
268
|
|
|
339
269
|
expect {
|
|
340
|
-
spec_customer_info.update_attributes(:
|
|
270
|
+
spec_customer_info.update_attributes(spec_customer: @other_customer)
|
|
341
271
|
}.to change { spec_customer_info.lock_version }.from(1).to(0)
|
|
342
272
|
end
|
|
343
273
|
|
|
344
274
|
it "updates lock_version column base on relation list from 'by' option" do
|
|
345
275
|
info_with_type =
|
|
346
|
-
@customer.reload.create_spec_customer_info_with_type(:
|
|
276
|
+
@customer.reload.create_spec_customer_info_with_type(spec_type: :important)
|
|
347
277
|
|
|
348
278
|
expect( info_with_type.lock_version ).to eq 0
|
|
349
279
|
|
|
350
280
|
other_info_with_type =
|
|
351
|
-
@customer.reload.create_spec_customer_info_with_type(:
|
|
281
|
+
@customer.reload.create_spec_customer_info_with_type(spec_type: :unimportant)
|
|
352
282
|
|
|
353
283
|
expect( other_info_with_type.lock_version ).to eq 0
|
|
354
284
|
|
|
355
|
-
info_with_type.update_attributes(:
|
|
285
|
+
info_with_type.update_attributes(spec_status: :active)
|
|
356
286
|
expect( info_with_type.lock_version ).to eq 1
|
|
357
287
|
|
|
358
|
-
info_with_type.update_attributes(:
|
|
288
|
+
info_with_type.update_attributes(spec_status: :inactive)
|
|
359
289
|
expect( info_with_type.lock_version ).to eq 2
|
|
360
290
|
|
|
361
291
|
expect( other_info_with_type.lock_version ).to eq 0
|
|
@@ -365,7 +295,7 @@ describe Moribus do
|
|
|
365
295
|
describe "with Aggregated" do
|
|
366
296
|
before do
|
|
367
297
|
@info.spec_person_name =
|
|
368
|
-
SpecPersonName.create(:
|
|
298
|
+
SpecPersonName.create(first_name: "John", last_name: "Smith")
|
|
369
299
|
@info.save
|
|
370
300
|
@info.reload
|
|
371
301
|
end
|
|
@@ -374,59 +304,12 @@ describe Moribus do
|
|
|
374
304
|
old_id = @info.id
|
|
375
305
|
@customer.spec_customer_info.spec_person_name.first_name = "Alice"
|
|
376
306
|
expect{ @customer.save }.to change(@info, :spec_person_name_id)
|
|
377
|
-
expect(@info.id).not_to eq old_id
|
|
378
|
-
expect(@info.is_current).to eq true
|
|
379
|
-
expect(SpecCustomerInfo.find(old_id).is_current).to eq false
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
describe "Delegations" do
|
|
385
|
-
before do
|
|
386
|
-
@customer = SpecCustomer.create(
|
|
387
|
-
:spec_customer_info_attributes => {
|
|
388
|
-
:spec_person_name_attributes => {:first_name => " John ", :last_name => "Smith"} } )
|
|
389
|
-
@info = @customer.spec_customer_info
|
|
390
|
-
end
|
|
391
307
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
it "does not delegate special methods" do
|
|
397
|
-
expect(@customer).not_to respond_to(:reset_first_name)
|
|
398
|
-
expect(@customer).not_to respond_to(:first_name_was)
|
|
399
|
-
expect(@customer).not_to respond_to(:first_name_before_type_cast)
|
|
400
|
-
expect(@customer).not_to respond_to(:first_name_will_change!)
|
|
401
|
-
expect(@customer).not_to respond_to(:first_name_changed?)
|
|
402
|
-
expect(@customer).not_to respond_to(:lock_version)
|
|
403
|
-
end
|
|
308
|
+
expect(@info.id ).not_to eq old_id
|
|
309
|
+
expect(@info.is_current).to eq true
|
|
404
310
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
expect(@info).to respond_to(:first_name=)
|
|
408
|
-
expect(@info).to respond_to(:spec_suffix)
|
|
409
|
-
expect(@info.last_name).to eq "Smith"
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
it "delegates methods to representation" do
|
|
413
|
-
expect(@customer).to respond_to(:first_name)
|
|
414
|
-
expect(@customer).to respond_to(:first_name=)
|
|
415
|
-
expect(@customer).to respond_to(:spec_suffix)
|
|
416
|
-
expect(@customer.last_name).to eq "Smith"
|
|
417
|
-
expect(@customer).to respond_to(:custom_field)
|
|
418
|
-
expect(@customer).to respond_to(:custom_field=)
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
it "properly delegates enumerated attributes" do
|
|
422
|
-
expect(@customer).to respond_to(:spec_type)
|
|
423
|
-
expect(@customer).to respond_to(:spec_type=)
|
|
424
|
-
@customer.spec_type = :important
|
|
425
|
-
expect(@customer.spec_type === :important).to eq true
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
it "raises NoMethodError if unknown method is received" do
|
|
429
|
-
expect{ @customer.impossibru }.to raise_error(NoMethodError)
|
|
311
|
+
expect(SpecCustomerInfo.find(old_id).is_current).to eq false
|
|
312
|
+
end
|
|
430
313
|
end
|
|
431
314
|
end
|
|
432
315
|
end
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
module MoribusSpecModel
|
|
3
3
|
def self.cleanup!
|
|
4
4
|
ObjectSpace.each_object(MoribusSpecModel) do |klass|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
unless klass.name.nil?
|
|
6
|
+
if Object.const_defined? klass.name.to_sym
|
|
7
|
+
conn, table_name = klass.connection, klass.table_name
|
|
8
|
+
conn.drop_table(table_name) if conn.tables.include?(table_name)
|
|
9
|
+
Object.send :remove_const, klass.name.to_sym
|
|
10
|
+
end
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
ActiveSupport::Dependencies::Reference.clear!
|
data/tags
CHANGED
|
@@ -27,12 +27,16 @@ HasAggregatedExtension lib/moribus/extensions/has_aggregated_extension.rb 5;" m
|
|
|
27
27
|
HasCurrentExtension lib/moribus/extensions/has_current_extension.rb 5;" m line:5 class:Moribus.Extensions
|
|
28
28
|
Helper lib/moribus/extensions/has_aggregated_extension.rb 18;" c line:18 class:Moribus.Extensions.HasAggregatedExtension
|
|
29
29
|
K.D coverage/assets/0.10.0/application.js 17;" f line:17
|
|
30
|
+
K.D coverage/assets/0.10.2/application.js 17;" f line:17
|
|
30
31
|
K.D coverage/assets/0.8.0/application.js 17;" f line:17
|
|
31
32
|
K.E coverage/assets/0.10.0/application.js 17;" f line:17
|
|
33
|
+
K.E coverage/assets/0.10.2/application.js 17;" f line:17
|
|
32
34
|
K.E coverage/assets/0.8.0/application.js 17;" f line:17
|
|
33
35
|
K.l coverage/assets/0.10.0/application.js 17;" f line:17
|
|
36
|
+
K.l coverage/assets/0.10.2/application.js 17;" f line:17
|
|
34
37
|
K.l coverage/assets/0.8.0/application.js 17;" f line:17
|
|
35
38
|
K.m coverage/assets/0.10.0/application.js 17;" f line:17
|
|
39
|
+
K.m coverage/assets/0.10.2/application.js 17;" f line:17
|
|
36
40
|
K.m coverage/assets/0.8.0/application.js 17;" f line:17
|
|
37
41
|
Macros lib/moribus/macros.rb 4;" m line:4 class:Moribus
|
|
38
42
|
Moribus lib/moribus.rb 6;" m line:6
|
|
@@ -46,23 +50,39 @@ Moribus lib/moribus/extensions/has_current_extension.rb 1;" m line:1
|
|
|
46
50
|
Moribus lib/moribus/macros.rb 1;" m line:1
|
|
47
51
|
Moribus lib/moribus/tracked_behavior.rb 1;" m line:1
|
|
48
52
|
Moribus lib/moribus/version.rb 1;" m line:1
|
|
49
|
-
MoribusSpecModel spec/support/moribus_spec_model.rb
|
|
53
|
+
MoribusSpecModel spec/support/moribus_spec_model.rb 28;" f line:28
|
|
50
54
|
MoribusSpecModel spec/support/moribus_spec_model.rb 2;" m line:2
|
|
51
55
|
SpecAuthor spec/moribus/alias_association_spec.rb 15;" c line:15
|
|
52
56
|
SpecComment spec/moribus/alias_association_spec.rb 23;" c line:23
|
|
53
|
-
SpecCustomer spec/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
SpecCustomer spec/moribus/extensions/delegate_associated_spec.rb 66;" c line:66
|
|
58
|
+
SpecCustomer spec/moribus/extensions/has_current_extension_spec.rb 17;" c line:17
|
|
59
|
+
SpecCustomer spec/moribus/macros_spec.rb 7;" c line:7
|
|
60
|
+
SpecCustomer spec/moribus_spec.rb 88;" c line:88
|
|
61
|
+
SpecCustomerEmail spec/moribus_spec.rb 96;" c line:96
|
|
62
|
+
SpecCustomerFeature spec/moribus/aggregated_behavior_spec.rb 28;" c line:28
|
|
63
|
+
SpecCustomerFeature spec/moribus_spec.rb 45;" c line:45
|
|
64
|
+
SpecCustomerInfo spec/moribus/extensions/delegate_associated_spec.rb 45;" c line:45
|
|
65
|
+
SpecCustomerInfo spec/moribus/extensions/has_current_extension_spec.rb 5;" c line:5
|
|
66
|
+
SpecCustomerInfo spec/moribus/macros_spec.rb 12;" c line:12
|
|
67
|
+
SpecCustomerInfo spec/moribus_spec.rb 49;" c line:49
|
|
68
|
+
SpecCustomerInfoOrder spec/moribus/macros_spec.rb 29;" c line:29
|
|
69
|
+
SpecCustomerInfoWithType spec/moribus_spec.rb 70;" c line:70
|
|
70
|
+
SpecCustomerOrder spec/moribus/macros_spec.rb 24;" c line:24
|
|
71
|
+
SpecPersonName spec/moribus/aggregated_behavior_spec.rb 13;" c line:13
|
|
72
|
+
SpecPersonName spec/moribus/extensions/delegate_associated_spec.rb 29;" c line:29
|
|
58
73
|
SpecPersonName spec/moribus_spec.rb 29;" c line:29
|
|
59
74
|
SpecPost spec/moribus/alias_association_spec.rb 5;" c line:5
|
|
60
75
|
SpecPostInfo spec/moribus/alias_association_spec.rb 19;" c line:19
|
|
76
|
+
SpecStatus spec/moribus/extensions/delegate_associated_spec.rb 5;" c line:5
|
|
61
77
|
SpecStatus spec/moribus_spec.rb 5;" c line:5
|
|
78
|
+
SpecSuffix spec/moribus/aggregated_behavior_spec.rb 5;" c line:5
|
|
79
|
+
SpecSuffix spec/moribus/extensions/delegate_associated_spec.rb 21;" c line:21
|
|
62
80
|
SpecSuffix spec/moribus_spec.rb 21;" c line:21
|
|
81
|
+
SpecType spec/moribus/extensions/delegate_associated_spec.rb 13;" c line:13
|
|
63
82
|
SpecType spec/moribus_spec.rb 13;" c line:13
|
|
64
83
|
TrackedBehavior lib/moribus/tracked_behavior.rb 8;" m line:8 class:Moribus
|
|
65
84
|
V.N coverage/assets/0.10.0/application.js 17;" f line:17
|
|
85
|
+
V.N coverage/assets/0.10.2/application.js 17;" f line:17
|
|
66
86
|
V.N coverage/assets/0.8.0/application.js 17;" f line:17
|
|
67
87
|
acts_as_aggregated lib/moribus.rb 28;" f line:28 class:Moribus.ClassMethods
|
|
68
88
|
acts_as_aggregated? lib/moribus.rb 58;" f line:58 class:Moribus.ClassMethods
|
|
@@ -77,27 +97,38 @@ alias_collection_association_methods lib/moribus/alias_association.rb 108;" f li
|
|
|
77
97
|
alias_singular_association_methods lib/moribus/alias_association.rb 98;" f line:98 class:Moribus.AliasAssociation.ClassMethods
|
|
78
98
|
association lib/moribus/extensions.rb 24;" f line:24 class:Moribus.Extensions
|
|
79
99
|
b$.bA coverage/assets/0.10.0/application.js 17;" f line:17
|
|
100
|
+
b$.bA coverage/assets/0.10.2/application.js 17;" f line:17
|
|
80
101
|
b$.bA coverage/assets/0.8.0/application.js 17;" f line:17
|
|
81
102
|
b$.bW coverage/assets/0.10.0/application.js 17;" f line:17
|
|
103
|
+
b$.bW coverage/assets/0.10.2/application.js 17;" f line:17
|
|
82
104
|
b$.bW coverage/assets/0.8.0/application.js 17;" f line:17
|
|
83
105
|
b$.bX coverage/assets/0.10.0/application.js 17;" f line:17
|
|
106
|
+
b$.bX coverage/assets/0.10.2/application.js 17;" f line:17
|
|
84
107
|
b$.bX coverage/assets/0.8.0/application.js 17;" f line:17
|
|
85
108
|
b$.bY coverage/assets/0.10.0/application.js 17;" f line:17
|
|
109
|
+
b$.bY coverage/assets/0.10.2/application.js 17;" f line:17
|
|
86
110
|
b$.bY coverage/assets/0.8.0/application.js 17;" f line:17
|
|
87
111
|
b$.bZ coverage/assets/0.10.0/application.js 17;" f line:17
|
|
112
|
+
b$.bZ coverage/assets/0.10.2/application.js 17;" f line:17
|
|
88
113
|
b$.bZ coverage/assets/0.8.0/application.js 17;" f line:17
|
|
89
114
|
b$.bi coverage/assets/0.10.0/application.js 17;" f line:17
|
|
115
|
+
b$.bi coverage/assets/0.10.2/application.js 17;" f line:17
|
|
90
116
|
b$.bi coverage/assets/0.8.0/application.js 17;" f line:17
|
|
91
117
|
b$.bj coverage/assets/0.10.0/application.js 17;" f line:17
|
|
118
|
+
b$.bj coverage/assets/0.10.2/application.js 17;" f line:17
|
|
92
119
|
b$.bj coverage/assets/0.8.0/application.js 17;" f line:17
|
|
93
120
|
b$.bk coverage/assets/0.10.0/application.js 17;" f line:17
|
|
121
|
+
b$.bk coverage/assets/0.10.2/application.js 17;" f line:17
|
|
94
122
|
b$.bk coverage/assets/0.8.0/application.js 17;" f line:17
|
|
95
123
|
b$.bl coverage/assets/0.10.0/application.js 17;" f line:17
|
|
124
|
+
b$.bl coverage/assets/0.10.2/application.js 17;" f line:17
|
|
96
125
|
b$.bl coverage/assets/0.8.0/application.js 17;" f line:17
|
|
97
126
|
b$.bm coverage/assets/0.10.0/application.js 17;" f line:17
|
|
127
|
+
b$.bm coverage/assets/0.10.2/application.js 17;" f line:17
|
|
98
128
|
b$.bm coverage/assets/0.8.0/application.js 17;" f line:17
|
|
99
129
|
belongs_to lib/moribus/alias_association.rb 39;" f line:39 class:Moribus.AliasAssociation.ClassMethods
|
|
100
130
|
bh.bg coverage/assets/0.10.0/application.js 17;" f line:17
|
|
131
|
+
bh.bg coverage/assets/0.10.2/application.js 17;" f line:17
|
|
101
132
|
bh.bg coverage/assets/0.8.0/application.js 17;" f line:17
|
|
102
133
|
bold lib/colorized_text.rb 30;" f line:30 class:ColorizedText
|
|
103
134
|
cache_aggregated_record lib/moribus/aggregated_cache_behavior.rb 63;" f line:63 class:Moribus.AggregatedCacheBehavior
|
|
@@ -108,19 +139,24 @@ colorize lib/colorized_text.rb 10;" f line:10 class:ColorizedText
|
|
|
108
139
|
column_for_attribute lib/moribus/extensions/delegate_associated.rb 35;" f line:35 class:Moribus.Extensions.DelegateAssociated
|
|
109
140
|
content_changed? lib/moribus/tracked_behavior.rb 48;" f line:48 class:Moribus.TrackedBehavior
|
|
110
141
|
cp.ce coverage/assets/0.10.0/application.js 17;" f line:17
|
|
142
|
+
cp.ce coverage/assets/0.10.2/application.js 17;" f line:17
|
|
111
143
|
cp.ce coverage/assets/0.8.0/application.js 17;" f line:17
|
|
112
144
|
cp.cf coverage/assets/0.10.0/application.js 17;" f line:17
|
|
145
|
+
cp.cf coverage/assets/0.10.2/application.js 17;" f line:17
|
|
113
146
|
cp.cf coverage/assets/0.8.0/application.js 17;" f line:17
|
|
114
|
-
current_lock_value lib/moribus/tracked_behavior.rb
|
|
115
|
-
current_to_false_sql_statement lib/moribus/tracked_behavior.rb
|
|
147
|
+
current_lock_value lib/moribus/tracked_behavior.rb 144;" f line:144
|
|
148
|
+
current_to_false_sql_statement lib/moribus/tracked_behavior.rb 117;" f line:117 class:Moribus.TrackedBehavior
|
|
116
149
|
define_delegation_module lib/moribus/extensions/has_aggregated_extension.rb 40;" f line:40 class:Moribus.Extensions.HasAggregatedExtension.Helper
|
|
117
150
|
define_effective_reader_for lib/moribus/macros.rb 144;" f line:144 class:Moribus.Macros
|
|
118
151
|
delegate_associated lib/moribus/macros.rb 51;" f line:51 class:Moribus.Macros
|
|
119
152
|
effective_ lib/moribus/macros.rb 146;" f line:146 class:Moribus.Macros.define_effective_reader_for
|
|
120
153
|
extend lib/moribus/extensions/has_aggregated_extension.rb 32;" f line:32 class:Moribus.Extensions.HasAggregatedExtension.Helper
|
|
121
154
|
extend_has_aggregated_reflection lib/moribus/extensions.rb 16;" f line:16 class:Moribus.Extensions.ClassMethods
|
|
122
|
-
first_name= spec/
|
|
155
|
+
first_name= spec/moribus/aggregated_behavior_spec.rb 23;" f line:23 class:SpecPersonName
|
|
156
|
+
first_name= spec/moribus/extensions/delegate_associated_spec.rb 40;" f line:40 class:SpecPersonName
|
|
157
|
+
first_name= spec/moribus_spec.rb 40;" f line:40 class:SpecPersonName
|
|
123
158
|
function.cr coverage/assets/0.10.0/application.js 17;" f line:17
|
|
159
|
+
function.cr coverage/assets/0.10.2/application.js 17;" f line:17
|
|
124
160
|
function.cr coverage/assets/0.8.0/application.js 17;" f line:17
|
|
125
161
|
get_parent_relation_keys lib/moribus.rb 71;" f line:71 class:Moribus.ClassMethods
|
|
126
162
|
green lib/colorized_text.rb 20;" f line:20 class:ColorizedText
|
|
@@ -131,8 +167,10 @@ has_one_current lib/moribus/macros.rb 78;" f line:78 class:Moribus.Macros
|
|
|
131
167
|
include_delegation_module lib/moribus/extensions/has_aggregated_extension.rb 81;" f line:81 class:Moribus.Extensions.HasAggregatedExtension.Helper
|
|
132
168
|
initialize lib/moribus/extensions/has_aggregated_extension.rb 26;" f line:26 class:Moribus.Extensions.HasAggregatedExtension.Helper
|
|
133
169
|
k.function.J coverage/assets/0.10.0/application.js 17;" f line:17
|
|
170
|
+
k.function.J coverage/assets/0.10.2/application.js 17;" f line:17
|
|
134
171
|
k.function.J coverage/assets/0.8.0/application.js 17;" f line:17
|
|
135
172
|
k.var.e coverage/assets/0.10.0/application.js 17;" f line:17
|
|
173
|
+
k.var.e coverage/assets/0.10.2/application.js 17;" f line:17
|
|
136
174
|
k.var.e coverage/assets/0.8.0/application.js 17;" f line:17
|
|
137
175
|
lookup_relation lib/moribus/aggregated_behavior.rb 72;" f line:72 class:Moribus.AggregatedBehavior
|
|
138
176
|
lookup_self lib/moribus/aggregated_behavior.rb 65;" f line:65 class:Moribus.AggregatedBehavior
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: HornsAndHooves-moribus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- HornsAndHooves
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date:
|
|
14
|
+
date: 2018-11-29 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: rails
|
|
@@ -184,7 +184,10 @@ files:
|
|
|
184
184
|
- spec/dummy/public/500.html
|
|
185
185
|
- spec/dummy/public/favicon.ico
|
|
186
186
|
- spec/dummy/script/rails
|
|
187
|
+
- spec/moribus/aggregated_behavior_spec.rb
|
|
187
188
|
- spec/moribus/alias_association_spec.rb
|
|
189
|
+
- spec/moribus/extensions/delegate_associated_spec.rb
|
|
190
|
+
- spec/moribus/extensions/has_current_extension_spec.rb
|
|
188
191
|
- spec/moribus/macros_spec.rb
|
|
189
192
|
- spec/moribus_spec.rb
|
|
190
193
|
- spec/spec_helper.rb
|
|
@@ -210,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
210
213
|
version: '0'
|
|
211
214
|
requirements: []
|
|
212
215
|
rubyforge_project:
|
|
213
|
-
rubygems_version: 2.
|
|
216
|
+
rubygems_version: 2.7.8
|
|
214
217
|
signing_key:
|
|
215
218
|
specification_version: 4
|
|
216
219
|
summary: Introduces Aggregated and Tracked behavior to ActiveRecord::Base models
|
|
@@ -248,7 +251,10 @@ test_files:
|
|
|
248
251
|
- spec/dummy/public/500.html
|
|
249
252
|
- spec/dummy/public/favicon.ico
|
|
250
253
|
- spec/dummy/script/rails
|
|
254
|
+
- spec/moribus/aggregated_behavior_spec.rb
|
|
251
255
|
- spec/moribus/alias_association_spec.rb
|
|
256
|
+
- spec/moribus/extensions/delegate_associated_spec.rb
|
|
257
|
+
- spec/moribus/extensions/has_current_extension_spec.rb
|
|
252
258
|
- spec/moribus/macros_spec.rb
|
|
253
259
|
- spec/moribus_spec.rb
|
|
254
260
|
- spec/spec_helper.rb
|