detour 0.0.1
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.
- data/.gitignore +18 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +261 -0
- data/Rakefile +1 -0
- data/detour.gemspec +33 -0
- data/lib/detour/acts_as_flaggable.rb +36 -0
- data/lib/detour/feature.rb +312 -0
- data/lib/detour/flag.rb +13 -0
- data/lib/detour/flaggable.rb +66 -0
- data/lib/detour/flaggable_flag.rb +10 -0
- data/lib/detour/group_flag.rb +8 -0
- data/lib/detour/opt_out_flag.rb +10 -0
- data/lib/detour/percentage_flag.rb +11 -0
- data/lib/detour/version.rb +3 -0
- data/lib/detour.rb +35 -0
- data/lib/generators/active_record_rollout_generator.rb +20 -0
- data/lib/generators/templates/active_record_rollout.rb +5 -0
- data/lib/generators/templates/migration.rb +30 -0
- data/lib/tasks/detour.rake +119 -0
- data/spec/integration/flag_rollout_spec.rb +27 -0
- data/spec/integration/group_rollout_spec.rb +20 -0
- data/spec/integration/percentage_rollout_spec.rb +13 -0
- data/spec/lib/active_record/rollout/acts_as_flaggable_spec.rb +31 -0
- data/spec/lib/active_record/rollout/feature_spec.rb +280 -0
- data/spec/lib/active_record/rollout/flag_spec.rb +8 -0
- data/spec/lib/active_record/rollout/flaggable_flag_spec.rb +9 -0
- data/spec/lib/active_record/rollout/flaggable_spec.rb +149 -0
- data/spec/lib/active_record/rollout/group_flag_spec.rb +8 -0
- data/spec/lib/active_record/rollout/opt_out_flag_spec.rb +9 -0
- data/spec/lib/active_record/rollout/percentage_flag_spec.rb +10 -0
- data/spec/lib/tasks/detour_rake_spec.rb +162 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/schema.rb +13 -0
- data/spec/support/shared_contexts/rake.rb +20 -0
- metadata +258 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fakefs/spec_helpers"
|
3
|
+
|
4
|
+
describe Detour::Feature do
|
5
|
+
it { should have_many(:flaggable_flags) }
|
6
|
+
it { should have_many(:group_flags) }
|
7
|
+
it { should have_many(:percentage_flags) }
|
8
|
+
it { should have_many(:opt_out_flags) }
|
9
|
+
it { should have_many(:flags).dependent(:destroy) }
|
10
|
+
it { should validate_presence_of :name }
|
11
|
+
it { should validate_uniqueness_of :name }
|
12
|
+
it { should allow_mass_assignment_of :name }
|
13
|
+
|
14
|
+
describe ".all_with_lines" do
|
15
|
+
include FakeFS::SpecHelpers
|
16
|
+
|
17
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
18
|
+
|
19
|
+
before do
|
20
|
+
Detour::Feature.grep_dirs = ["/foo/**/*.rb"]
|
21
|
+
|
22
|
+
FileUtils.mkdir("/foo")
|
23
|
+
|
24
|
+
File.open("/foo/bar.rb", "w") do |file|
|
25
|
+
file.write <<-EOF
|
26
|
+
current_user.has_feature?(:foo) do
|
27
|
+
end
|
28
|
+
|
29
|
+
current_user.has_feature?(:bar) do
|
30
|
+
end
|
31
|
+
|
32
|
+
current_user.has_feature?(:foo) do
|
33
|
+
end
|
34
|
+
EOF
|
35
|
+
end
|
36
|
+
|
37
|
+
File.open("/foo/baz.rb", "w") do |file|
|
38
|
+
file.write <<-EOF
|
39
|
+
current_user.has_feature?(:foo) || current_user.has_feature?(:bar)
|
40
|
+
|
41
|
+
current_user.has_feature? :bar do
|
42
|
+
end
|
43
|
+
EOF
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "fetches lines for persisted features" do
|
48
|
+
persisted_feature = Detour::Feature.all_with_lines.detect { |f| f.name == feature.name }
|
49
|
+
persisted_feature.lines.should eq %w[/foo/bar.rb#L1 /foo/bar.rb#L7 /foo/baz.rb#L1]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "fetches lines for un-persisted features" do
|
53
|
+
unpersisted_feature = Detour::Feature.all_with_lines.detect { |f| f.name == "bar" }
|
54
|
+
unpersisted_feature.lines.should eq %w[/foo/bar.rb#L4 /foo/baz.rb#L1 /foo/baz.rb#L3]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe ".define_{klass}_group" do
|
59
|
+
let(:block) { Proc.new {} }
|
60
|
+
it "defines a group for the given class" do
|
61
|
+
Detour::Feature.should_receive(:define_group_for_class).with("User", :id_is_1)
|
62
|
+
Detour::Feature.define_user_group :id_is_1, block
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".add_record_to_feature" do
|
67
|
+
let(:user) { User.create }
|
68
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
69
|
+
|
70
|
+
before do
|
71
|
+
Detour::Feature.add_record_to_feature user, :foo
|
72
|
+
end
|
73
|
+
|
74
|
+
it "creates a flag for the given instance and feature" do
|
75
|
+
user.features.should include feature
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ".remove_record_from_feature" do
|
80
|
+
let(:user) { User.create }
|
81
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
82
|
+
|
83
|
+
before do
|
84
|
+
Detour::Feature.add_record_to_feature user, :foo
|
85
|
+
Detour::Feature.remove_record_from_feature user, :foo
|
86
|
+
end
|
87
|
+
|
88
|
+
it "creates a flag for the given instance and feature" do
|
89
|
+
user.features.should_not include feature
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe ".opt_record_out_of_feature" do
|
94
|
+
let(:user) { User.create }
|
95
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
96
|
+
|
97
|
+
before do
|
98
|
+
Detour::Feature.add_percentage_to_feature "User", 100, "foo"
|
99
|
+
Detour::Feature.opt_record_out_of_feature user, "foo"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "opts the record out of the feature" do
|
103
|
+
user.has_feature?("foo").should be_false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe ".un_opt_record_out_of_feature" do
|
108
|
+
let(:user) { User.create }
|
109
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
110
|
+
|
111
|
+
before do
|
112
|
+
Detour::Feature.add_percentage_to_feature "User", 100, "foo"
|
113
|
+
Detour::Feature.opt_record_out_of_feature user, "foo"
|
114
|
+
Detour::Feature.un_opt_record_out_of_feature user, "foo"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "opts the record out of the feature" do
|
118
|
+
user.has_feature?("foo").should be_true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe ".add_group_to_feature" do
|
123
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
124
|
+
|
125
|
+
before do
|
126
|
+
Detour::Feature.define_user_group :bar do
|
127
|
+
end
|
128
|
+
Detour::Feature.add_group_to_feature "User", :bar, "foo"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "creates a flag for the given group and feature" do
|
132
|
+
feature.group_flags.where(flaggable_type: "User", group_name: "bar").first.should_not be_nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe ".remove_group_from_feature" do
|
137
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
138
|
+
|
139
|
+
before do
|
140
|
+
Detour::Feature.define_user_group :bar do
|
141
|
+
end
|
142
|
+
Detour::Feature.add_group_to_feature "User", :bar, "foo"
|
143
|
+
Detour::Feature.remove_group_from_feature "User", :bar, "foo"
|
144
|
+
end
|
145
|
+
|
146
|
+
it "destroys flags for the given group and feature" do
|
147
|
+
feature.group_flags.where(flaggable_type: "User", group_name: "bar").first.should be_nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe ".add_percentage_to_feature" do
|
152
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
153
|
+
|
154
|
+
before do
|
155
|
+
Detour::Feature.add_percentage_to_feature "User", 50, "foo"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "creates a flag for the given percentage and feature" do
|
159
|
+
feature.percentage_flags.where(flaggable_type: "User", percentage: 50).first.should_not be_nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe ".remove_percentage_from_feature" do
|
164
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
165
|
+
|
166
|
+
before do
|
167
|
+
Detour::Feature.add_percentage_to_feature "User", 50, "foo"
|
168
|
+
Detour::Feature.remove_percentage_from_feature "User", "foo"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "creates a flag for the given percentage and feature" do
|
172
|
+
feature.percentage_flags.where(flaggable_type: "User").first.should be_nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe ".define_group_for_class" do
|
177
|
+
let(:block) { Proc.new {} }
|
178
|
+
before do
|
179
|
+
Detour::Feature.send :define_group_for_class, "User", "user_id_1", &block
|
180
|
+
end
|
181
|
+
|
182
|
+
it "defines a group for the given class" do
|
183
|
+
Detour::Feature.defined_groups["User"].should eq({ "user_id_1" => block })
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "#match?" do
|
188
|
+
let(:user) { User.create }
|
189
|
+
let(:feature) { Detour::Feature.create(name: "foo") }
|
190
|
+
|
191
|
+
it "checks if the user is flagged individually" do
|
192
|
+
feature.should_receive(:match_id?).with(user)
|
193
|
+
feature.match?(user)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "checks if the user is flagged as part of a percentage" do
|
197
|
+
feature.should_receive(:match_percentage?).with(user)
|
198
|
+
feature.match?(user)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "checks if the user is flagged as part of a group" do
|
202
|
+
feature.should_receive(:match_groups?).with(user)
|
203
|
+
feature.match?(user)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "#match_id?" do
|
208
|
+
let(:user) { User.create }
|
209
|
+
let(:user2) { User.create }
|
210
|
+
let!(:feature) { Detour::Feature.create!(name: "foo") }
|
211
|
+
|
212
|
+
before do
|
213
|
+
Detour::Feature.add_record_to_feature user, :foo
|
214
|
+
end
|
215
|
+
|
216
|
+
context "when the feature exists for the instance" do
|
217
|
+
it "returns true" do
|
218
|
+
feature.match_id?(user).should be_true
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context "when the feature does not exist for the instance" do
|
223
|
+
it "returns false" do
|
224
|
+
feature.match_id?(user2).should be_false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#match_percentage?" do
|
230
|
+
let(:user) { User.create }
|
231
|
+
let(:feature) { Detour::Feature.create!(name: "foo") }
|
232
|
+
let!(:flag) { feature.percentage_flags.create(flaggable_type: "User", percentage: 50) }
|
233
|
+
|
234
|
+
context "when the user's ID matches `id % 10 < percentage / 10" do
|
235
|
+
it "returns true" do
|
236
|
+
user.stub(:id) { 1 }
|
237
|
+
feature.match_percentage?(user).should be_true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when the user's ID does not match `id % 10 < percentage / 10" do
|
242
|
+
it "returns false" do
|
243
|
+
user.stub(:id) { 5 }
|
244
|
+
feature.match_percentage?(user).should be_false
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "#match_groups?" do
|
250
|
+
let!(:user) { User.create(name: "foo") }
|
251
|
+
let!(:user2) { User.create(name: "bar") }
|
252
|
+
let!(:organization) { Organization.create(name: "foo") }
|
253
|
+
let(:feature) { Detour::Feature.create!(name: "baz") }
|
254
|
+
let!(:flag) { feature.group_flags.create(flaggable_type: "User", group_name: "foo_users") }
|
255
|
+
|
256
|
+
before do
|
257
|
+
Detour::Feature.define_user_group "foo_users" do |user|
|
258
|
+
user.name == "foo"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "when the instance matches the block" do
|
263
|
+
it "returns true" do
|
264
|
+
feature.match_groups?(user).should be_true
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context "when the instance does not match the block" do
|
269
|
+
it "returns false" do
|
270
|
+
feature.match_groups?(user2).should be_false
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context "when the instance is not of the type of the block" do
|
275
|
+
it "returns false" do
|
276
|
+
feature.match_groups?(organization).should be_false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Detour::FlaggableFlag do
|
4
|
+
it { should be_a Detour::Flag }
|
5
|
+
it { should belong_to :flaggable }
|
6
|
+
it { should validate_presence_of :flaggable_id }
|
7
|
+
it { should allow_mass_assignment_of :flaggable }
|
8
|
+
it { should validate_uniqueness_of :feature_id }
|
9
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Detour::Flaggable do
|
4
|
+
subject { User.new }
|
5
|
+
|
6
|
+
describe "#flaggable_find!" do
|
7
|
+
context "when a non-default find_by is not specified" do
|
8
|
+
it "finds by id" do
|
9
|
+
User.should_receive(:find_by_id!).with(1)
|
10
|
+
User.flaggable_find!(1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when a non-default find_by is not specified" do
|
15
|
+
class Foo < ActiveRecord::Base
|
16
|
+
acts_as_flaggable find_by: :email
|
17
|
+
end
|
18
|
+
|
19
|
+
it "finds by id" do
|
20
|
+
Foo.should_receive(:find_by_email!).with("user@example.com")
|
21
|
+
Foo.flaggable_find!("user@example.com")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#has_feature?" do
|
27
|
+
let(:user) { User.create(name: "foo") }
|
28
|
+
let(:feature) { Detour::Feature.create(name: "bar") }
|
29
|
+
|
30
|
+
it "memoizes found features" do
|
31
|
+
Detour::Feature.stub(:find_by_name) { feature }
|
32
|
+
feature.flaggable_flags.create(flaggable: user)
|
33
|
+
|
34
|
+
feature.should_receive(:match?).with(user).and_return(true)
|
35
|
+
user.has_feature?(:bar)
|
36
|
+
|
37
|
+
feature.should_not_receive(:match?).with(user)
|
38
|
+
user.has_feature?(:bar)
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when given a block" do
|
42
|
+
context "and the user is flagged in" do
|
43
|
+
before do
|
44
|
+
feature.flaggable_flags.create(flaggable: user)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "calls the block" do
|
48
|
+
foo = "foo"
|
49
|
+
user.has_feature?(:bar) { foo = "bar" }
|
50
|
+
foo.should eq "bar"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns the match" do
|
54
|
+
foo = "foo"
|
55
|
+
|
56
|
+
if user.has_feature? :not_feature do
|
57
|
+
end; else
|
58
|
+
foo = "bar"
|
59
|
+
end
|
60
|
+
|
61
|
+
foo.should eq "bar"
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the block raises an exception" do
|
65
|
+
it "increments the failure_count of the feature" do
|
66
|
+
begin
|
67
|
+
user.has_feature? :bar do
|
68
|
+
raise "This is an exception"
|
69
|
+
end
|
70
|
+
rescue
|
71
|
+
feature.reload.failure_count.should eq 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "raises the exception" do
|
76
|
+
expect do
|
77
|
+
user.has_feature? :bar do
|
78
|
+
raise "This is an exception"
|
79
|
+
end
|
80
|
+
end.to raise_error "This is an exception"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "and the user is not flagged in" do
|
86
|
+
it "does not call the block" do
|
87
|
+
foo = "foo"
|
88
|
+
user.has_feature?(:bar) { foo = "bar" }
|
89
|
+
foo.should eq "foo"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when the user is not flagged in" do
|
95
|
+
it "returns false" do
|
96
|
+
user.has_feature?(:bar).should be_false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when the user is flagged in" do
|
101
|
+
context "and the user is opted out" do
|
102
|
+
before do
|
103
|
+
feature.flaggable_flags.create(flaggable: user)
|
104
|
+
feature.opt_out_flags.create(flaggable: user)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns false" do
|
108
|
+
user.has_feature?(:bar).should be_false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "and the user is not opted out" do
|
113
|
+
context "and the user is flagged in individually" do
|
114
|
+
before do
|
115
|
+
feature.flaggable_flags.create(flaggable: user)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns true" do
|
119
|
+
user.has_feature?(:bar).should be_true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "and the user is flagged in as a percentage" do
|
124
|
+
before do
|
125
|
+
feature.percentage_flags.create(flaggable_type: "User", percentage: 100)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns true" do
|
129
|
+
user.has_feature?(:bar).should be_true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "and the user is flagged in via a group" do
|
134
|
+
before do
|
135
|
+
Detour::Feature.define_user_group "name_foo" do |user|
|
136
|
+
user.name == "foo"
|
137
|
+
end
|
138
|
+
|
139
|
+
feature.group_flags.create(flaggable_type: "User", group_name: "name_foo")
|
140
|
+
end
|
141
|
+
|
142
|
+
it "returns true" do
|
143
|
+
user.has_feature?(:bar).should be_true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
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_id }
|
7
|
+
it { should allow_mass_assignment_of :flaggable}
|
8
|
+
it { should validate_uniqueness_of :feature_id }
|
9
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Detour::PercentageFlag do
|
4
|
+
subject { Detour::PercentageFlag.new(feature_id: 1, flaggable_type: "User") }
|
5
|
+
|
6
|
+
it { should be_a Detour::Flag }
|
7
|
+
it { should validate_numericality_of(:percentage).is_greater_than(0).is_less_than_or_equal_to(100) }
|
8
|
+
it { should allow_mass_assignment_of :percentage }
|
9
|
+
it { should validate_uniqueness_of :feature_id }
|
10
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "detour:create" do
|
4
|
+
include_context "rake"
|
5
|
+
|
6
|
+
it "creates the given feature" do
|
7
|
+
Detour::Feature.should_receive(:find_or_create_by_name!).with("foo")
|
8
|
+
subject.invoke("foo")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "detour:destroy" do
|
13
|
+
include_context "rake"
|
14
|
+
|
15
|
+
let(:feature) { Detour::Feature.create(name: "foo") }
|
16
|
+
|
17
|
+
it "destroys the given feature" do
|
18
|
+
Detour::Feature.should_receive(:find_by_name!).with(feature.name).and_return(feature)
|
19
|
+
feature.should_receive(:destroy)
|
20
|
+
subject.invoke("foo")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "detour:activate" do
|
25
|
+
include_context "rake"
|
26
|
+
|
27
|
+
let(:user) { User.create(name: "foo") }
|
28
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
29
|
+
|
30
|
+
it "activates the feature for the record" do
|
31
|
+
Detour::Feature.should_receive(:add_record_to_feature).with(user, "foo")
|
32
|
+
subject.invoke("foo", "User", user.id.to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does not require a class if defined_flaggable_class is set" do
|
36
|
+
Detour::Feature.should_receive(:add_record_to_feature).with(user, "foo")
|
37
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
38
|
+
expect { subject.invoke("foo", user.id.to_s) }.to_not raise_error
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "detour:deactivate" do
|
43
|
+
include_context "rake"
|
44
|
+
|
45
|
+
let(:user) { User.create(name: "foo") }
|
46
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
47
|
+
|
48
|
+
it "deactivates the feature for the record" do
|
49
|
+
Detour::Feature.should_receive(:remove_record_from_feature).with(user, "foo")
|
50
|
+
subject.invoke("foo", "User", user.id.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "does not require a class if defined_flaggable_class is set" do
|
54
|
+
Detour::Feature.should_receive(:remove_record_from_feature).with(user, "foo")
|
55
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
56
|
+
expect { subject.invoke("foo", user.id.to_s) }.to_not raise_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "detour:opt_out" do
|
61
|
+
include_context "rake"
|
62
|
+
|
63
|
+
let(:user) { User.create(name: "foo") }
|
64
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
65
|
+
|
66
|
+
it "deactivates the feature for the record" do
|
67
|
+
Detour::Feature.should_receive(:opt_record_out_of_feature).with(user, "foo")
|
68
|
+
subject.invoke("foo", "User", user.id.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not require a class if defined_flaggable_class is set" do
|
72
|
+
Detour::Feature.should_receive(:opt_record_out_of_feature).with(user, "foo")
|
73
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
74
|
+
expect { subject.invoke("foo", user.id.to_s) }.to_not raise_error
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "detour:un_opt_out" do
|
79
|
+
include_context "rake"
|
80
|
+
|
81
|
+
let(:user) { User.create(name: "foo") }
|
82
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
83
|
+
|
84
|
+
it "deactivates the feature for the record" do
|
85
|
+
Detour::Feature.should_receive(:un_opt_record_out_of_feature).with(user, "foo")
|
86
|
+
subject.invoke("foo", "User", user.id.to_s)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "does not require a class if defined_flaggable_class is set" do
|
90
|
+
Detour::Feature.should_receive(:un_opt_record_out_of_feature).with(user, "foo")
|
91
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
92
|
+
expect { subject.invoke("foo", user.id.to_s) }.to_not raise_error
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "detour:activate_group" do
|
97
|
+
include_context "rake"
|
98
|
+
|
99
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
100
|
+
|
101
|
+
it "activates the feature for the group" do
|
102
|
+
Detour::Feature.should_receive(:add_group_to_feature).with("User", "admins", "foo")
|
103
|
+
subject.invoke("foo", "User", "admins")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "does not require a class if defined_flaggable_class is set" do
|
107
|
+
Detour::Feature.should_receive(:add_group_to_feature).with("User", "admins", "foo")
|
108
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
109
|
+
expect { subject.invoke("foo", "admins") }.to_not raise_error
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "detour:deactivate_group" do
|
114
|
+
include_context "rake"
|
115
|
+
|
116
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
117
|
+
|
118
|
+
it "deactivates the feature for the group" do
|
119
|
+
Detour::Feature.should_receive(:remove_group_from_feature).with("User", "admins", "foo")
|
120
|
+
subject.invoke("foo", "User", "admins")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "does not require a class if defined_flaggable_class is set" do
|
124
|
+
Detour::Feature.should_receive(:remove_group_from_feature).with("User", "admins", "foo")
|
125
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
126
|
+
expect { subject.invoke("foo", "admins") }.to_not raise_error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "detour:activate_percentage" do
|
131
|
+
include_context "rake"
|
132
|
+
|
133
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
134
|
+
|
135
|
+
it "activates the feature for the percentage" do
|
136
|
+
Detour::Feature.should_receive(:add_percentage_to_feature).with("User", 50, "foo")
|
137
|
+
subject.invoke("foo", "User", "50")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "does not require a class if defined_flaggable_class is set" do
|
141
|
+
Detour::Feature.should_receive(:add_percentage_to_feature).with("User", 50, "foo")
|
142
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
143
|
+
expect { subject.invoke("foo", "50") }.to_not raise_error
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "detour:deactivate_percentage" do
|
148
|
+
include_context "rake"
|
149
|
+
|
150
|
+
let!(:feature) { Detour::Feature.create(name: "foo") }
|
151
|
+
|
152
|
+
it "deactivates the feature for the percentage" do
|
153
|
+
Detour::Feature.should_receive(:remove_percentage_from_feature).with("User", "foo")
|
154
|
+
subject.invoke("foo", "User")
|
155
|
+
end
|
156
|
+
|
157
|
+
it "does not require a class if defined_flaggable_class is set" do
|
158
|
+
Detour::Feature.should_receive(:remove_percentage_from_feature).with("User", "foo")
|
159
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", "User"
|
160
|
+
expect { subject.invoke("foo") }.to_not raise_error
|
161
|
+
end
|
162
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rails"
|
2
|
+
require "active_record"
|
3
|
+
require "detour"
|
4
|
+
require "shoulda-matchers"
|
5
|
+
require "generators/templates/migration"
|
6
|
+
require "support/shared_contexts/rake"
|
7
|
+
require "pry"
|
8
|
+
|
9
|
+
class User < ActiveRecord::Base
|
10
|
+
acts_as_flaggable
|
11
|
+
end
|
12
|
+
|
13
|
+
class Organization < ActiveRecord::Base
|
14
|
+
acts_as_flaggable
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.before :suite do
|
19
|
+
ActiveRecord::Base.establish_connection \
|
20
|
+
adapter: "sqlite3",
|
21
|
+
database: File.dirname(__FILE__) + "/spec.sqlite3"
|
22
|
+
|
23
|
+
require File.dirname(__FILE__) + "/support/schema.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
config.before :each do
|
27
|
+
SetupDetour.migrate :up
|
28
|
+
ActiveRecord::Schema.migrate :up
|
29
|
+
end
|
30
|
+
|
31
|
+
config.after :each do
|
32
|
+
SetupDetour.migrate :down
|
33
|
+
ActiveRecord::Schema.migrate :down
|
34
|
+
end
|
35
|
+
|
36
|
+
config.after :each do
|
37
|
+
Detour::Feature.instance_variable_set "@defined_groups", {}
|
38
|
+
Detour::Feature.instance_variable_set "@default_flaggable_class_name", nil
|
39
|
+
end
|
40
|
+
end
|