tunable 0.0.2 → 0.0.5
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/Gemfile +1 -1
- data/README.md +11 -3
- data/Rakefile +4 -4
- data/lib/tunable/model.rb +31 -12
- data/lib/tunable/normalizer.rb +10 -0
- data/lib/tunable/setting.rb +1 -1
- data/lib/tunable/version.rb +1 -1
- data/spec/spec_helper.rb +6 -4
- data/spec/tunable/defaults_spec.rb +2 -2
- data/spec/tunable/normalization_spec.rb +4 -7
- data/spec/tunable/setters_spec.rb +43 -0
- data/tunable.gemspec +5 -5
- metadata +15 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 18948658c5465d7de3637533a887367622d7c1067d60cc67cb4a340936144fa7
|
4
|
+
data.tar.gz: 9caad8b2f1e03f15c862e4ae44b5f07f423ab21f2fbde7be9c0bf3ce2ceafa8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1843588f136e89158718d78765dfede4be12a7748ecb6e2572c8e349b0b847209ab7f8b2e7239529eab4fc1885e0c3ee37b96f437f866d8230930efc1216dc38
|
7
|
+
data.tar.gz: 204320680e9719bcb102c25c166bb20c1df168b98fc788b2476e9106e0f81c352c01a4ad656747d811ac5b4a5d6b8207ab2cb8cd86f4e606f00ef711381dd82e
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
Tunable
|
2
2
|
=======
|
3
3
|
|
4
|
-
A simple gem that provides scoped, pluggable settings for your AR 4+ models.
|
4
|
+
A simple gem that provides scoped, pluggable settings for your AR 4+ models. Lets you keep things simple in your models by moving all customizable settings into a separate table, using a polymorphic Settings model.
|
5
|
+
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
In your Gemfile:
|
10
|
+
|
11
|
+
```
|
12
|
+
gem 'tunable'
|
13
|
+
```
|
5
14
|
|
6
15
|
The code
|
7
16
|
--------
|
@@ -19,7 +28,7 @@ Now we can do:
|
|
19
28
|
user.get_setting(:theme, :layout) # => nil
|
20
29
|
user.get_setting(:theme, :color) # => nil
|
21
30
|
|
22
|
-
user.settings = { theme: {
|
31
|
+
user.settings = { theme: { layout: 'wide', color: 'red' } }
|
23
32
|
user.save
|
24
33
|
|
25
34
|
user.get_setting(:theme, :layout) # => 'wide'
|
@@ -94,7 +103,6 @@ class User < ActiveRecord::Base
|
|
94
103
|
main_settings :layout_type => {
|
95
104
|
:default => lambda { |user| user.is_admin? ? 'advanced' : 'simple' }
|
96
105
|
}
|
97
|
-
|
98
106
|
end
|
99
107
|
```
|
100
108
|
|
data/Rakefile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
begin
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
rescue LoadError
|
8
8
|
end
|
data/lib/tunable/model.rb
CHANGED
@@ -86,14 +86,7 @@ module Tunable
|
|
86
86
|
set_default_setting(:main, field, default) unless default.nil?
|
87
87
|
|
88
88
|
define_method field do
|
89
|
-
|
90
|
-
# the instance var is already normalized to 1/0 when called by the setter
|
91
|
-
Tunable.getter_value(instance_variable_get("@setting_main_#{field}"))
|
92
|
-
else
|
93
|
-
current = main_settings[field.to_sym]
|
94
|
-
default_value = default.is_a?(Proc) ? default.call(self) : default
|
95
|
-
current.nil? ? default_value : current
|
96
|
-
end
|
89
|
+
get_value_for(field)
|
97
90
|
end
|
98
91
|
|
99
92
|
define_method "#{field}?" do
|
@@ -111,11 +104,12 @@ module Tunable
|
|
111
104
|
end
|
112
105
|
|
113
106
|
value = Tunable.normalize_value(raw_value)
|
114
|
-
current = Tunable.normalize_value(
|
107
|
+
current = Tunable.normalize_value(get_value_for(field, false)) # don't fallback to default
|
115
108
|
# debug "Setting #{field} to #{value} (#{value.class}), current: #{current} (#{current.class})"
|
116
109
|
|
117
110
|
if value === current
|
118
|
-
|
111
|
+
# puts 'Value is same as current'
|
112
|
+
@changed_attributes.delete(field) # in case we had set if before
|
119
113
|
return
|
120
114
|
end
|
121
115
|
|
@@ -159,6 +153,15 @@ module Tunable
|
|
159
153
|
self.class.default_settings(context)[key.to_sym]
|
160
154
|
end
|
161
155
|
|
156
|
+
def set_setting(context, key, val)
|
157
|
+
obj = { context => { key => val } }
|
158
|
+
self.settings = obj
|
159
|
+
end
|
160
|
+
|
161
|
+
def remove_setting(context, key)
|
162
|
+
set_setting(context, key, nil)
|
163
|
+
end
|
164
|
+
|
162
165
|
def get_main_setting(key)
|
163
166
|
get_setting(:main, key)
|
164
167
|
end
|
@@ -186,20 +189,36 @@ module Tunable
|
|
186
189
|
|
187
190
|
def queue_setting_for_update(context, key, val)
|
188
191
|
if self.class.main_settings_list.include?(key.to_sym)
|
189
|
-
|
192
|
+
@changed_attributes[key.to_sym] = val if changed_attributes.include?(key.to_sym)
|
190
193
|
end
|
191
194
|
(modified_settings[context.to_sym] ||= {})[key.to_sym] = val
|
192
195
|
end
|
193
196
|
|
194
197
|
def queue_setting_for_deletion(context, key)
|
195
198
|
if self.class.main_settings_list.include?(key)
|
196
|
-
|
199
|
+
@changed_attributes[key.to_sym] = nil if changed_attributes.include?(key.to_sym)
|
197
200
|
end
|
198
201
|
(deleted_settings[context.to_sym] ||= []) << key.to_sym
|
199
202
|
end
|
200
203
|
|
201
204
|
private
|
202
205
|
|
206
|
+
def get_value_for(field, use_default = true)
|
207
|
+
if instance_variable_defined?("@setting_main_#{field}")
|
208
|
+
# the instance var is already normalized to 1/0 when called by the setter
|
209
|
+
Tunable.getter_value(instance_variable_get("@setting_main_#{field}"))
|
210
|
+
else
|
211
|
+
current = main_settings[field.to_sym]
|
212
|
+
return current if current.present? or !use_default
|
213
|
+
|
214
|
+
if default = self.class.default_settings(:main)[field.to_sym]
|
215
|
+
return default.is_a?(Proc) ? default.call(self) : default
|
216
|
+
end
|
217
|
+
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
203
222
|
def modified_settings
|
204
223
|
@modified_settings ||= {}
|
205
224
|
end
|
data/lib/tunable/normalizer.rb
CHANGED
@@ -8,6 +8,7 @@ module Tunable
|
|
8
8
|
def normalize_value(val)
|
9
9
|
return 1 if TRUTHIES.include?(val.to_s)
|
10
10
|
return 0 if FALSIES.include?(val.to_s)
|
11
|
+
return Integer(val) if is_integer?(val)
|
11
12
|
return if val.blank? # false.blank? returns true so this needs to go after the 0 line
|
12
13
|
val
|
13
14
|
end
|
@@ -29,6 +30,15 @@ module Tunable
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
33
|
+
private
|
34
|
+
|
35
|
+
def is_integer?(val)
|
36
|
+
Integer(val)
|
37
|
+
true
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
32
42
|
end
|
33
43
|
|
34
44
|
end
|
data/lib/tunable/setting.rb
CHANGED
@@ -26,7 +26,7 @@ module Tunable
|
|
26
26
|
|
27
27
|
hash.each do |context, fields|
|
28
28
|
fields.each do |key, val|
|
29
|
-
if val.blank? && object.settings_context(context.to_sym)[key.to_sym].
|
29
|
+
if val.blank? && !object.settings_context(context.to_sym)[key.to_sym].nil?
|
30
30
|
|
31
31
|
# setting was present and now deleted
|
32
32
|
object.queue_setting_for_deletion(context, key)
|
data/lib/tunable/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
3
4
|
require 'active_record'
|
5
|
+
require 'tunable'
|
4
6
|
|
5
7
|
ActiveRecord::Base.establish_connection(
|
6
|
-
"adapter"
|
8
|
+
"adapter" => "sqlite3",
|
9
|
+
"database" => ':memory:'
|
7
10
|
)
|
8
11
|
|
9
12
|
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
|
10
13
|
|
11
14
|
this_path = File.dirname(__FILE__)
|
12
15
|
load File.join(this_path, '/schema.rb')
|
13
|
-
require File.join(this_path, '..', 'lib', 'tunable.rb')
|
14
16
|
|
15
17
|
class TunableModel < ActiveRecord::Base
|
16
18
|
include Tunable::Model
|
@@ -100,7 +100,7 @@ describe 'defaults' do
|
|
100
100
|
|
101
101
|
TEST_SETTINGS.each do |key, val|
|
102
102
|
it "#{key} equals new val" do
|
103
|
-
@foo.main_settings[key].should
|
103
|
+
@foo.main_settings[key].should === @new_settings[key]
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
@@ -110,7 +110,7 @@ describe 'defaults' do
|
|
110
110
|
|
111
111
|
TEST_SETTINGS.each do |key, val|
|
112
112
|
it "#{key} equals new val" do
|
113
|
-
@foo.get_main_setting(key).should
|
113
|
+
@foo.get_main_setting(key).should === @new_settings[key]
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -18,7 +18,7 @@ describe 'normalization' do
|
|
18
18
|
:numbers => {
|
19
19
|
:number_0 => '0',
|
20
20
|
:number_1 => '1',
|
21
|
-
:number_of_the_beast =>
|
21
|
+
:number_of_the_beast => 666
|
22
22
|
},
|
23
23
|
:onoffs => {
|
24
24
|
:on => 'on',
|
@@ -57,7 +57,7 @@ describe 'normalization' do
|
|
57
57
|
sql = "select `value` from `settings` where `settable_id` = #{id} and `context` = '#{context}' and `key` = '#{key}';"
|
58
58
|
res = ActiveRecord::Base.connection.execute(sql)
|
59
59
|
# puts res.inspect
|
60
|
-
res[0]['value']
|
60
|
+
res[0] ? res[0]['value'] : nil
|
61
61
|
end
|
62
62
|
|
63
63
|
describe 'creating a new setting directly' do
|
@@ -188,10 +188,7 @@ describe 'normalization' do
|
|
188
188
|
describe 'using settings relationship' do
|
189
189
|
|
190
190
|
before :all do
|
191
|
-
|
192
|
-
@model.update_attributes({
|
193
|
-
:settings => VALUE_TYPES
|
194
|
-
})
|
191
|
+
@model.update_attributes(:settings => VALUE_TYPES)
|
195
192
|
end
|
196
193
|
|
197
194
|
after :all do
|
@@ -292,7 +289,7 @@ describe 'normalization' do
|
|
292
289
|
end
|
293
290
|
|
294
291
|
it 'keeps 666 as 666' do
|
295
|
-
get_value_from_model(:numbers, :number_of_the_beast).should ==
|
292
|
+
get_value_from_model(:numbers, :number_of_the_beast).should == 666
|
296
293
|
end
|
297
294
|
|
298
295
|
it 'keeps foo as foo' do
|
@@ -1,5 +1,48 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
|
+
describe 'basic setters' do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
load_main_settings!
|
7
|
+
@model = TunableModel.create(name: "Yay, I'm settable!")
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#set_setting' do
|
11
|
+
|
12
|
+
it 'stores new setting, without saving' do
|
13
|
+
@model.set_setting(:foo, :bar, 123)
|
14
|
+
expect(@model.get_setting(:foo, :bar)).to eq(nil)
|
15
|
+
expect(@model.settings_hash[:foo]).to eq(nil)
|
16
|
+
@model.save
|
17
|
+
@model.reload
|
18
|
+
expect(@model.get_setting(:foo, :bar)).to eq(123)
|
19
|
+
expect(@model.settings_hash[:foo]).to eq({ bar: 123 })
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#remove_setting' do
|
25
|
+
|
26
|
+
it 'stores new setting' do
|
27
|
+
@model.set_setting(:foo, :bar, 123)
|
28
|
+
@model.set_setting(:foo, :test, false)
|
29
|
+
@model.save
|
30
|
+
@model.reload
|
31
|
+
@model.remove_setting(:foo, :bar)
|
32
|
+
expect(@model.get_setting(:foo, :bar)).to eq(123)
|
33
|
+
@model.save
|
34
|
+
@model.reload
|
35
|
+
expect(@model.settings_hash[:foo]).to eq({ test: false })
|
36
|
+
@model.remove_setting(:foo, :test)
|
37
|
+
@model.save
|
38
|
+
@model.reload
|
39
|
+
expect(@model.settings_hash[:foo]).to eq(nil)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
3
46
|
describe 'main_setting setters' do
|
4
47
|
|
5
48
|
before :all do
|
data/tunable.gemspec
CHANGED
@@ -14,12 +14,12 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.required_rubygems_version = ">= 1.3.6"
|
15
15
|
# s.rubyforge_project = "tunable"
|
16
16
|
|
17
|
-
s.add_runtime_dependency "activerecord", "~> 4.
|
18
|
-
s.add_runtime_dependency "activesupport", "~> 4.
|
17
|
+
s.add_runtime_dependency "activerecord", "~> 4.2.x"
|
18
|
+
s.add_runtime_dependency "activesupport", "~> 4.2.x"
|
19
19
|
|
20
|
-
s.add_development_dependency "bundler"
|
21
|
-
s.add_development_dependency "rspec"
|
22
|
-
s.add_development_dependency "sqlite3", '
|
20
|
+
s.add_development_dependency "bundler" #, '~> 1.0', '>= 1.0.0'
|
21
|
+
s.add_development_dependency "rspec" #, '~> 3.0', '>= 3.0.0'
|
22
|
+
s.add_development_dependency "sqlite3", '< 1.4.x'
|
23
23
|
|
24
24
|
s.files = `git ls-files`.split("\n")
|
25
25
|
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tunable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomás Pollak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,88 +16,70 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 4.2.x
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.2.x
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 4.2.x
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 4.2.x
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.0'
|
48
45
|
- - ">="
|
49
46
|
- !ruby/object:Gem::Version
|
50
|
-
version:
|
47
|
+
version: '0'
|
51
48
|
type: :development
|
52
49
|
prerelease: false
|
53
50
|
version_requirements: !ruby/object:Gem::Requirement
|
54
51
|
requirements:
|
55
|
-
- - "~>"
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '1.0'
|
58
52
|
- - ">="
|
59
53
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
54
|
+
version: '0'
|
61
55
|
- !ruby/object:Gem::Dependency
|
62
56
|
name: rspec
|
63
57
|
requirement: !ruby/object:Gem::Requirement
|
64
58
|
requirements:
|
65
|
-
- - "~>"
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: '3.0'
|
68
59
|
- - ">="
|
69
60
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
61
|
+
version: '0'
|
71
62
|
type: :development
|
72
63
|
prerelease: false
|
73
64
|
version_requirements: !ruby/object:Gem::Requirement
|
74
65
|
requirements:
|
75
|
-
- - "~>"
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '3.0'
|
78
66
|
- - ">="
|
79
67
|
- !ruby/object:Gem::Version
|
80
|
-
version:
|
68
|
+
version: '0'
|
81
69
|
- !ruby/object:Gem::Dependency
|
82
70
|
name: sqlite3
|
83
71
|
requirement: !ruby/object:Gem::Requirement
|
84
72
|
requirements:
|
85
|
-
- - "
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
version: '1.0'
|
88
|
-
- - ">="
|
73
|
+
- - "<"
|
89
74
|
- !ruby/object:Gem::Version
|
90
|
-
version: 1.
|
75
|
+
version: 1.4.x
|
91
76
|
type: :development
|
92
77
|
prerelease: false
|
93
78
|
version_requirements: !ruby/object:Gem::Requirement
|
94
79
|
requirements:
|
95
|
-
- - "
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '1.0'
|
98
|
-
- - ">="
|
80
|
+
- - "<"
|
99
81
|
- !ruby/object:Gem::Version
|
100
|
-
version: 1.
|
82
|
+
version: 1.4.x
|
101
83
|
description: Pluggable settings for your AR models.
|
102
84
|
email:
|
103
85
|
- tomas@forkhq.com
|
@@ -145,9 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
127
|
version: 1.3.6
|
146
128
|
requirements: []
|
147
129
|
rubyforge_project:
|
148
|
-
rubygems_version: 2.
|
130
|
+
rubygems_version: 2.7.3
|
149
131
|
signing_key:
|
150
132
|
specification_version: 4
|
151
133
|
summary: Pluggable settings for your AR models.
|
152
134
|
test_files: []
|
153
|
-
has_rdoc:
|