detour 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2 -25
  3. data/app/assets/javascripts/detour/add_fields.js +7 -0
  4. data/app/assets/javascripts/detour/delete_feature.js +9 -11
  5. data/app/assets/javascripts/detour/delete_flag.js +9 -11
  6. data/app/assets/javascripts/detour/delete_group.js +5 -0
  7. data/app/assets/javascripts/detour/feature_lines.js +23 -0
  8. data/app/assets/javascripts/detour/modals.js +1 -0
  9. data/app/assets/stylesheets/detour/main.css +12 -0
  10. data/app/controllers/detour/features_controller.rb +3 -2
  11. data/app/controllers/detour/flaggable_flags_controller.rb +9 -77
  12. data/app/controllers/detour/groups_controller.rb +39 -0
  13. data/app/helpers/detour/application_helper.rb +11 -0
  14. data/app/helpers/detour/flaggable_flags_helper.rb +20 -0
  15. data/app/helpers/detour/flags_helper.rb +5 -1
  16. data/app/models/detour/concerns/keepable.rb +21 -0
  17. data/app/models/detour/concerns/matchers.rb +28 -9
  18. data/app/models/detour/database_group_flag.rb +30 -0
  19. data/app/models/detour/defined_group.rb +21 -0
  20. data/app/models/detour/defined_group_flag.rb +28 -0
  21. data/app/models/detour/feature.rb +4 -3
  22. data/app/models/detour/flag_in_flag.rb +1 -8
  23. data/app/models/detour/flaggable_flag.rb +24 -0
  24. data/app/models/detour/group.rb +17 -0
  25. data/app/models/detour/membership.rb +41 -0
  26. data/app/models/detour/opt_out_flag.rb +1 -8
  27. data/app/views/detour/flaggable_flags/_flaggable_flag_fields.html.erb +19 -0
  28. data/app/views/detour/flaggable_flags/index.html.erb +13 -26
  29. data/app/views/detour/flags/_feature_form.html.erb +12 -3
  30. data/app/views/detour/flags/index.html.erb +7 -2
  31. data/app/views/detour/groups/_group.html.erb +3 -0
  32. data/app/views/detour/groups/_membership_fields.html.erb +19 -0
  33. data/app/views/detour/groups/index.html.erb +21 -0
  34. data/app/views/detour/groups/show.html.erb +41 -0
  35. data/app/views/detour/memberships/_membership.html.erb +4 -0
  36. data/app/views/detour/{features → shared}/_errors.html.erb +2 -2
  37. data/app/views/detour/shared/_nav.html.erb +1 -0
  38. data/app/views/detour/shared/error.js.erb +5 -0
  39. data/config/locales/en.yml +11 -0
  40. data/config/routes.rb +8 -7
  41. data/detour.gemspec +1 -0
  42. data/lib/detour/acts_as_flaggable.rb +19 -4
  43. data/lib/detour/configuration.rb +1 -1
  44. data/lib/detour/flag_form.rb +53 -34
  45. data/lib/detour/flaggable.rb +0 -19
  46. data/lib/detour/version.rb +1 -1
  47. data/lib/generators/templates/migration.rb +21 -1
  48. data/lib/tasks/.gitkeep +0 -0
  49. data/spec/controllers/detour/flaggable_flags_controller_spec.rb +30 -67
  50. data/spec/controllers/detour/groups_controller_spec.rb +107 -0
  51. data/spec/dummy/db/migrate/20131221052201_setup_detour.rb +21 -1
  52. data/spec/dummy/db/schema.rb +20 -1
  53. data/spec/factories/database_group_flag.rb +7 -0
  54. data/spec/factories/{group_flag.rb → defined_group_flag.rb} +1 -1
  55. data/spec/factories/group.rb +10 -0
  56. data/spec/factories/membership.rb +6 -0
  57. data/spec/features/database_group_flags_spec.rb +50 -0
  58. data/spec/features/database_groups_spec.rb +174 -0
  59. data/spec/features/defined_group_flags_spec.rb +67 -0
  60. data/spec/features/features_spec.rb +44 -0
  61. data/spec/features/flag_in_flags_spec.rb +22 -60
  62. data/spec/features/opt_out_flags_spec.rb +34 -59
  63. data/spec/integration/group_rollout_spec.rb +2 -2
  64. data/spec/lib/detour/acts_as_flaggable_spec.rb +12 -3
  65. data/spec/lib/detour/configuration_spec.rb +6 -2
  66. data/spec/lib/detour/flag_form_spec.rb +0 -11
  67. data/spec/lib/detour/flaggable_spec.rb +1 -54
  68. data/spec/models/detour/database_group_flag_spec.rb +29 -0
  69. data/spec/models/detour/defined_group_spec.rb +21 -0
  70. data/spec/models/detour/feature_spec.rb +57 -119
  71. data/spec/models/detour/flag_in_flag_spec.rb +1 -4
  72. data/spec/models/detour/flaggable_flag_spec.rb +25 -0
  73. data/spec/models/detour/group_flag_spec.rb +1 -1
  74. data/spec/models/detour/membership_spec.rb +58 -0
  75. data/spec/models/detour/opt_out_flag_spec.rb +1 -4
  76. data/spec/spec_helper.rb +4 -0
  77. metadata +97 -81
  78. data/app/models/detour/concerns/flag_actions.rb +0 -141
  79. data/app/models/detour/group_flag.rb +0 -13
  80. data/app/views/detour/features/_success.html.erb +0 -1
  81. data/app/views/detour/features/error.js.erb +0 -5
  82. data/lib/tasks/detour.rake +0 -119
  83. data/spec/features/group_flags_spec.rb +0 -49
  84. data/spec/integration/flag_rollout_spec.rb +0 -27
  85. data/spec/lib/tasks/detour_spec.rb +0 -162
  86. /data/app/views/detour/{features → shared}/success.js.erb +0 -0
@@ -3,7 +3,8 @@ require "fakefs/spec_helpers"
3
3
 
4
4
  describe Detour::Feature do
5
5
  it { should have_many(:flag_in_flags) }
6
- it { should have_many(:group_flags) }
6
+ it { should have_many(:database_group_flags) }
7
+ it { should have_many(:defined_group_flags) }
7
8
  it { should have_many(:percentage_flags) }
8
9
  it { should have_many(:opt_out_flags) }
9
10
  it { should have_many(:flags).dependent(:destroy) }
@@ -11,6 +12,20 @@ describe Detour::Feature do
11
12
  it { should validate_uniqueness_of :name }
12
13
  it { should allow_mass_assignment_of :name }
13
14
 
15
+ it { should allow_value("foo").for(:name) }
16
+ it { should allow_value("foo_bar").for(:name) }
17
+ it { should allow_value("foo-bar").for(:name) }
18
+ it { should_not allow_value("foo-bar.").for(:name) }
19
+
20
+ describe "name format validation" do
21
+ let(:feature) { build :feature, name: "foo bar." }
22
+
23
+ it "returns a readable format error" do
24
+ feature.valid?
25
+ feature.errors[:name].should eq ["must be composed of letters, numbers, underscores, and dashes"]
26
+ end
27
+ end
28
+
14
29
  describe ".with_lines" do
15
30
  include FakeFS::SpecHelpers
16
31
 
@@ -55,116 +70,6 @@ describe Detour::Feature do
55
70
  end
56
71
  end
57
72
 
58
- describe ".add_record_to_feature" do
59
- let(:user) { create :user }
60
- let(:feature) { create :feature }
61
-
62
- before do
63
- Detour::Feature.add_record_to_feature user, feature.name
64
- end
65
-
66
- it "creates a flag for the given instance and feature" do
67
- user.features.should include feature
68
- end
69
- end
70
-
71
- describe ".remove_record_from_feature" do
72
- let(:user) { create :user }
73
- let(:feature) { create :feature }
74
-
75
- before do
76
- Detour::Feature.add_record_to_feature user, feature.name
77
- Detour::Feature.remove_record_from_feature user, feature.name
78
- end
79
-
80
- it "creates a flag for the given instance and feature" do
81
- user.features.should_not include feature
82
- end
83
- end
84
-
85
- describe ".opt_record_out_of_feature" do
86
- let(:user) { create :user }
87
- let(:feature) { create :feature }
88
-
89
- before do
90
- Detour::Feature.add_percentage_to_feature user.class.to_s, 100, feature.name
91
- Detour::Feature.opt_record_out_of_feature user, feature.name
92
- end
93
-
94
- it "opts the record out of the feature" do
95
- user.has_feature?(feature.name).should be_false
96
- end
97
- end
98
-
99
- describe ".un_opt_record_out_of_feature" do
100
- let(:user) { create :user }
101
- let(:feature) { create :feature }
102
-
103
- before do
104
- Detour::Feature.add_percentage_to_feature user.class.to_s, 100, feature.name
105
- Detour::Feature.opt_record_out_of_feature user, feature.name
106
- Detour::Feature.un_opt_record_out_of_feature user, feature.name
107
- end
108
-
109
- it "opts the record out of the feature" do
110
- user.has_feature?(feature.name).should be_true
111
- end
112
- end
113
-
114
- describe ".add_group_to_feature" do
115
- let(:feature) { create :feature }
116
-
117
- before do
118
- Detour.config.define_user_group :bar do
119
- end
120
- Detour::Feature.add_group_to_feature "User", :bar, feature.name
121
- end
122
-
123
- it "creates a flag for the given group and feature" do
124
- feature.group_flags.where(flaggable_type: "User", group_name: "bar").first.should_not be_nil
125
- end
126
- end
127
-
128
- describe ".remove_group_from_feature" do
129
- let(:feature) { create :feature }
130
-
131
- before do
132
- Detour.config.define_user_group :bar do
133
- end
134
- Detour::Feature.add_group_to_feature "User", :bar, feature.name
135
- Detour::Feature.remove_group_from_feature "User", :bar, feature.name
136
- end
137
-
138
- it "destroys flags for the given group and feature" do
139
- feature.group_flags.where(flaggable_type: "User", group_name: "bar").first.should be_nil
140
- end
141
- end
142
-
143
- describe ".add_percentage_to_feature" do
144
- let(:feature) { create :feature }
145
-
146
- before do
147
- Detour::Feature.add_percentage_to_feature "User", 50, feature.name
148
- end
149
-
150
- it "creates a flag for the given percentage and feature" do
151
- feature.percentage_flags.where(flaggable_type: "User", percentage: 50).first.should_not be_nil
152
- end
153
- end
154
-
155
- describe ".remove_percentage_from_feature" do
156
- let(:feature) { create :feature }
157
-
158
- before do
159
- Detour::Feature.add_percentage_to_feature "User", 50, feature.name
160
- Detour::Feature.remove_percentage_from_feature "User", feature.name
161
- end
162
-
163
- it "creates a flag for the given percentage and feature" do
164
- feature.percentage_flags.where(flaggable_type: "User").first.should be_nil
165
- end
166
- end
167
-
168
73
  describe "#to_s" do
169
74
  let(:feature) { create :feature }
170
75
 
@@ -187,8 +92,13 @@ describe Detour::Feature do
187
92
  feature.match?(user)
188
93
  end
189
94
 
190
- it "checks if the user is flagged as part of a group" do
191
- feature.should_receive(:match_groups?).with(user)
95
+ it "checks if the user is flagged as part of a database group" do
96
+ feature.should_receive(:match_database_groups?).with(user)
97
+ feature.match?(user)
98
+ end
99
+
100
+ it "checks if the user is flagged as part of a defined group" do
101
+ feature.should_receive(:match_defined_groups?).with(user)
192
102
  feature.match?(user)
193
103
  end
194
104
  end
@@ -236,7 +146,7 @@ describe Detour::Feature do
236
146
  let(:feature) { create :feature }
237
147
 
238
148
  before do
239
- Detour::Feature.add_record_to_feature user, feature.name
149
+ create(:flag_in_flag, flaggable: user, feature: feature)
240
150
  end
241
151
 
242
152
  context "when the feature exists for the instance" do
@@ -272,12 +182,40 @@ describe Detour::Feature do
272
182
  end
273
183
  end
274
184
 
275
- describe "#match_groups?" do
185
+ describe "#match_database_group?" do
186
+ let(:user) { create :user, name: "foo" }
187
+ let(:user2) { create :user }
188
+ let(:widget) { create :widget }
189
+ let(:feature) { create :feature }
190
+ let(:group) { create :group }
191
+ let!(:membership) { create :membership, group: group, member: user }
192
+ let!(:flag) { create :database_group_flag, feature: feature, flaggable_type: user.class.to_s, group: group }
193
+
194
+ context "when the instance is in the group" do
195
+ it "returns true" do
196
+ feature.match_database_groups?(user).should be_true
197
+ end
198
+ end
199
+
200
+ context "when the instance is not in the group" do
201
+ it "returns false" do
202
+ feature.match_database_groups?(user2).should be_false
203
+ end
204
+ end
205
+
206
+ context "when the instance is not of the type of the group" do
207
+ it "returns false" do
208
+ feature.match_database_groups?(widget).should be_false
209
+ end
210
+ end
211
+ end
212
+
213
+ describe "#match_defined_groups?" do
276
214
  let(:user) { create :user, name: "foo" }
277
215
  let(:user2) { create :user }
278
216
  let(:widget) { create :widget }
279
217
  let(:feature) { create :feature }
280
- let!(:flag) { create :group_flag, feature: feature, flaggable_type: user.class.to_s, group_name: "foo_users" }
218
+ let!(:flag) { create :defined_group_flag, feature: feature, flaggable_type: user.class.to_s, group_name: "foo_users" }
281
219
 
282
220
  before do
283
221
  Detour.config.define_user_group "foo_users" do |user|
@@ -287,19 +225,19 @@ describe Detour::Feature do
287
225
 
288
226
  context "when the instance matches the block" do
289
227
  it "returns true" do
290
- feature.match_groups?(user).should be_true
228
+ feature.match_defined_groups?(user).should be_true
291
229
  end
292
230
  end
293
231
 
294
232
  context "when the instance does not match the block" do
295
233
  it "returns false" do
296
- feature.match_groups?(user2).should be_false
234
+ feature.match_defined_groups?(user2).should be_false
297
235
  end
298
236
  end
299
237
 
300
238
  context "when the instance is not of the type of the block" do
301
239
  it "returns false" do
302
- feature.match_groups?(widget).should be_false
240
+ feature.match_defined_groups?(widget).should be_false
303
241
  end
304
242
  end
305
243
  end
@@ -1,10 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Detour::FlagInFlag do
4
- it { should be_a Detour::Flag }
5
- it { should belong_to :flaggable }
6
- it { should validate_presence_of :flaggable }
7
- it { should allow_mass_assignment_of :flaggable }
4
+ it { should be_a Detour::FlaggableFlag }
8
5
 
9
6
  it "validates uniquness of feature_id on flaggable" do
10
7
  user = create :user
@@ -0,0 +1,25 @@
1
+ require "spec_helper"
2
+
3
+ describe Detour::FlaggableFlag do
4
+ it { should belong_to :flaggable }
5
+ it { should validate_presence_of :flaggable }
6
+ it { should allow_mass_assignment_of :flaggable }
7
+
8
+ describe "before validating" do
9
+ it "sets its flaggable" do
10
+ feature = create(:feature)
11
+ user = create(:user)
12
+ flag = create(:flag_in_flag, flaggable: nil, feature: feature, flaggable_key: user.id, flaggable_type: "User")
13
+ flag.flaggable_id.should eq user.id
14
+ end
15
+
16
+ context "when the flaggable is not found" do
17
+ let(:flag) { build :flag_in_flag, flaggable: nil, flaggable_key: 1, flaggable_type: "User" }
18
+
19
+ it "is invalidated" do
20
+ flag.valid?
21
+ flag.errors["User"].should eq ['"1" could not be found']
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Detour::GroupFlag do
3
+ describe Detour::DefinedGroupFlag do
4
4
  it { should be_a Detour::Flag }
5
5
  it { should validate_presence_of :group_name }
6
6
  it { should allow_mass_assignment_of :group_name }
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe Detour::Membership do
4
+ subject { create :membership }
5
+
6
+ it { should validate_presence_of :group_id }
7
+ it { should validate_presence_of :member_id }
8
+ it { should validate_presence_of :member_type }
9
+ it { should validate_uniqueness_of(:member_id).scoped_to(:group_id) }
10
+
11
+ it { should allow_mass_assignment_of :group_id }
12
+ it { should allow_mass_assignment_of :member_key }
13
+ it { should allow_mass_assignment_of :member_type }
14
+
15
+ it { should belong_to :group }
16
+ it { should belong_to(:member) }
17
+
18
+ describe "before validating" do
19
+ it "sets its member" do
20
+ group = create(:group, flaggable_type: "User")
21
+ user = create(:user)
22
+ membership = create(:membership, member: nil, group: group, member_key: user.id)
23
+ membership.member_id.should eq user.id
24
+ end
25
+
26
+ context "when the member is not found" do
27
+ let(:membership) { build :membership, member: nil, member_key: 1 }
28
+
29
+ it "is invalidated" do
30
+ membership.valid?
31
+ membership.errors["User"].should eq ['"1" could not be found']
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "validate #member_type" do
37
+ let(:group) { create :group, flaggable_type: "User" }
38
+
39
+ context "when its member_type matches its group's flaggable_type" do
40
+ let(:user) { create :user }
41
+ let(:membership) { build :membership, group: group, member: user }
42
+
43
+ it "is valid" do
44
+ membership.should be_valid
45
+ end
46
+ end
47
+
48
+ context "when its member_type does not match its group's flaggable_type" do
49
+ let(:widget) { create :widget }
50
+ let(:membership) { build :membership, group: group, member: widget }
51
+
52
+ it "is invalid" do
53
+ membership.valid?
54
+ membership.errors[:member_type].should eq ["must match the group's flaggable_type"]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,10 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Detour::OptOutFlag do
4
- it { should be_a Detour::Flag }
5
- it { should belong_to :flaggable }
6
- it { should validate_presence_of :flaggable }
7
- it { should allow_mass_assignment_of :flaggable }
4
+ it { should be_a Detour::FlaggableFlag }
8
5
 
9
6
  it "validates uniquness of feature_id on flaggable" do
10
7
  user = create :user
data/spec/spec_helper.rb CHANGED
@@ -21,9 +21,12 @@ RSpec.configure do |config|
21
21
  config.mock_with :rspec
22
22
  config.use_transactional_fixtures = false
23
23
  config.infer_base_class_for_anonymous_controllers = false
24
+ config.color = true
24
25
  config.order = "random"
25
26
  config.include FactoryGirl::Syntax::Methods
26
27
 
28
+ I18n.enforce_available_locales = false
29
+
27
30
  config.after :each do
28
31
  Rake::Task.tasks.each { |t| t.reenable }
29
32
  end
@@ -50,6 +53,7 @@ RSpec.configure do |config|
50
53
 
51
54
  config.before :each do
52
55
  User.instance_variable_set "@detour_flaggable_find_by", :id
56
+ Detour.config.instance_variable_set "@defined_groups", {}
53
57
  end
54
58
 
55
59
  config.after :each do