settingcrazy 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +17 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +245 -0
  6. data/Rakefile +2 -0
  7. data/TODO +10 -0
  8. data/db/safeattributes.db +0 -0
  9. data/lib/generators/settingcrazy/setting_values_migration/setting_values_migration_generator.rb +18 -0
  10. data/lib/generators/settingcrazy/setting_values_migration/templates/migration.rb +15 -0
  11. data/lib/settingcrazy.rb +23 -0
  12. data/lib/settingcrazy/class_methods.rb +32 -0
  13. data/lib/settingcrazy/inheritor.rb +31 -0
  14. data/lib/settingcrazy/instance_methods.rb +20 -0
  15. data/lib/settingcrazy/namespace.rb +14 -0
  16. data/lib/settingcrazy/namespaced_settings_proxy.rb +18 -0
  17. data/lib/settingcrazy/setting_value.rb +11 -0
  18. data/lib/settingcrazy/settings_proxy.rb +107 -0
  19. data/lib/settingcrazy/settings_validator.rb +43 -0
  20. data/lib/settingcrazy/template.rb +2 -0
  21. data/lib/settingcrazy/template/base.rb +66 -0
  22. data/lib/settingcrazy/template/enum.rb +19 -0
  23. data/lib/settingcrazy/version.rb +3 -0
  24. data/settingcrazy.gemspec +21 -0
  25. data/spec/inheritance_spec.rb +76 -0
  26. data/spec/mass_assignment_spec.rb +69 -0
  27. data/spec/namespaced_settings_proxy_spec.rb +70 -0
  28. data/spec/settingcrazy_spec.rb +78 -0
  29. data/spec/settings_proxy_spec.rb +55 -0
  30. data/spec/settings_validator_spec.rb +121 -0
  31. data/spec/spec_helper.rb +8 -0
  32. data/spec/support/models.rb +10 -0
  33. data/spec/support/models/campaign.rb +12 -0
  34. data/spec/support/models/case.rb +11 -0
  35. data/spec/support/models/clever_campaign.rb +13 -0
  36. data/spec/support/models/duck.rb +13 -0
  37. data/spec/support/models/farm.rb +10 -0
  38. data/spec/support/models/note.rb +12 -0
  39. data/spec/support/models/scenario.rb +13 -0
  40. data/spec/support/models/setting_value.rb +8 -0
  41. data/spec/support/models/templated_campaign.rb +11 -0
  42. data/spec/support/models/templated_scenario.rb +11 -0
  43. data/spec/support/models/vendor_instance.rb +9 -0
  44. data/spec/support/templates.rb +3 -0
  45. data/spec/support/templates/example_campaign_template.rb +24 -0
  46. data/spec/support/templates/example_template.rb +12 -0
  47. 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
@@ -0,0 +1,8 @@
1
+ require File.expand_path("../../lib/settingcrazy", __FILE__)
2
+
3
+ require 'support/templates'
4
+ require 'support/models'
5
+
6
+ RSpec.configure do |config|
7
+ # config.mock_with :rr
8
+ end
@@ -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,10 @@
1
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'farms'")
2
+ ActiveRecord::Base.connection.create_table(:farms) do |t|
3
+ t.string :name
4
+ end
5
+
6
+ class Farm < ActiveRecord::Base
7
+ include SettingCrazy
8
+ attr_accessible :name
9
+ has_many :ducks
10
+ 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