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.
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