settingcrazy 0.0.2
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.
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +245 -0
- data/Rakefile +2 -0
- data/TODO +10 -0
- data/db/safeattributes.db +0 -0
- data/lib/generators/settingcrazy/setting_values_migration/setting_values_migration_generator.rb +18 -0
- data/lib/generators/settingcrazy/setting_values_migration/templates/migration.rb +15 -0
- data/lib/settingcrazy.rb +23 -0
- data/lib/settingcrazy/class_methods.rb +32 -0
- data/lib/settingcrazy/inheritor.rb +31 -0
- data/lib/settingcrazy/instance_methods.rb +20 -0
- data/lib/settingcrazy/namespace.rb +14 -0
- data/lib/settingcrazy/namespaced_settings_proxy.rb +18 -0
- data/lib/settingcrazy/setting_value.rb +11 -0
- data/lib/settingcrazy/settings_proxy.rb +107 -0
- data/lib/settingcrazy/settings_validator.rb +43 -0
- data/lib/settingcrazy/template.rb +2 -0
- data/lib/settingcrazy/template/base.rb +66 -0
- data/lib/settingcrazy/template/enum.rb +19 -0
- data/lib/settingcrazy/version.rb +3 -0
- data/settingcrazy.gemspec +21 -0
- data/spec/inheritance_spec.rb +76 -0
- data/spec/mass_assignment_spec.rb +69 -0
- data/spec/namespaced_settings_proxy_spec.rb +70 -0
- data/spec/settingcrazy_spec.rb +78 -0
- data/spec/settings_proxy_spec.rb +55 -0
- data/spec/settings_validator_spec.rb +121 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/models.rb +10 -0
- data/spec/support/models/campaign.rb +12 -0
- data/spec/support/models/case.rb +11 -0
- data/spec/support/models/clever_campaign.rb +13 -0
- data/spec/support/models/duck.rb +13 -0
- data/spec/support/models/farm.rb +10 -0
- data/spec/support/models/note.rb +12 -0
- data/spec/support/models/scenario.rb +13 -0
- data/spec/support/models/setting_value.rb +8 -0
- data/spec/support/models/templated_campaign.rb +11 -0
- data/spec/support/models/templated_scenario.rb +11 -0
- data/spec/support/models/vendor_instance.rb +9 -0
- data/spec/support/templates.rb +3 -0
- data/spec/support/templates/example_campaign_template.rb +24 -0
- data/spec/support/templates/example_template.rb +12 -0
- metadata +161 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SettingCrazy::NamespacedSettingsProxy do
|
4
|
+
context "no template" do
|
5
|
+
let(:model) { VendorInstance.create(:name => "VI") }
|
6
|
+
let(:namespace) { SettingCrazy::Namespace.new('google') }
|
7
|
+
subject { SettingCrazy::NamespacedSettingsProxy.new(model, namespace) }
|
8
|
+
|
9
|
+
context "single values" do
|
10
|
+
before { subject.foo = "bar"; model.save! }
|
11
|
+
its(:foo) { should == 'bar' }
|
12
|
+
its(:oth) { should be(nil) }
|
13
|
+
it { subject[:foo].should == 'bar' }
|
14
|
+
it { subject[:oth].should be(nil) }
|
15
|
+
|
16
|
+
it "should apply the namespace to the setting values" do
|
17
|
+
model.setting_values(true).first.namespace.should == 'google'
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "update a value" do
|
21
|
+
before { subject.foo = "different" }
|
22
|
+
its(:foo) { should == "different" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "multiple values" do
|
27
|
+
before do
|
28
|
+
subject.foo = %w(a b c)
|
29
|
+
model.save!
|
30
|
+
end
|
31
|
+
|
32
|
+
its(:foo) { should == [ 'a', 'b', 'c' ] }
|
33
|
+
|
34
|
+
describe "update a value" do
|
35
|
+
before do
|
36
|
+
subject.foo = %w(d e f)
|
37
|
+
end
|
38
|
+
its(:foo) { should == [ 'd', 'e', 'f' ] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'one settable object with multiple namespaces' do
|
44
|
+
let(:scenario) { Scenario.create }
|
45
|
+
subject { scenario.settings }
|
46
|
+
|
47
|
+
context 'not share setting_values' do
|
48
|
+
before do
|
49
|
+
scenario.settings.google.foo = 'bar'
|
50
|
+
scenario.save!
|
51
|
+
end
|
52
|
+
it { subject.google.inspect.should == '{:foo=>"bar"}' }
|
53
|
+
it { subject.google.foo.should == 'bar' }
|
54
|
+
it { subject.yahoo.inspect.should == '{}' }
|
55
|
+
it { subject.yahoo.foo.should be(nil) }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'has unique values' do
|
59
|
+
before do
|
60
|
+
scenario.settings.google.foo = 'bar'
|
61
|
+
scenario.settings.yahoo.foo = 'baz'
|
62
|
+
scenario.save!
|
63
|
+
end
|
64
|
+
it { subject.google.inspect.should == '{:foo=>"bar"}' }
|
65
|
+
it { subject.google.foo.should == 'bar' }
|
66
|
+
it { subject.yahoo.inspect.should == '{:foo=>"baz"}' }
|
67
|
+
it { subject.yahoo.foo.should == 'baz' }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SettingCrazy do
|
4
|
+
describe "class methods" do
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "settings" do
|
9
|
+
|
10
|
+
context "without namespaces" do
|
11
|
+
let(:model) { VendorInstance.create(:name => "VI") }
|
12
|
+
subject { model.settings }
|
13
|
+
it { should be_a(SettingCrazy::SettingsProxy) }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with namespaces" do
|
17
|
+
let(:model) { Scenario.create(:name => "Scenario") }
|
18
|
+
subject { model.settings }
|
19
|
+
it { should be_a(SettingCrazy::SettingsProxy) }
|
20
|
+
its(:google) { should be_a(SettingCrazy::SettingsProxy) }
|
21
|
+
its(:yahoo) { should be_a(SettingCrazy::SettingsProxy) }
|
22
|
+
its(:unknown) { should be_nil }
|
23
|
+
|
24
|
+
describe "setting and getting" do
|
25
|
+
before do
|
26
|
+
model.settings.google.foo = "bar"
|
27
|
+
model.save!
|
28
|
+
end
|
29
|
+
|
30
|
+
subject { model.settings.google }
|
31
|
+
its(:foo) { should == 'bar' }
|
32
|
+
|
33
|
+
it "should not be set in other namespaces" do
|
34
|
+
model.settings.yahoo.foo.should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with namespaces and templates" do
|
40
|
+
let(:model) { TemplatedScenario.create(:name => "Scenario") }
|
41
|
+
subject { model.settings }
|
42
|
+
it { should be_a(SettingCrazy::SettingsProxy) }
|
43
|
+
its(:google) { should be_a(SettingCrazy::SettingsProxy) }
|
44
|
+
its(:yahoo) { should be_a(SettingCrazy::SettingsProxy) }
|
45
|
+
its(:unknown) { should be_nil }
|
46
|
+
|
47
|
+
it "should have a template for google" do
|
48
|
+
subject.google.template.should == ExampleTemplate
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "setting and getting" do
|
52
|
+
before do
|
53
|
+
model.settings.google.foo = "bar"
|
54
|
+
model.save!
|
55
|
+
end
|
56
|
+
|
57
|
+
subject { model.settings.google }
|
58
|
+
its(:foo) { should == 'bar' }
|
59
|
+
|
60
|
+
it "should not be set in other namespaces" do
|
61
|
+
model.settings.yahoo.foo.should be_nil
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not allow settings not in the templated namespace" do
|
65
|
+
-> {
|
66
|
+
model.settings.google.not_in_template
|
67
|
+
}.should raise_error(ActiveRecord::UnknownAttributeError)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should ALLOW settings in the UNtemplated namespace" do
|
71
|
+
-> {
|
72
|
+
model.settings.yahoo.not_in_template_but_we_dont_care
|
73
|
+
}.should_not raise_error(ActiveRecord::UnknownAttributeError)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SettingCrazy::SettingsProxy do
|
4
|
+
let(:model) { VendorInstance.create(:name => "VI") }
|
5
|
+
|
6
|
+
context "no template" do
|
7
|
+
subject { SettingCrazy::SettingsProxy.new(model, nil) }
|
8
|
+
|
9
|
+
context "single values" do
|
10
|
+
before { subject.foo = "bar"; model.save! }
|
11
|
+
its(:foo) { should == 'bar' }
|
12
|
+
its(:oth) { should be(nil) }
|
13
|
+
it { subject[:foo].should == 'bar' }
|
14
|
+
it { subject[:oth].should be(nil) }
|
15
|
+
it { subject.to_hash.should == {foo: 'bar'} }
|
16
|
+
|
17
|
+
describe "update a value" do
|
18
|
+
before { subject.foo = "different" }
|
19
|
+
its(:foo) { should == "different" }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "multiple values" do
|
24
|
+
before do
|
25
|
+
subject.foo = %w(a b c)
|
26
|
+
model.save!
|
27
|
+
end
|
28
|
+
|
29
|
+
its(:foo) { should == [ 'a', 'b', 'c' ] }
|
30
|
+
|
31
|
+
describe "update a value" do
|
32
|
+
before do
|
33
|
+
subject.foo = %w(d e f)
|
34
|
+
end
|
35
|
+
its(:foo) { should == [ 'd', 'e', 'f' ] }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "a template is provided" do
|
41
|
+
let(:template) { ExampleTemplate }
|
42
|
+
subject { SettingCrazy::SettingsProxy.new(model, template) }
|
43
|
+
before { subject.foo = "1234"; model.save! }
|
44
|
+
its(:foo) { should == "1234" }
|
45
|
+
its(:bar) { should == "A string default" }
|
46
|
+
it { subject[:foo].should == "1234" }
|
47
|
+
it { subject[:bar].should == "A string default" }
|
48
|
+
|
49
|
+
it "should raise if we try to get an invalid option" do
|
50
|
+
-> {
|
51
|
+
subject.unknown
|
52
|
+
}.should raise_error(ActiveRecord::UnknownAttributeError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SettingsValidator do
|
4
|
+
context 'no template' do
|
5
|
+
context 'model not saved' do
|
6
|
+
subject { VendorInstance.new(:name => "VI") }
|
7
|
+
it { should be_valid }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'model saved' do
|
11
|
+
subject { VendorInstance.create(:name => "VI") }
|
12
|
+
it { should be_valid }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'a template is provided' do
|
17
|
+
context 'model not saved' do
|
18
|
+
subject { TemplatedCampaign.new(:name => "TemplatedCampaign") }
|
19
|
+
it { should be_valid }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'model saved' do
|
23
|
+
subject { TemplatedCampaign.create(:name => "TemplatedCampaign") }
|
24
|
+
it { should_not be_valid }
|
25
|
+
|
26
|
+
context 'validates presence' do
|
27
|
+
context 'required_key does not exist' do
|
28
|
+
before { subject.valid? }
|
29
|
+
it { subject.errors.messages[:required_key].should include("Setting, 'RequiredKey', is required") }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'required_key exists' do
|
33
|
+
before { subject.settings.required_key = 'true'; subject.save! }
|
34
|
+
it { should be_valid }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'validates singleness' do
|
39
|
+
before { subject.settings.required_key = 'true'; subject.valid? }
|
40
|
+
|
41
|
+
context 'multiple_key' do
|
42
|
+
context 'has one value' do
|
43
|
+
before { subject.settings.multiple_key = 'foo', 'bar' }
|
44
|
+
it { should be_valid }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'has more than one values' do
|
48
|
+
before { subject.settings.multiple_key = ['foo', 'bar'] }
|
49
|
+
it { should be_valid }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'single_key' do
|
54
|
+
context 'assigns one value' do
|
55
|
+
before { subject.settings.single_key = 'foo' }
|
56
|
+
it { should be_valid }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'assigns more than one values' do
|
60
|
+
before { subject.settings.single_key = ['foo', 'bar']; subject.valid? }
|
61
|
+
it { should_not be_valid }
|
62
|
+
it { subject.errors.messages[:single_key].should include("Cannot save multiple values for Setting, 'SingleKey'") }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'validates dependency' do
|
68
|
+
before { subject.settings.required_key = 'true'; subject.valid? }
|
69
|
+
|
70
|
+
context 'dependent key' do
|
71
|
+
context 'dependee value does not exist' do
|
72
|
+
before { subject.settings.dependent_key = 'BAR'; subject.valid? }
|
73
|
+
it { should_not be_valid }
|
74
|
+
it { subject.errors.messages[:dependent_key].should include("'DependentKey' can only be specified if 'DependeeKey' is set to 'bar'") }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'dependee value is not satisfied' do
|
78
|
+
before { subject.settings = {dependee_key: 'foo', dependent_key: 'BAR'}; subject.valid? }
|
79
|
+
it { should_not be_valid }
|
80
|
+
it { subject.errors.messages[:dependent_key].should include("'DependentKey' can only be specified if 'DependeeKey' is set to 'bar'") }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'dependee value is satisfied' do
|
84
|
+
before { subject.settings = {dependee_key: 'bar', dependent_key: 'BAR'} }
|
85
|
+
it { should be_valid }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'validates values' do
|
91
|
+
before { subject.settings.required_key = 'true'; subject.valid? }
|
92
|
+
|
93
|
+
context 'single key' do
|
94
|
+
context 'invalid value' do
|
95
|
+
before { subject.settings.single_key = 'foobar'; subject.valid? }
|
96
|
+
it { should_not be_valid }
|
97
|
+
it { subject.errors.messages[:single_key].should include("'foobar' is not a valid setting for 'SingleKey'") }
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'valid value' do
|
101
|
+
before { subject.settings.single_key = 'bar' }
|
102
|
+
it { should be_valid }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'multiple key' do
|
107
|
+
context 'invalid value' do
|
108
|
+
before { subject.settings.multiple_key = ['foo', 'foobar']; subject.valid? }
|
109
|
+
it { should_not be_valid }
|
110
|
+
it { subject.errors.messages[:multiple_key].should include("'foobar' is not a valid setting for 'MultipleKey'") }
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'valid value' do
|
114
|
+
before { subject.settings.multiple_key = ['foo', 'bar'] }
|
115
|
+
it { should be_valid }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'sqlite3'
|
3
|
+
|
4
|
+
root = File.expand_path("../../../", __FILE__)
|
5
|
+
ActiveRecord::Base.establish_connection(
|
6
|
+
:adapter => "sqlite3",
|
7
|
+
:database => "#{root}/db/safeattributes.db"
|
8
|
+
)
|
9
|
+
|
10
|
+
Dir["#{root}/spec/support/models/*.rb"].each { |f| require f }
|
@@ -0,0 +1,12 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'campaigns'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:campaigns) do |t|
|
3
|
+
t.string :name
|
4
|
+
t.references :scenario
|
5
|
+
end
|
6
|
+
|
7
|
+
class Campaign < ActiveRecord::Base
|
8
|
+
include SettingCrazy
|
9
|
+
belongs_to :scenario
|
10
|
+
attr_accessible :name
|
11
|
+
settings_inherit_via :scenario, :namespace => :google
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'cases'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:cases) do |t|
|
3
|
+
t.string :name
|
4
|
+
end
|
5
|
+
|
6
|
+
class Case < ActiveRecord::Base
|
7
|
+
include SettingCrazy
|
8
|
+
use_setting_template ExampleTemplate
|
9
|
+
attr_accessible :name
|
10
|
+
has_many :notes
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'clever_campaigns'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:clever_campaigns) do |t|
|
3
|
+
t.string :name
|
4
|
+
t.string :setting_namespace
|
5
|
+
t.references :scenario
|
6
|
+
end
|
7
|
+
|
8
|
+
class CleverCampaign < ActiveRecord::Base
|
9
|
+
include SettingCrazy
|
10
|
+
belongs_to :scenario
|
11
|
+
attr_accessible :name, :setting_namespace
|
12
|
+
settings_inherit_via :scenario, :namespace => Proc.new { |clever_campaign| clever_campaign.setting_namespace }
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'ducks'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:ducks) do |t|
|
3
|
+
t.string :name
|
4
|
+
t.integer :quacks
|
5
|
+
t.references :farm
|
6
|
+
end
|
7
|
+
|
8
|
+
class Duck < ActiveRecord::Base
|
9
|
+
include SettingCrazy
|
10
|
+
attr_accessible :name, :quacks
|
11
|
+
belongs_to :farm
|
12
|
+
settings_inherit_via :farm
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'notes'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:notes) do |t|
|
3
|
+
t.string :name
|
4
|
+
t.references :case
|
5
|
+
end
|
6
|
+
|
7
|
+
class Note < ActiveRecord::Base
|
8
|
+
include SettingCrazy
|
9
|
+
attr_accessible :name
|
10
|
+
belongs_to :case
|
11
|
+
settings_inherit_via :case
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'scenarios'")
|
2
|
+
ActiveRecord::Base.connection.create_table(:scenarios) do |t|
|
3
|
+
t.string :name
|
4
|
+
end
|
5
|
+
|
6
|
+
class Scenario < ActiveRecord::Base
|
7
|
+
include SettingCrazy
|
8
|
+
has_many :campaigns
|
9
|
+
has_many :clever_campaigns
|
10
|
+
attr_accessible :name
|
11
|
+
setting_namespace :google
|
12
|
+
setting_namespace :yahoo
|
13
|
+
end
|