detour 0.0.7 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|