arturo 0.2.3.8 → 1.0.0

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/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