settingcrazy 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|