arturo 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +33 -42
- data/app/controllers/arturo/features_controller.rb +3 -1
- data/lib/arturo.rb +1 -1
- data/lib/arturo/controller_filters.rb +1 -1
- data/lib/arturo/engine.rb +1 -0
- data/lib/arturo/feature_availability.rb +10 -2
- data/lib/arturo/feature_management.rb +23 -0
- data/lib/generators/arturo/templates/initializer.rb +18 -10
- metadata +4 -4
- data/lib/arturo/configuration.rb +0 -43
data/README.md
CHANGED
@@ -48,7 +48,7 @@ the feature:
|
|
48
48
|
|
49
49
|
# in app/controllers/postings_controller:
|
50
50
|
class PostingsController < ApplicationController
|
51
|
-
require_feature
|
51
|
+
require_feature :live_postings, :only => :recent
|
52
52
|
# ...
|
53
53
|
end
|
54
54
|
|
@@ -92,13 +92,17 @@ checkout.
|
|
92
92
|
|
93
93
|
### In Rails
|
94
94
|
|
95
|
-
#### Run the
|
95
|
+
#### Run the generators:
|
96
96
|
|
97
97
|
$ rails g arturo:migration
|
98
98
|
$ rails g arturo:initializer
|
99
99
|
$ rails g arturo:route
|
100
100
|
$ rails g arturo:assets
|
101
101
|
|
102
|
+
#### Run the migration:
|
103
|
+
|
104
|
+
$ rake db:migrate
|
105
|
+
|
102
106
|
#### Edit the configuration
|
103
107
|
|
104
108
|
##### Initializer
|
@@ -106,10 +110,10 @@ checkout.
|
|
106
110
|
Open up the newly-generated `config/initializers/arturo_initializer.rb`.
|
107
111
|
There are configuration options for the following:
|
108
112
|
|
109
|
-
* the
|
113
|
+
* the method that determines whether a user has permission to manage features
|
110
114
|
(see [admin permissions](#adminpermissions))
|
111
|
-
* the
|
112
|
-
(
|
115
|
+
* the method that returns the object that has features
|
116
|
+
(e.g. User, Person, or Account; see
|
113
117
|
[feature recipients](#featurerecipients))
|
114
118
|
* whitelists and blacklists for features
|
115
119
|
(see [white- and blacklisting](#wblisting))
|
@@ -132,20 +136,19 @@ work with you on support for your favorite framework.
|
|
132
136
|
|
133
137
|
### <span id='adminpermissions'>Admin Permissions</span>
|
134
138
|
|
135
|
-
`Arturo
|
136
|
-
a Controller instance. It should return `true`
|
137
|
-
|
138
|
-
|
139
|
-
might be
|
139
|
+
`Arturo::FeatureManagement#may_manage_features?` is a method that is run in
|
140
|
+
the context of a Controller or View instance. It should return `true` if
|
141
|
+
and only if the current user may manage permissions. The default implementation
|
142
|
+
is as follows:
|
140
143
|
|
141
|
-
|
142
|
-
current_user.admin?
|
143
|
-
end
|
144
|
+
current_user.present? && current_user.admin?
|
144
145
|
|
145
|
-
|
146
|
+
You can change the implementation in
|
147
|
+
`config/initializers/arturo_initializer.rb`. A reasonable implementation
|
148
|
+
might be
|
146
149
|
|
147
150
|
Arturo.permit_management do
|
148
|
-
signed_in? &&
|
151
|
+
signed_in? && current_user.can?(:manage_features)
|
149
152
|
end
|
150
153
|
|
151
154
|
### <span id='featurerecipients'>Feature Recipients</span>
|
@@ -157,35 +160,23 @@ early days -- may have deployed features on a per-university basis. It wouldn't
|
|
157
160
|
make much sense to deploy a feature to one user of a Basecamp project but not
|
158
161
|
to others, so 37Signals would probably want a per-project or per-account basis.
|
159
162
|
|
160
|
-
`Arturo
|
161
|
-
|
162
|
-
...) that is a member of the category that is the basis for
|
163
|
-
features.
|
164
|
-
`config/initializers/arturo_initializer.rb`. It should return an `Object` that
|
165
|
-
responds to `#id`. If you want to deploy features on a per-user basis, a
|
166
|
-
reasonable implementation might be
|
167
|
-
|
168
|
-
Arturo.thing_that_has_features do
|
169
|
-
current_user
|
170
|
-
end
|
171
|
-
|
172
|
-
or
|
173
|
-
|
174
|
-
Arturo.thing_that_has_features do
|
175
|
-
signed_in_person
|
176
|
-
end
|
163
|
+
`Arturo::FeatureAvailability#feature_recipient` is intended to support these
|
164
|
+
many use cases. It is a method that returns the current "thing" (a user, account,
|
165
|
+
project, university, ...) that is a member of the category that is the basis for
|
166
|
+
deploying new features. It should return an `Object` that responds to `#id`.
|
177
167
|
|
178
|
-
|
179
|
-
|
180
|
-
|
168
|
+
The default implementation simply returns `current_user`. Like
|
169
|
+
`Arturo::FeatureManagement#may_manage_features?`, this method can be configured
|
170
|
+
in `config/initializers/arturo_initializer.rb`. If you want to deploy features
|
171
|
+
on a per-account basis, a reasonable implementation might be
|
181
172
|
|
182
|
-
Arturo.
|
173
|
+
Arturo.feature_recipient do
|
183
174
|
current_account
|
184
175
|
end
|
185
176
|
|
186
177
|
or
|
187
178
|
|
188
|
-
Arturo.
|
179
|
+
Arturo.feature_recipient do
|
189
180
|
current_user.account
|
190
181
|
end
|
191
182
|
|
@@ -224,10 +215,10 @@ every action within `BookHoldsController` that is invoked by a user who
|
|
224
215
|
does not have the `:hold_book` feature.
|
225
216
|
|
226
217
|
class BookHoldsController < ApplicationController
|
227
|
-
require_feature
|
218
|
+
require_feature :hold_book
|
228
219
|
end
|
229
220
|
|
230
|
-
`require_feature
|
221
|
+
`require_feature` accepts as a second argument a `Hash` that it passes on
|
231
222
|
to `before_filter`, so you can use `:only` and `:except` to specify exactly
|
232
223
|
which actions are filtered.
|
233
224
|
|
@@ -258,11 +249,11 @@ The latter can be used like so:
|
|
258
249
|
|
259
250
|
**Note**: Arturo does not yet have caching support. Be very careful when
|
260
251
|
caching actions or pages that involve feature detection as you will get
|
261
|
-
strange behavior when a
|
252
|
+
strange behavior when a user who has access to a feature requests a page
|
262
253
|
just after one who does not (and vice versa). The following is the
|
263
254
|
**intended** support for caching.
|
264
255
|
|
265
|
-
Both the `require_feature
|
256
|
+
Both the `require_feature` before filter and the `if_feature_enabled` block
|
266
257
|
evaluation automatically append a string based on the feature's
|
267
258
|
`last_modified` timestamp to cache keys that Rails generates. Thus, you don't
|
268
259
|
have to worry about expiring caches when you increase a feature's deployment
|
@@ -272,4 +263,4 @@ percentage. See `Arturo::CacheSupport` for more information.
|
|
272
263
|
|
273
264
|
Arturo gets its name from
|
274
265
|
[Professor Maximillian Arturo](http://en.wikipedia.org/wiki/Maximillian_Arturo)
|
275
|
-
on [Sliders](http://en.wikipedia.org/wiki/Sliders).
|
266
|
+
on [Sliders](http://en.wikipedia.org/wiki/Sliders).
|
@@ -17,6 +17,8 @@ module Arturo
|
|
17
17
|
# should redefine Arturo::FeaturesController#permitted? to
|
18
18
|
# return true only for users who are permitted to manage features.
|
19
19
|
class FeaturesController < base
|
20
|
+
include Arturo::FeatureManagement
|
21
|
+
|
20
22
|
unloadable
|
21
23
|
respond_to :html, :json, :xml
|
22
24
|
before_filter :require_permission
|
@@ -95,7 +97,7 @@ module Arturo
|
|
95
97
|
protected
|
96
98
|
|
97
99
|
def require_permission
|
98
|
-
unless
|
100
|
+
unless may_manage_features?
|
99
101
|
render :action => 'forbidden', :status => 403
|
100
102
|
return false
|
101
103
|
end
|
data/lib/arturo.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Arturo
|
2
2
|
|
3
|
-
require 'arturo/configuration'
|
4
3
|
require 'arturo/special_handling'
|
5
4
|
require 'arturo/feature_availability'
|
5
|
+
require 'arturo/feature_management'
|
6
6
|
require 'arturo/controller_filters'
|
7
7
|
require 'arturo/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
|
8
8
|
|
data/lib/arturo/engine.rb
CHANGED
@@ -12,8 +12,7 @@ module Arturo
|
|
12
12
|
def feature_enabled?(symbol_or_feature)
|
13
13
|
feature = ::Arturo::Feature.to_feature(symbol_or_feature)
|
14
14
|
return false if feature.blank?
|
15
|
-
|
16
|
-
feature.enabled_for?(thing)
|
15
|
+
feature.enabled_for?(feature_recipient)
|
17
16
|
end
|
18
17
|
|
19
18
|
def if_feature_enabled(symbol_or_feature, &block)
|
@@ -24,6 +23,15 @@ module Arturo
|
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
26
|
+
# By default, returns current_user.
|
27
|
+
#
|
28
|
+
# If you would like to change this implementation, it is recommended
|
29
|
+
# you do so in config/initializers/arturo_initializer.rb
|
30
|
+
# @return [Object] the recipient of features.
|
31
|
+
def feature_recipient
|
32
|
+
current_user
|
33
|
+
end
|
34
|
+
|
27
35
|
end
|
28
36
|
|
29
37
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arturo
|
2
|
+
|
3
|
+
# A mixin that is included by Arturo::FeaturesController and is declared
|
4
|
+
# as a helper for all views. It provides a single method,
|
5
|
+
# may_manage_features?, that returns whether or not the current user
|
6
|
+
# may manage features. By default, it is implemented as follows:
|
7
|
+
#
|
8
|
+
# def may_manage_features?
|
9
|
+
# current_user.present? && current_user.admin?
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# If you would like to change this implementation, it is recommended
|
13
|
+
# you do so in config/initializers/arturo_initializer.rb
|
14
|
+
module FeatureManagement
|
15
|
+
|
16
|
+
# @return [true,false] whether the current user may manage features
|
17
|
+
def may_manage_features?
|
18
|
+
current_user.present? && current_user.admin?
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -1,21 +1,29 @@
|
|
1
1
|
require 'arturo'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# Configure who may manage features here.
|
4
|
+
# The following is the default implementation.
|
5
|
+
# Arturo::FeatureManagement.class_eval do
|
6
|
+
# def may_manage_features?
|
7
|
+
# current_user.present? && current_user.admin?
|
8
|
+
# end
|
9
|
+
# end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
# Configure what receives features here.
|
12
|
+
# The following is the default implementation.
|
13
|
+
# Arturo::FeatureAvailability.class_eval do
|
14
|
+
# def feature_recipient
|
15
|
+
# current_user
|
16
|
+
# end
|
17
|
+
# end
|
10
18
|
|
11
19
|
# Whitelists and Blacklists:
|
12
20
|
#
|
13
|
-
#
|
14
|
-
# Arturo::Feature.whitelist(
|
21
|
+
# Enable feature one for all admins:
|
22
|
+
# Arturo::Feature.whitelist(:feature_one) do |user|
|
15
23
|
# user.admin?
|
16
24
|
# end
|
17
25
|
#
|
18
|
-
#
|
19
|
-
# Arturo::Feature.blacklist(
|
26
|
+
# Disable feature two for all small accounts:
|
27
|
+
# Arturo::Feature.blacklist(:feature_two) do |user|
|
20
28
|
# user.account.small?
|
21
29
|
# end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 1.0.0
|
9
|
+
version: 1.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- James A. Rosen
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-10-
|
17
|
+
date: 2010-10-25 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -108,11 +108,11 @@ extensions: []
|
|
108
108
|
extra_rdoc_files: []
|
109
109
|
|
110
110
|
files:
|
111
|
-
- lib/arturo/configuration.rb
|
112
111
|
- lib/arturo/controller_filters.rb
|
113
112
|
- lib/arturo/engine.rb
|
114
113
|
- lib/arturo/feature_availability.rb
|
115
114
|
- lib/arturo/feature_factories.rb
|
115
|
+
- lib/arturo/feature_management.rb
|
116
116
|
- lib/arturo/special_handling.rb
|
117
117
|
- lib/arturo.rb
|
118
118
|
- lib/generators/arturo/assets_generator.rb
|
data/lib/arturo/configuration.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/module'
|
2
|
-
|
3
|
-
module Arturo
|
4
|
-
|
5
|
-
# A block that, when bound to a Controller instance, returns
|
6
|
-
# true iff the current user can manage features.
|
7
|
-
mattr_writer :permit_management
|
8
|
-
@@permit_management = lambda { false }
|
9
|
-
|
10
|
-
# A block that, when bound to a Controller or Helper instance,
|
11
|
-
# returns an object (probably a User, Person, or Account),
|
12
|
-
# that may or may not have features enabled.
|
13
|
-
mattr_writer :feature_recipient
|
14
|
-
@@feature_recipient = lambda { nil }
|
15
|
-
|
16
|
-
# Get or set Arturo's permission block.
|
17
|
-
# @param [block] block
|
18
|
-
# @return [block]
|
19
|
-
# @see Arturo.permission_block
|
20
|
-
def self.permit_management(&block)
|
21
|
-
if block
|
22
|
-
@@permit_management = block
|
23
|
-
end
|
24
|
-
@@permit_management
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.permit_management?(controller)
|
28
|
-
block = self.permit_management
|
29
|
-
block && block.call(controller)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Get or set Arturo's block for retrieving the
|
33
|
-
# thing that may or may not have features enabled.
|
34
|
-
# @param [block] block
|
35
|
-
# @return [block]
|
36
|
-
def self.feature_recipient(&block)
|
37
|
-
if block
|
38
|
-
@@feature_recipient = block
|
39
|
-
end
|
40
|
-
@@feature_recipient
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|