arturo 0.2.3.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md DELETED
@@ -1,16 +0,0 @@
1
- ## v 1.1.0 - cleanup
2
- * changed `require_feature!` to `require_feature`
3
- * replaced `Arturo.permit_management` and `Arturo.feature_recipient`
4
- blocks with instance methods
5
- `Arturo::FeatureManagement.may_manage_features?` and
6
- `Arturo::FeatureAvailability.feature_recipient`
7
-
8
- ## v 1.0.0 - Initial Release
9
- * `require_feature!` controller filter
10
- * `if_feature_enabled` controller and view method
11
- * `feature_enabled?` controller and view method
12
- * CRUD for features
13
- * `Arturo.permit_management` to configure management permission
14
- * `Arturo.feature_recipient` to configure on what basis features are deployed
15
- * whitelists and blacklists
16
-
@@ -1,141 +0,0 @@
1
- module Arturo
2
-
3
- # To be extended by Arturo::Feature if you want to enable
4
- # in-memory caching.
5
- # NB: Arturo's feature caching only works when using
6
- # Arturo::Feature.to_feature or when using the helper methods
7
- # in Arturo and Arturo::FeatureAvailability.
8
- # NB: if you have multiple application servers, you almost certainly
9
- # want to clear this cache after each request:
10
- #
11
- # class ApplicationController < ActionController::Base
12
- # after_filter { Arturo::Feature.clear_feature_cache }
13
- # end
14
- #
15
- # Alternatively, you could redefine Arturo::Feature.feature_cache
16
- # to use a shared cache like Memcached.
17
- module FeatureCaching
18
-
19
- # A marker in the cache to record the fact that the feature with the
20
- # given symbol doesn't exist.
21
- NO_SUCH_FEATURE = :NO_SUCH_FEATURE
22
-
23
- def self.extended(base)
24
- class << base
25
- alias_method_chain :to_feature, :caching
26
- attr_accessor :cache_ttl, :feature_cache, :feature_caching_strategy
27
- end
28
- base.send(:after_save) do |f|
29
- f.class.feature_caching_strategy.expire(f.class.feature_cache, f.symbol.to_sym) if f.class.caches_features?
30
- end
31
- base.cache_ttl = 0
32
- base.feature_cache = Arturo::FeatureCaching::Cache.new
33
- base.feature_caching_strategy = AllStrategy
34
- end
35
-
36
- def caches_features?
37
- self.cache_ttl.to_i > 0
38
- end
39
-
40
- # Wraps Arturo::Feature.to_feature with in-memory caching.
41
- def to_feature_with_caching(feature_or_symbol)
42
- if !caches_features?
43
- to_feature_without_caching(feature_or_symbol)
44
- elsif feature_or_symbol.kind_of?(Arturo::Feature)
45
- feature_or_symbol
46
- else
47
- symbol = feature_or_symbol.to_sym
48
- feature = feature_caching_strategy.fetch(feature_cache, symbol) { to_feature_without_caching(symbol) }
49
- feature unless feature == NO_SUCH_FEATURE
50
- end
51
- end
52
-
53
- def warm_cache!
54
- warn "Deprecated, no longer necessary!"
55
- end
56
-
57
- class AllStrategy
58
- class << self
59
- def fetch(cache, symbol, &block)
60
- features = cache.read("arturo.all")
61
-
62
- unless cache_is_current?(cache, features)
63
- features = Hash[Arturo::Feature.all.map { |f| [f.symbol.to_sym, f] }]
64
- mark_as_current!(cache)
65
- cache.write("arturo.all", features, :expires_in => Arturo::Feature.cache_ttl * 10)
66
- end
67
-
68
- features[symbol] || NO_SUCH_FEATURE
69
- end
70
-
71
- def expire(cache, symbol)
72
- cache.delete("arturo.all")
73
- end
74
-
75
- private
76
-
77
- def cache_is_current?(cache, features)
78
- return unless features
79
- return true if cache.read("arturo.current")
80
- return false if features.values.map(&:updated_at).compact.max != Arturo::Feature.maximum(:updated_at)
81
- mark_as_current!(cache)
82
- end
83
-
84
- def mark_as_current!(cache)
85
- cache.write("arturo.current", true, :expires_in => Arturo::Feature.cache_ttl)
86
- end
87
- end
88
- end
89
-
90
- class OneStrategy
91
- def self.fetch(cache, symbol, &block)
92
- if feature = cache.read("arturo.#{symbol}")
93
- feature
94
- else
95
- cache.write("arturo.#{symbol}", yield || NO_SUCH_FEATURE, :expires_in => Arturo::Feature.cache_ttl)
96
- end
97
- end
98
-
99
- def self.expire(cache, symbol)
100
- cache.delete("arturo.#{symbol}")
101
- end
102
- end
103
-
104
- # Quack like a Rails cache.
105
- class Cache
106
- def initialize
107
- @data = {} # of the form {key => [value, expires_at or nil]}
108
- end
109
-
110
- def read(name, options = nil)
111
- value, expires_at = *@data[name]
112
- if value && (expires_at.blank? || expires_at > Time.now)
113
- value
114
- else
115
- nil
116
- end
117
- end
118
-
119
- def delete(name)
120
- @data.delete(name)
121
- end
122
-
123
- def write(name, value, options = nil)
124
- expires_at = if options && options.respond_to?(:[]) && options[:expires_in]
125
- Time.now + options.delete(:expires_in)
126
- else
127
- nil
128
- end
129
- value.freeze.tap do |val|
130
- @data[name] = [value, expires_at]
131
- end
132
- end
133
-
134
- def clear
135
- @data.clear
136
- end
137
- end
138
-
139
- end
140
-
141
- end
@@ -1,23 +0,0 @@
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,60 +0,0 @@
1
- module Arturo
2
- # A Rack middleware that requires a feature to be present. By default,
3
- # checks feature availability against an `arturo.recipient` object
4
- # in the `env`. If that object is missing, this middleware always fails,
5
- # even if the feature is available for everyone.
6
- #
7
- # ## Usage
8
- #
9
- # use Arturo::Middleware, :feature => :foo
10
- #
11
- # ## Options
12
- #
13
- # * feature -- the name of the feature to require, as a Symbol; required
14
- #
15
- # * recipient -- the key in the `env` hash under which the feature
16
- # recipient can be found; defaults to "arturo.recipient".
17
- # * on_unavailable -- a Rack-like object
18
- # (has `#call(Hash) -> [status, headers, body]`) that
19
- # is called when the feature is unavailable; defaults
20
- # to returning `[ 404, {}, ['Not Found'] ]`.
21
- class Middleware
22
-
23
- MISSING_FEATURE_ERROR = "Cannot create an Arturo::Middleware without a :feature"
24
-
25
- DEFAULT_RECIPIENT_KEY = 'arturo.recipient'
26
-
27
- DEFAULT_ON_UNAVAILABLE = lambda { |env| [ 404, {}, ['Not Found'] ] }
28
-
29
- def initialize(app, options = {})
30
- @app = app
31
- @feature = options[:feature]
32
- raise ArgumentError.new(MISSING_FEATURE_ERROR) unless @feature
33
- @recipient_key = options[:recipient] || DEFAULT_RECIPIENT_KEY
34
- @on_unavailable = options[:on_unavailable] || DEFAULT_ON_UNAVAILABLE
35
- end
36
-
37
- def call(env)
38
- if enabled_for_recipient?(env)
39
- @app.call(env)
40
- else
41
- fail(env)
42
- end
43
- end
44
-
45
- private
46
-
47
- def enabled_for_recipient?(env)
48
- ::Arturo.feature_enabled_for?(@feature, recipient(env))
49
- end
50
-
51
- def recipient(env)
52
- env[@recipient_key]
53
- end
54
-
55
- def fail(env)
56
- @on_unavailable.call(env)
57
- end
58
-
59
- end
60
- end
@@ -1,17 +0,0 @@
1
- module Arturo
2
- module RangeFormSupport
3
-
4
- module HelperMethods
5
- def range_field(object_name, method, options = {})
6
- ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("range", options)
7
- end
8
- end
9
-
10
- module FormBuilderMethods
11
- def range_field(method, options = {})
12
- @template.send('range_field', @object_name, method, objectify_options(options))
13
- end
14
- end
15
-
16
- end
17
- end
@@ -1,32 +0,0 @@
1
- Arturo.instance_eval do
2
-
3
- # Enable a feature; create it if necessary.
4
- # For use in testing. Not auto-required on load. To load,
5
- #
6
- # require 'arturo/test_support'
7
- #
8
- # @param [Symbol, String] name the feature name
9
- def enable_feature!(name)
10
- feature = Arturo::Feature.to_feature(name)
11
- if feature
12
- feature = feature.class.find(feature.id) if feature.frozen?
13
- feature.update_attributes(:deployment_percentage => 100)
14
- else
15
- Arturo::Feature.create!(:symbol => name, :deployment_percentage => 100)
16
- end
17
- end
18
-
19
- # Disable a feature if it exists.
20
- # For use in testing. Not auto-required on load. To load,
21
- #
22
- # require 'arturo/test_support'
23
- #
24
- # @param [Symbol, String] name the feature name
25
- def disable_feature!(name)
26
- if (feature = Arturo::Feature.to_feature(name))
27
- feature = feature.class.find(feature.id) if feature.frozen?
28
- feature.update_attributes(:deployment_percentage => 0)
29
- end
30
- end
31
-
32
- end
@@ -1,40 +0,0 @@
1
- require 'rails_generator'
2
-
3
- class ArturoGenerator < Rails::Generator::Base
4
- def manifest
5
- record do |m|
6
- m.file 'initializer.rb', 'config/initializers/arturo_initializer.rb'
7
- m.file 'arturo.css', 'public/stylesheets/arturo.css', :collision => :force
8
- m.file 'arturo_customizations.css', 'public/stylesheets/arturo_customizations.css', :collision => :skip
9
- m.file 'arturo.js', 'public/javascripts/arturo.js'
10
- m.file 'semicolon.png', 'public/images/semicolon.png'
11
- m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => 'create_features'
12
- add_feature_routes(m)
13
- end
14
- end
15
-
16
- protected
17
-
18
- def source_root
19
- File.expand_path('../templates', __FILE__)
20
- end
21
-
22
- def banner
23
- %{Usage: #{$0} #{spec.name}\nCopies an initializer; copies CSS, JS, and PNG assets; generates a migration; adds routes/}
24
- end
25
-
26
- def add_feature_routes(manifest)
27
- sentinel = 'ActionController::Routing::Routes.draw do |map|'
28
- logger.route "map.resources features"
29
- unless options[:pretend]
30
- manifest.gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
31
- "#{match}#{feature_routes}\n"
32
- end
33
- end
34
- end
35
-
36
- def feature_routes
37
- "\n map.resources :features, :controller => 'arturo/features'" +
38
- "\n map.features 'features', :controller => 'arturo/features', :action => 'update_all', :conditions => { :method => :put }"
39
- end
40
- end
data/rails/init.rb DELETED
@@ -1,7 +0,0 @@
1
- if ActiveSupport::Dependencies.respond_to?(:autoload_once_paths)
2
- # 2.3.9+
3
- ActiveSupport::Dependencies.autoload_once_paths << lib_path
4
- elsif ActiveSupport::Dependencies.respond_to?(:load_once_paths)
5
- # 2.3.8-
6
- ActiveSupport::Dependencies.load_once_paths << lib_path
7
- end