detour 0.0.3 → 0.0.5
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.
- checksums.yaml +7 -0
- data/README.md +2 -25
- data/app/assets/javascripts/detour/add_fields.js +7 -0
- data/app/assets/javascripts/detour/delete_feature.js +9 -11
- data/app/assets/javascripts/detour/delete_flag.js +9 -11
- data/app/assets/javascripts/detour/delete_group.js +5 -0
- data/app/assets/javascripts/detour/feature_lines.js +23 -0
- data/app/assets/javascripts/detour/modals.js +1 -0
- data/app/assets/stylesheets/detour/main.css +12 -0
- data/app/controllers/detour/features_controller.rb +3 -2
- data/app/controllers/detour/flaggable_flags_controller.rb +9 -77
- data/app/controllers/detour/groups_controller.rb +39 -0
- data/app/helpers/detour/application_helper.rb +11 -0
- data/app/helpers/detour/flaggable_flags_helper.rb +20 -0
- data/app/helpers/detour/flags_helper.rb +5 -1
- data/app/models/detour/concerns/keepable.rb +21 -0
- data/app/models/detour/concerns/matchers.rb +28 -9
- data/app/models/detour/database_group_flag.rb +30 -0
- data/app/models/detour/defined_group.rb +21 -0
- data/app/models/detour/defined_group_flag.rb +28 -0
- data/app/models/detour/feature.rb +4 -3
- data/app/models/detour/flag_in_flag.rb +1 -8
- data/app/models/detour/flaggable_flag.rb +24 -0
- data/app/models/detour/group.rb +17 -0
- data/app/models/detour/membership.rb +41 -0
- data/app/models/detour/opt_out_flag.rb +1 -8
- data/app/views/detour/flaggable_flags/_flaggable_flag_fields.html.erb +19 -0
- data/app/views/detour/flaggable_flags/index.html.erb +13 -26
- data/app/views/detour/flags/_feature_form.html.erb +12 -3
- data/app/views/detour/flags/index.html.erb +7 -2
- data/app/views/detour/groups/_group.html.erb +3 -0
- data/app/views/detour/groups/_membership_fields.html.erb +19 -0
- data/app/views/detour/groups/index.html.erb +21 -0
- data/app/views/detour/groups/show.html.erb +41 -0
- data/app/views/detour/memberships/_membership.html.erb +4 -0
- data/app/views/detour/{features → shared}/_errors.html.erb +2 -2
- data/app/views/detour/shared/_nav.html.erb +1 -0
- data/app/views/detour/shared/error.js.erb +5 -0
- data/config/locales/en.yml +11 -0
- data/config/routes.rb +8 -7
- data/detour.gemspec +1 -0
- data/lib/detour/acts_as_flaggable.rb +19 -4
- data/lib/detour/configuration.rb +1 -1
- data/lib/detour/flag_form.rb +53 -34
- data/lib/detour/flaggable.rb +0 -19
- data/lib/detour/version.rb +1 -1
- data/lib/generators/templates/migration.rb +21 -1
- data/lib/tasks/.gitkeep +0 -0
- data/spec/controllers/detour/flaggable_flags_controller_spec.rb +30 -67
- data/spec/controllers/detour/groups_controller_spec.rb +107 -0
- data/spec/dummy/db/migrate/20131221052201_setup_detour.rb +21 -1
- data/spec/dummy/db/schema.rb +20 -1
- data/spec/factories/database_group_flag.rb +7 -0
- data/spec/factories/{group_flag.rb → defined_group_flag.rb} +1 -1
- data/spec/factories/group.rb +10 -0
- data/spec/factories/membership.rb +6 -0
- data/spec/features/database_group_flags_spec.rb +50 -0
- data/spec/features/database_groups_spec.rb +174 -0
- data/spec/features/defined_group_flags_spec.rb +67 -0
- data/spec/features/features_spec.rb +44 -0
- data/spec/features/flag_in_flags_spec.rb +22 -60
- data/spec/features/opt_out_flags_spec.rb +34 -59
- data/spec/integration/group_rollout_spec.rb +2 -2
- data/spec/lib/detour/acts_as_flaggable_spec.rb +12 -3
- data/spec/lib/detour/configuration_spec.rb +6 -2
- data/spec/lib/detour/flag_form_spec.rb +0 -11
- data/spec/lib/detour/flaggable_spec.rb +1 -54
- data/spec/models/detour/database_group_flag_spec.rb +29 -0
- data/spec/models/detour/defined_group_spec.rb +21 -0
- data/spec/models/detour/feature_spec.rb +57 -119
- data/spec/models/detour/flag_in_flag_spec.rb +1 -4
- data/spec/models/detour/flaggable_flag_spec.rb +25 -0
- data/spec/models/detour/group_flag_spec.rb +1 -1
- data/spec/models/detour/membership_spec.rb +58 -0
- data/spec/models/detour/opt_out_flag_spec.rb +1 -4
- data/spec/spec_helper.rb +4 -0
- metadata +97 -81
- data/app/models/detour/concerns/flag_actions.rb +0 -141
- data/app/models/detour/group_flag.rb +0 -13
- data/app/views/detour/features/_success.html.erb +0 -1
- data/app/views/detour/features/error.js.erb +0 -5
- data/lib/tasks/detour.rake +0 -119
- data/spec/features/group_flags_spec.rb +0 -49
- data/spec/integration/flag_rollout_spec.rb +0 -27
- data/spec/lib/tasks/detour_spec.rb +0 -162
- /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(:
|
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(:
|
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
|
-
|
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 "#
|
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 :
|
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.
|
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.
|
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.
|
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::
|
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
|
@@ -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::
|
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
|