detour 0.0.7 → 0.0.9
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 +4 -4
- data/README.md +36 -7
- data/app/controllers/detour/flaggable_flags_controller.rb +11 -2
- data/app/helpers/detour/flaggable_flags_helper.rb +1 -1
- data/app/models/detour/feature.rb +6 -6
- data/app/views/detour/flags/_feature_form.html.erb +2 -2
- data/config/routes.rb +4 -6
- data/lib/detour/configuration.rb +0 -1
- data/lib/detour/version.rb +1 -1
- data/spec/controllers/detour/flaggable_flags_controller_spec.rb +70 -31
- data/spec/models/detour/feature_spec.rb +6 -5
- data/spec/spec_helper.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8684efda0b28c3dbd1dd637adced4476063e1b69
|
4
|
+
data.tar.gz: 7b8de882f70365420e74d75734583ac336f58762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 070f25d30517f8f1dbd062087e90db55f15b1a51a7eb456211a57123f71314ec33d93417d06ac5921ec2815fb784c8f666b2765a7c8eb290e8163188ce135849
|
7
|
+
data.tar.gz: 0080ba77c52c9de244cdc68676519ff81148ee4e190e60f5c9d9c54e903cfd343c1e5f6bd0861d239c66073e085977e658d57bc2fd01dc54525f2edec78b2391
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Detour
|
2
2
|
|
3
|
-
Rollouts for `ActiveRecord` models. It is a spiritual fork of
|
3
|
+
Rollouts for `ActiveRecord` models. It is a spiritual fork of
|
4
|
+
[ArRollout](https://github.com/markpundsack/ar_rollout).
|
4
5
|
|
5
6
|
| development status | master status | Code Climate |
|
6
7
|
| ------------------ | ------------- | ------------ |
|
@@ -17,6 +18,7 @@ Rollouts for `ActiveRecord` models. It is a spiritual fork of [ArRollout](https:
|
|
17
18
|
- [Installation](#installation)
|
18
19
|
- [Usage](#usage)
|
19
20
|
- [Configuration](#configuration)
|
21
|
+
- [Restricting the admin interface](#restricting-the-admin-interface)
|
20
22
|
- [Marking a model as flaggable](#marking-a-model-as-flaggable)
|
21
23
|
- [Determining if a record is flagged into a feature](#determining-if-a-record-is-flagged-into-a-feature)
|
22
24
|
- [Defining programmatic groups](#defining-programmatic-groups)
|
@@ -44,7 +46,8 @@ Run the Detour migrations:
|
|
44
46
|
|
45
47
|
`Detour` works by determining whether or not a specific record
|
46
48
|
should have features accessible to it based on individual flags, flags for a
|
47
|
-
percentage of records,
|
49
|
+
percentage of records, flags for a database-backed group of records, or flags
|
50
|
+
for a code-defined group of records.
|
48
51
|
|
49
52
|
### Configuration
|
50
53
|
|
@@ -61,11 +64,6 @@ Detour.configure do |config|
|
|
61
64
|
# checking for flags in your code. Provide it an
|
62
65
|
# array of glob strings:
|
63
66
|
config.feature_search_dirs = %w[app/**/*.{rb,erb}]
|
64
|
-
|
65
|
-
# Provide a default class to manage rollouts for, if
|
66
|
-
# desired. This means you can omit the class name from
|
67
|
-
# rake tasks:
|
68
|
-
config.default_flaggable_class_name = "User"
|
69
67
|
end
|
70
68
|
```
|
71
69
|
|
@@ -77,6 +75,37 @@ Rails.application.routes.draw do
|
|
77
75
|
end
|
78
76
|
```
|
79
77
|
|
78
|
+
### Restricting the admin interface
|
79
|
+
|
80
|
+
If you'd like your Detour admin interface only accessible to admins, for example,
|
81
|
+
you can add a `before_filter` to `Detour::ApplicationController` in the Detour
|
82
|
+
initializer:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# config/initializers/detour.rb
|
86
|
+
|
87
|
+
Detour::ApplicationController.class_eval do
|
88
|
+
include CurrentUser
|
89
|
+
|
90
|
+
before_filter :admin_required!
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def admin_required!
|
95
|
+
if current_user && current_user.admin?
|
96
|
+
true
|
97
|
+
else
|
98
|
+
# redirect somewhere for non-admins
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
Since `Detour::ApplicationController` isn't a subclass of my `ApplicationController`,
|
105
|
+
I've included a `CurrentUser` module that I built in this app that adds a
|
106
|
+
`current_user` method to any controller that it's included in. Now, only admins
|
107
|
+
will have access to the Detour admin interface.
|
108
|
+
|
80
109
|
### Marking a model as flaggable
|
81
110
|
|
82
111
|
In addition to listing classes that are flaggable in your initializer, add
|
@@ -4,9 +4,10 @@ class Detour::FlaggableFlagsController < Detour::ApplicationController
|
|
4
4
|
include Detour::FlaggableFlagsHelper
|
5
5
|
|
6
6
|
before_filter :ensure_flaggable_type_exists
|
7
|
+
before_filter :ensure_flag_type_exists
|
7
8
|
|
8
9
|
def index
|
9
|
-
@feature = Detour::Feature.
|
10
|
+
@feature = Detour::Feature.where(name: params[:feature_name]).first_or_create!
|
10
11
|
end
|
11
12
|
|
12
13
|
def update
|
@@ -14,9 +15,17 @@ class Detour::FlaggableFlagsController < Detour::ApplicationController
|
|
14
15
|
|
15
16
|
if @feature.update_attributes(params[:feature])
|
16
17
|
flash[:notice] = "Your #{flag_noun.pluralize} have been updated"
|
17
|
-
redirect_to
|
18
|
+
redirect_to flaggable_flags_path flag_type: params[:flag_type], feature_name: params[:feature_name], flaggable_type: params[:flaggable_type]
|
18
19
|
else
|
19
20
|
render :index
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def ensure_flag_type_exists
|
27
|
+
unless %w[flag-ins opt-outs].include? params[:flag_type]
|
28
|
+
raise ActionController::RoutingError.new("Not Found")
|
29
|
+
end
|
30
|
+
end
|
22
31
|
end
|
@@ -8,12 +8,12 @@ class Detour::Feature < ActiveRecord::Base
|
|
8
8
|
serialize :flag_in_counts, JSON
|
9
9
|
serialize :opt_out_counts, JSON
|
10
10
|
|
11
|
-
has_many :flag_in_flags
|
12
|
-
has_many :defined_group_flags
|
13
|
-
has_many :database_group_flags
|
14
|
-
has_many :percentage_flags
|
15
|
-
has_many :opt_out_flags
|
16
|
-
has_many :flags,
|
11
|
+
has_many :flag_in_flags, dependent: :destroy
|
12
|
+
has_many :defined_group_flags, dependent: :destroy
|
13
|
+
has_many :database_group_flags, dependent: :destroy
|
14
|
+
has_many :percentage_flags, dependent: :destroy
|
15
|
+
has_many :opt_out_flags, dependent: :destroy
|
16
|
+
has_many :flags, dependent: :destroy
|
17
17
|
|
18
18
|
validates_presence_of :name
|
19
19
|
validates_uniqueness_of :name
|
@@ -33,13 +33,13 @@
|
|
33
33
|
<% end %>
|
34
34
|
|
35
35
|
<td class="flag-in-count">
|
36
|
-
<%= link_to
|
36
|
+
<%= link_to flaggable_flags_path(flag_type: "flag-ins", feature_name: feature.name, flaggable_type: params[:flaggable_type]) do %>
|
37
37
|
<%= feature.flag_in_count_for(params[:flaggable_type]) %>
|
38
38
|
<% end %>
|
39
39
|
</td>
|
40
40
|
|
41
41
|
<td class="opt-out-count">
|
42
|
-
<%= link_to
|
42
|
+
<%= link_to flaggable_flags_path(flag_type: "opt-outs", feature_name: feature.name, flaggable_type: params[:flaggable_type]) do %>
|
43
43
|
<%= feature.opt_out_count_for(params[:flaggable_type]) %>
|
44
44
|
<% end %>
|
45
45
|
</td>
|
data/config/routes.rb
CHANGED
@@ -5,12 +5,10 @@ Detour::Engine.routes.draw do
|
|
5
5
|
resources :features, only: [:create, :destroy]
|
6
6
|
resources :groups, only: [:index, :show, :create, :update, :destroy]
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
delete ":flaggable_type/:id" => "flaggable_flags#destroy", as: "#{flag_type.underscore.singularize}_flag"
|
13
|
-
end
|
8
|
+
scope "/:flag_type/:feature_name" do
|
9
|
+
get ":flaggable_type" => "flaggable_flags#index", as: "flaggable_flags"
|
10
|
+
put ":flaggable_type" => "flaggable_flags#update"
|
11
|
+
delete ":flaggable_type/:id" => "flaggable_flags#destroy", as: "flaggable_flag"
|
14
12
|
end
|
15
13
|
|
16
14
|
root to: "application#index"
|
data/lib/detour/configuration.rb
CHANGED
data/lib/detour/version.rb
CHANGED
@@ -6,57 +6,96 @@ describe Detour::FlaggableFlagsController do
|
|
6
6
|
describe "GET #index" do
|
7
7
|
let(:flag) { create :flag_in_flag }
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
context "when the flag type is invalid" do
|
10
|
+
def do_get
|
11
|
+
get :index, flag_type: "foo", feature_name: flag.feature.name, flaggable_type: "users"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns a 404" do
|
15
|
+
expect { do_get }.to raise_error ActionController::RoutingError
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
19
|
+
context "when the feature does not exist" do
|
20
|
+
before do
|
21
|
+
get :index, flag_type: "flag-ins", feature_name: "foo", flaggable_type: "users"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "persists the feature" do
|
25
|
+
Detour::Feature.where(name: "foo").should_not be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "is a 200" do
|
29
|
+
response.status.should eq 200
|
30
|
+
end
|
15
31
|
end
|
16
32
|
|
17
|
-
|
18
|
-
|
33
|
+
context "when the flag type is valid" do
|
34
|
+
before do
|
35
|
+
get :index, flag_type: "flag-ins", feature_name: flag.feature.name, flaggable_type: "users"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "assigns the feature" do
|
39
|
+
assigns(:feature).should eq flag.feature
|
40
|
+
end
|
41
|
+
|
42
|
+
it "renders the index template" do
|
43
|
+
response.should render_template :index
|
44
|
+
end
|
19
45
|
end
|
20
46
|
end
|
21
47
|
|
22
48
|
describe "PUT #update" do
|
23
49
|
let(:flag) { create :flag_in_flag }
|
24
50
|
|
25
|
-
|
26
|
-
|
51
|
+
context "when the flag type is invalid" do
|
52
|
+
def do_put
|
53
|
+
put :update, flag_type: "foo", feature_name: flag.feature.name, flaggable_type: "users", feature: {}
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns a 404" do
|
57
|
+
expect { do_put }.to raise_error ActionController::RoutingError
|
58
|
+
end
|
27
59
|
end
|
28
60
|
|
29
|
-
context "when successful" do
|
30
|
-
let(:user) { create :user }
|
31
61
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
"1" => { flaggable_type: "User", flaggable_key: user.id, _destroy: 0 }
|
36
|
-
}
|
37
|
-
}
|
62
|
+
context "when the flag type is valid" do
|
63
|
+
before do
|
64
|
+
put :update, flag_type: "flag-ins", feature_name: flag.feature.name, flaggable_type: "users", feature: flag_params
|
38
65
|
end
|
39
66
|
|
40
|
-
|
41
|
-
|
42
|
-
end
|
67
|
+
context "when successful" do
|
68
|
+
let(:user) { create :user }
|
43
69
|
|
44
|
-
|
45
|
-
|
70
|
+
let(:flag_params) do
|
71
|
+
{
|
72
|
+
users_flag_ins_attributes: {
|
73
|
+
"1" => { flaggable_type: "User", flaggable_key: user.id, _destroy: 0 }
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "sets a flash message" do
|
79
|
+
flash[:notice].should eq "Your flag-ins have been updated"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "redirect to the group" do
|
83
|
+
response.should redirect_to flaggable_flags_path(flag_type: "flag-ins", feature_name: flag.feature.name, flaggable_type: "users")
|
84
|
+
end
|
46
85
|
end
|
47
|
-
end
|
48
86
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
87
|
+
context "when unsuccessful" do
|
88
|
+
let(:flag_params) do
|
89
|
+
{
|
90
|
+
users_flag_ins_attributes: {
|
91
|
+
"1" => { flaggable_type: "User", flaggable_key: nil, _destroy: 0 }
|
92
|
+
}
|
54
93
|
}
|
55
|
-
|
56
|
-
end
|
94
|
+
end
|
57
95
|
|
58
|
-
|
59
|
-
|
96
|
+
it "renders the show template" do
|
97
|
+
response.should render_template :index
|
98
|
+
end
|
60
99
|
end
|
61
100
|
end
|
62
101
|
end
|
@@ -2,11 +2,11 @@ require "spec_helper"
|
|
2
2
|
require "fakefs/spec_helpers"
|
3
3
|
|
4
4
|
describe Detour::Feature do
|
5
|
-
it { should have_many(:flag_in_flags) }
|
6
|
-
it { should have_many(:database_group_flags) }
|
7
|
-
it { should have_many(:defined_group_flags) }
|
8
|
-
it { should have_many(:percentage_flags) }
|
9
|
-
it { should have_many(:opt_out_flags) }
|
5
|
+
it { should have_many(:flag_in_flags).dependent(:destroy) }
|
6
|
+
it { should have_many(:database_group_flags).dependent(:destroy) }
|
7
|
+
it { should have_many(:defined_group_flags).dependent(:destroy) }
|
8
|
+
it { should have_many(:percentage_flags).dependent(:destroy) }
|
9
|
+
it { should have_many(:opt_out_flags).dependent(:destroy) }
|
10
10
|
it { should have_many(:flags).dependent(:destroy) }
|
11
11
|
it { should validate_presence_of :name }
|
12
12
|
it { should validate_uniqueness_of :name }
|
@@ -16,6 +16,7 @@ describe Detour::Feature do
|
|
16
16
|
it { should allow_value("foo_bar").for(:name) }
|
17
17
|
it { should allow_value("foo-bar").for(:name) }
|
18
18
|
it { should_not allow_value("foo-bar.").for(:name) }
|
19
|
+
it { should_not allow_value("foo bar").for(:name) }
|
19
20
|
|
20
21
|
describe "name format validation" do
|
21
22
|
let(:feature) { build :feature, name: "foo bar." }
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: detour
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Clem
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|