ledermann-rails-settings 1.2.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +14 -6
  4. data/Gemfile +1 -1
  5. data/MIT-LICENSE +6 -5
  6. data/README.md +171 -158
  7. data/Rakefile +4 -9
  8. data/ci/Gemfile-rails-4-2 +7 -0
  9. data/ci/Gemfile-rails-5-0 +6 -0
  10. data/ci/Gemfile-rails-5-1 +6 -0
  11. data/ci/Gemfile-rails-5-2 +6 -0
  12. data/ci/Gemfile-rails-6-0 +6 -0
  13. data/lib/generators/rails_settings/migration/migration_generator.rb +23 -0
  14. data/lib/generators/rails_settings/migration/templates/migration.rb +21 -0
  15. data/lib/ledermann-rails-settings.rb +1 -0
  16. data/lib/rails-settings.rb +21 -5
  17. data/lib/rails-settings/base.rb +48 -0
  18. data/lib/rails-settings/configuration.rb +39 -0
  19. data/lib/rails-settings/scopes.rb +34 -0
  20. data/lib/rails-settings/setting_object.rb +84 -0
  21. data/lib/rails-settings/version.rb +2 -2
  22. data/rails-settings.gemspec +22 -21
  23. data/spec/configuration_spec.rb +120 -0
  24. data/spec/database.yml +3 -0
  25. data/spec/queries_spec.rb +101 -0
  26. data/spec/scopes_spec.rb +31 -0
  27. data/spec/serialize_spec.rb +40 -0
  28. data/spec/setting_object_spec.rb +153 -0
  29. data/spec/settings_spec.rb +248 -0
  30. data/spec/spec_helper.rb +111 -0
  31. data/spec/support/matchers/perform_queries.rb +22 -0
  32. data/spec/support/query_counter.rb +17 -0
  33. metadata +130 -118
  34. data/Changelog.md +0 -17
  35. data/ci/Gemfile.rails-2.3.x +0 -5
  36. data/ci/Gemfile.rails-3.0.x +0 -5
  37. data/ci/Gemfile.rails-3.1.x +0 -5
  38. data/ci/Gemfile.rails-3.2.x +0 -5
  39. data/init.rb +0 -1
  40. data/lib/rails-settings/active_record.rb +0 -38
  41. data/lib/rails-settings/null_store.rb +0 -48
  42. data/lib/rails-settings/scoped_settings.rb +0 -14
  43. data/lib/rails-settings/settings.rb +0 -142
  44. data/test/settings_test.rb +0 -252
  45. data/test/test_helper.rb +0 -34
data/spec/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Queries performed' do
4
+ context 'New record' do
5
+ let!(:user) { User.new :name => 'Mr. Pink' }
6
+
7
+ it 'should be saved by one SQL query' do
8
+ expect {
9
+ user.save!
10
+ }.to perform_queries(1)
11
+ end
12
+
13
+ it 'should be saved with settings for one key by two SQL queries' do
14
+ expect {
15
+ user.settings(:dashboard).foo = 42
16
+ user.settings(:dashboard).bar = 'string'
17
+ user.save!
18
+ }.to perform_queries(2)
19
+ end
20
+
21
+ it 'should be saved with settings for two keys by three SQL queries' do
22
+ expect {
23
+ user.settings(:dashboard).foo = 42
24
+ user.settings(:dashboard).bar = 'string'
25
+ user.settings(:calendar).bar = 'string'
26
+ user.save!
27
+ }.to perform_queries(3)
28
+ end
29
+ end
30
+
31
+ context 'Existing record without settings' do
32
+ let!(:user) { User.create! :name => 'Mr. Pink' }
33
+
34
+ it 'should be saved without SQL queries' do
35
+ expect {
36
+ user.save!
37
+ }.to perform_queries(0)
38
+ end
39
+
40
+ it 'should be saved with settings for one key by two SQL queries' do
41
+ expect {
42
+ user.settings(:dashboard).foo = 42
43
+ user.settings(:dashboard).bar = 'string'
44
+ user.save!
45
+ }.to perform_queries(2)
46
+ end
47
+
48
+ it 'should be saved with settings for two keys by three SQL queries' do
49
+ expect {
50
+ user.settings(:dashboard).foo = 42
51
+ user.settings(:dashboard).bar = 'string'
52
+ user.settings(:calendar).bar = 'string'
53
+ user.save!
54
+ }.to perform_queries(3)
55
+ end
56
+ end
57
+
58
+ context 'Existing record with settings' do
59
+ let!(:user) do
60
+ User.create! :name => 'Mr. Pink' do |user|
61
+ user.settings(:dashboard).theme = 'pink'
62
+ user.settings(:calendar).scope = 'all'
63
+ end
64
+ end
65
+
66
+ it 'should be saved without SQL queries' do
67
+ expect {
68
+ user.save!
69
+ }.to perform_queries(0)
70
+ end
71
+
72
+ it 'should be saved with settings for one key by one SQL queries' do
73
+ expect {
74
+ user.settings(:dashboard).foo = 42
75
+ user.settings(:dashboard).bar = 'string'
76
+ user.save!
77
+ }.to perform_queries(1)
78
+ end
79
+
80
+ it 'should be saved with settings for two keys by two SQL queries' do
81
+ expect {
82
+ user.settings(:dashboard).foo = 42
83
+ user.settings(:dashboard).bar = 'string'
84
+ user.settings(:calendar).bar = 'string'
85
+ user.save!
86
+ }.to perform_queries(2)
87
+ end
88
+
89
+ it 'should be destroyed by two SQL queries' do
90
+ expect {
91
+ user.destroy
92
+ }.to perform_queries(2)
93
+ end
94
+
95
+ it "should update settings by one SQL query" do
96
+ expect {
97
+ user.settings(:dashboard).update! :foo => 'bar'
98
+ }.to perform_queries(1)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'scopes' do
4
+ let!(:user1) { User.create! :name => 'Mr. White' do |user| user.settings(:dashboard).theme = 'white' end }
5
+ let!(:user2) { User.create! :name => 'Mr. Blue' }
6
+
7
+ it "should find objects with existing settings" do
8
+ expect(User.with_settings).to eq([user1])
9
+ end
10
+
11
+ it "should find objects with settings for key" do
12
+ expect(User.with_settings_for(:dashboard)).to eq([user1])
13
+ expect(User.with_settings_for(:foo)).to eq([])
14
+ end
15
+
16
+ it "should records without settings" do
17
+ expect(User.without_settings).to eq([user2])
18
+ end
19
+
20
+ it "should records without settings for key" do
21
+ expect(User.without_settings_for(:foo)).to eq([user1, user2])
22
+ expect(User.without_settings_for(:dashboard)).to eq([user2])
23
+ end
24
+
25
+ it "should require symbol as key" do
26
+ [ nil, "string", 42 ].each do |invalid_key|
27
+ expect { User.without_settings_for(invalid_key) }.to raise_error(ArgumentError)
28
+ expect { User.with_settings_for(invalid_key) }.to raise_error(ArgumentError)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Serialization" do
4
+ let!(:user) do
5
+ User.create! :name => 'Mr. White' do |user|
6
+ user.settings(:dashboard).theme = 'white'
7
+ user.settings(:calendar).scope = 'all'
8
+ end
9
+ end
10
+
11
+ describe 'created settings' do
12
+ it 'should be serialized' do
13
+ user.reload
14
+
15
+ dashboard_settings = user.setting_objects.where(:var => 'dashboard').first
16
+ calendar_settings = user.setting_objects.where(:var => 'calendar').first
17
+
18
+ expect(dashboard_settings.var).to eq('dashboard')
19
+ expect(dashboard_settings.value).to eq({'theme' => 'white'})
20
+
21
+ expect(calendar_settings.var).to eq('calendar')
22
+ expect(calendar_settings.value).to eq({'scope' => 'all'})
23
+ end
24
+ end
25
+
26
+ describe 'updated settings' do
27
+ it 'should be serialized' do
28
+ user.settings(:dashboard).update! :smart => true
29
+
30
+ dashboard_settings = user.setting_objects.where(:var => 'dashboard').first
31
+ calendar_settings = user.setting_objects.where(:var => 'calendar').first
32
+
33
+ expect(dashboard_settings.var).to eq('dashboard')
34
+ expect(dashboard_settings.value).to eq({'theme' => 'white', 'smart' => true})
35
+
36
+ expect(calendar_settings.var).to eq('calendar')
37
+ expect(calendar_settings.value).to eq({'scope' => 'all'})
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe RailsSettings::SettingObject do
4
+ let(:user) { User.create! :name => 'Mr. Pink' }
5
+
6
+ if RailsSettings.can_protect_attributes?
7
+ let(:new_setting_object) { user.setting_objects.build({ :var => 'dashboard'}, :without_protection => true) }
8
+ let(:saved_setting_object) { user.setting_objects.create!({ :var => 'dashboard', :value => { 'theme' => 'pink', 'filter' => false}}, :without_protection => true) }
9
+ else
10
+ let(:new_setting_object) { user.setting_objects.build({ :var => 'dashboard'}) }
11
+ let(:saved_setting_object) { user.setting_objects.create!({ :var => 'dashboard', :value => { 'theme' => 'pink', 'filter' => false}}) }
12
+ end
13
+
14
+ describe "serialization" do
15
+ it "should have a hash default" do
16
+ expect(RailsSettings::SettingObject.new.value).to eq({})
17
+ end
18
+ end
19
+
20
+ describe "Getter and Setter" do
21
+ context "on unsaved settings" do
22
+ it "should respond to setters" do
23
+ expect(new_setting_object).to respond_to(:foo=)
24
+ expect(new_setting_object).to respond_to(:bar=)
25
+ end
26
+
27
+ it "should not respond to some getters" do
28
+ expect { new_setting_object.foo! }.to raise_error(NoMethodError)
29
+ expect { new_setting_object.foo? }.to raise_error(NoMethodError)
30
+ end
31
+
32
+ it "should not respond if a block is given" do
33
+ expect {
34
+ new_setting_object.foo do
35
+ end
36
+ }.to raise_error(NoMethodError)
37
+ end
38
+
39
+ it "should not respond if params are given" do
40
+ expect { new_setting_object.foo(42) }.to raise_error(NoMethodError)
41
+ expect { new_setting_object.foo(42,43) }.to raise_error(NoMethodError)
42
+ end
43
+
44
+ it "should return nil for unknown attribute" do
45
+ expect(new_setting_object.foo).to eq(nil)
46
+ expect(new_setting_object.bar).to eq(nil)
47
+ end
48
+
49
+ it "should return defaults" do
50
+ expect(new_setting_object.theme).to eq('blue')
51
+ expect(new_setting_object.view).to eq('monthly')
52
+ expect(new_setting_object.filter).to eq(true)
53
+ end
54
+
55
+ it "should return defaults when using `try`" do
56
+ expect(new_setting_object.try(:theme)).to eq('blue')
57
+ expect(new_setting_object.try(:view)).to eq('monthly')
58
+ expect(new_setting_object.try(:filter)).to eq(true)
59
+ end
60
+
61
+ it "should store different objects to value hash" do
62
+ new_setting_object.integer = 42
63
+ new_setting_object.float = 1.234
64
+ new_setting_object.string = 'Hello, World!'
65
+ new_setting_object.array = [ 1,2,3 ]
66
+ new_setting_object.symbol = :foo
67
+
68
+ expect(new_setting_object.value).to eq('integer' => 42,
69
+ 'float' => 1.234,
70
+ 'string' => 'Hello, World!',
71
+ 'array' => [ 1,2,3 ],
72
+ 'symbol' => :foo)
73
+ end
74
+
75
+ it "should set and return attributes" do
76
+ new_setting_object.theme = 'pink'
77
+ new_setting_object.foo = 42
78
+ new_setting_object.bar = 'hello'
79
+
80
+ expect(new_setting_object.theme).to eq('pink')
81
+ expect(new_setting_object.foo).to eq(42)
82
+ expect(new_setting_object.bar).to eq('hello')
83
+ end
84
+
85
+ it "should set dirty trackers on change" do
86
+ new_setting_object.theme = 'pink'
87
+ expect(new_setting_object).to be_value_changed
88
+ expect(new_setting_object).to be_changed
89
+ end
90
+ end
91
+
92
+ context "on saved settings" do
93
+ it "should not set dirty trackers on setting same value" do
94
+ saved_setting_object.theme = 'pink'
95
+ expect(saved_setting_object).not_to be_value_changed
96
+ expect(saved_setting_object).not_to be_changed
97
+ end
98
+
99
+ it "should delete key on assigning nil" do
100
+ saved_setting_object.theme = nil
101
+ expect(saved_setting_object.value).to eq({ 'filter' => false })
102
+ end
103
+ end
104
+ end
105
+
106
+ describe "update" do
107
+ it 'should save' do
108
+ expect(new_setting_object.update(:foo => 42, :bar => 'string')).to be_truthy
109
+ new_setting_object.reload
110
+
111
+ expect(new_setting_object.foo).to eq(42)
112
+ expect(new_setting_object.bar).to eq('string')
113
+ expect(new_setting_object).not_to be_new_record
114
+ expect(new_setting_object.id).not_to be_zero
115
+ end
116
+
117
+ it 'should not save blank hash' do
118
+ expect(new_setting_object.update({})).to be_truthy
119
+ end
120
+
121
+ if RailsSettings.can_protect_attributes?
122
+ it 'should not allow changing protected attributes' do
123
+ new_setting_object.update!(:var => 'calendar', :foo => 42)
124
+
125
+ expect(new_setting_object.var).to eq('dashboard')
126
+ expect(new_setting_object.foo).to eq(42)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe "save" do
132
+ it "should save" do
133
+ new_setting_object.foo = 42
134
+ new_setting_object.bar = 'string'
135
+ expect(new_setting_object.save).to be_truthy
136
+ new_setting_object.reload
137
+
138
+ expect(new_setting_object.foo).to eq(42)
139
+ expect(new_setting_object.bar).to eq('string')
140
+ expect(new_setting_object).not_to be_new_record
141
+ expect(new_setting_object.id).not_to be_zero
142
+ end
143
+ end
144
+
145
+ describe "validation" do
146
+ it "should not validate for unknown var" do
147
+ new_setting_object.var = "unknown-var"
148
+
149
+ expect(new_setting_object).not_to be_valid
150
+ expect(new_setting_object.errors[:var]).to be_present
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Defaults" do
4
+ it "should be stored for simple class" do
5
+ expect(Account.default_settings).to eq(:portal => {})
6
+ end
7
+
8
+ it "should be stored for parent class" do
9
+ expect(User.default_settings).to eq(:dashboard => { 'theme' => 'blue', 'view' => 'monthly', 'filter' => true },
10
+ :calendar => { 'scope' => 'company'})
11
+ end
12
+
13
+ it "should be stored for child class" do
14
+ expect(GuestUser.default_settings).to eq(:dashboard => { 'theme' => 'red', 'view' => 'monthly', 'filter' => true })
15
+ end
16
+ end
17
+
18
+ describe "Getter/Setter" do
19
+ let(:account) { Account.new :subdomain => 'foo' }
20
+
21
+ it "should handle method syntax" do
22
+ account.settings(:portal).enabled = true
23
+ account.settings(:portal).template = 'black'
24
+
25
+ expect(account.settings(:portal).enabled).to eq(true)
26
+ expect(account.settings(:portal).template).to eq('black')
27
+ end
28
+
29
+ it "should return nil for not existing key" do
30
+ expect(account.settings(:portal).foo).to eq(nil)
31
+ end
32
+ end
33
+
34
+ describe 'Objects' do
35
+ context 'without defaults' do
36
+ let(:account) { Account.new :subdomain => 'foo' }
37
+
38
+ it 'should have blank settings' do
39
+ expect(account.settings(:portal).value).to eq({})
40
+ end
41
+
42
+ it 'should allow saving a blank value' do
43
+ account.save!
44
+ expect(account.settings(:portal).save).to be_truthy
45
+ end
46
+
47
+ it 'should allow removing all values' do
48
+ account.settings(:portal).premium = true
49
+ account.settings(:portal).fee = 42.5
50
+ account.save!
51
+
52
+ account.settings(:portal).premium = nil
53
+ expect(account.save).to be_truthy
54
+
55
+ account.settings(:portal).fee = nil
56
+ expect(account.save).to be_truthy
57
+ end
58
+
59
+ it 'should not add settings on saving' do
60
+ account.save!
61
+ expect(RailsSettings::SettingObject.count).to eq(0)
62
+ end
63
+
64
+ it "should save object with settings" do
65
+ account.settings(:portal).premium = true
66
+ account.settings(:portal).fee = 42.5
67
+ account.save!
68
+
69
+ account.reload
70
+ expect(account.settings(:portal).premium).to eq(true)
71
+ expect(account.settings(:portal).fee).to eq(42.5)
72
+
73
+ expect(RailsSettings::SettingObject.count).to eq(1)
74
+ expect(RailsSettings::SettingObject.first.value).to eq({ 'premium' => true, 'fee' => 42.5 })
75
+ end
76
+
77
+ it "should save settings separated" do
78
+ account.save!
79
+
80
+ settings = account.settings(:portal)
81
+ settings.enabled = true
82
+ settings.template = 'black'
83
+ settings.save!
84
+
85
+ account.reload
86
+ expect(account.settings(:portal).enabled).to eq(true)
87
+ expect(account.settings(:portal).template).to eq('black')
88
+ end
89
+ end
90
+
91
+ context 'with defaults' do
92
+ let(:user) { User.new :name => 'Mr. Brown' }
93
+
94
+ it 'should have default settings' do
95
+ expect(user.settings(:dashboard).theme).to eq('blue')
96
+ expect(user.settings(:dashboard).view).to eq('monthly')
97
+ expect(user.settings(:dashboard).filter).to eq(true)
98
+ expect(user.settings(:calendar).scope).to eq('company')
99
+ end
100
+
101
+ it 'should have default settings after changing one' do
102
+ user.settings(:dashboard).theme = 'gray'
103
+
104
+ expect(user.settings(:dashboard).theme).to eq('gray')
105
+ expect(user.settings(:dashboard).view).to eq('monthly')
106
+ expect(user.settings(:dashboard).filter).to eq(true)
107
+ expect(user.settings(:calendar).scope).to eq('company')
108
+ end
109
+
110
+ it "should overwrite settings" do
111
+ user.settings(:dashboard).theme = 'brown'
112
+ user.settings(:dashboard).filter = false
113
+ user.save!
114
+
115
+ user.reload
116
+ expect(user.settings(:dashboard).theme).to eq('brown')
117
+ expect(user.settings(:dashboard).filter).to eq(false)
118
+ expect(RailsSettings::SettingObject.count).to eq(1)
119
+ expect(RailsSettings::SettingObject.first.value).to eq({ 'theme' => 'brown', 'filter' => false })
120
+ end
121
+
122
+ it "should merge settings with defaults" do
123
+ user.settings(:dashboard).theme = 'brown'
124
+ user.save!
125
+
126
+ user.reload
127
+ expect(user.settings(:dashboard).theme).to eq('brown')
128
+ expect(user.settings(:dashboard).filter).to eq(true)
129
+ expect(RailsSettings::SettingObject.count).to eq(1)
130
+ expect(RailsSettings::SettingObject.first.value).to eq({ 'theme' => 'brown' })
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "Object without settings" do
136
+ let!(:user) { User.create! :name => 'Mr. White' }
137
+
138
+ it "should respond to #settings?" do
139
+ expect(user.settings?).to eq(false)
140
+ expect(user.settings?(:dashboard)).to eq(false)
141
+ end
142
+
143
+ it "should have no setting objects" do
144
+ expect(RailsSettings::SettingObject.count).to eq(0)
145
+ end
146
+
147
+ it "should add settings" do
148
+ user.settings(:dashboard).update! :smart => true
149
+
150
+ user.reload
151
+ expect(user.settings(:dashboard).smart).to eq(true)
152
+ end
153
+
154
+ it "should not save settings if assigned nil" do
155
+ expect {
156
+ user.settings = nil
157
+ user.save!
158
+ }.to_not change(RailsSettings::SettingObject, :count)
159
+ end
160
+ end
161
+
162
+ describe "Object with settings" do
163
+ let!(:user) do
164
+ User.create! :name => 'Mr. White' do |user|
165
+ user.settings(:dashboard).theme = 'white'
166
+ user.settings(:calendar).scope = 'all'
167
+ end
168
+ end
169
+
170
+ it "should respond to #settings?" do
171
+ expect(user.settings?).to eq(true)
172
+
173
+ expect(user.settings?(:dashboard)).to eq(true)
174
+ expect(user.settings?(:calendar)).to eq(true)
175
+ end
176
+
177
+ it "should have two setting objects" do
178
+ expect(RailsSettings::SettingObject.count).to eq(2)
179
+ end
180
+
181
+ it "should update settings" do
182
+ user.settings(:dashboard).update! :smart => true
183
+ user.reload
184
+
185
+ expect(user.settings(:dashboard).smart).to eq(true)
186
+ expect(user.settings(:dashboard).theme).to eq('white')
187
+ expect(user.settings(:calendar).scope).to eq('all')
188
+ end
189
+
190
+ it "should update settings by saving object" do
191
+ user.settings(:dashboard).smart = true
192
+ user.save!
193
+
194
+ user.reload
195
+ expect(user.settings(:dashboard).smart).to eq(true)
196
+ end
197
+
198
+ it "should destroy settings with nil" do
199
+ expect {
200
+ user.settings = nil
201
+ user.save!
202
+ }.to change(RailsSettings::SettingObject, :count).by(-2)
203
+
204
+ expect(user.settings?).to eq(false)
205
+ end
206
+
207
+ it "should raise exception on assigning other than nil" do
208
+ expect {
209
+ user.settings = :foo
210
+ user.save!
211
+ }.to raise_error(ArgumentError)
212
+ end
213
+ end
214
+
215
+ describe "Customized SettingObject" do
216
+ let(:project) { Project.create! :name => 'Heist' }
217
+
218
+ it "should not accept invalid attributes" do
219
+ project.settings(:info).owner_name = 42
220
+ expect(project.settings(:info)).not_to be_valid
221
+
222
+ project.settings(:info).owner_name = ''
223
+ expect(project.settings(:info)).not_to be_valid
224
+ end
225
+
226
+ it "should accept valid attributes" do
227
+ project.settings(:info).owner_name = 'Mr. Brown'
228
+ expect(project.settings(:info)).to be_valid
229
+ end
230
+ end
231
+
232
+ describe "to_settings_hash" do
233
+ let(:user) do
234
+ User.new :name => 'Mrs. Fin' do |user|
235
+ user.settings(:dashboard).theme = 'green'
236
+ user.settings(:dashboard).sound = 11
237
+ user.settings(:calendar).scope = 'some'
238
+ end
239
+ end
240
+
241
+ it "should return defaults" do
242
+ expect(User.new.to_settings_hash).to eq({:dashboard=>{"theme"=>"blue", "view"=>"monthly", "filter"=>true}, :calendar=>{"scope"=>"company"}})
243
+ end
244
+
245
+ it "should return merged settings" do
246
+ expect(user.to_settings_hash).to eq({:dashboard=>{"theme"=>"green", "view"=>"monthly", "filter"=>true, "sound" => 11}, :calendar=>{"scope"=>"some"}})
247
+ end
248
+ end