enphase-arturo 1.3.2

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.
Files changed (35) hide show
  1. data/HISTORY.md +16 -0
  2. data/README.md +295 -0
  3. data/app/controllers/arturo/features_controller.rb +105 -0
  4. data/app/helpers/arturo/features_helper.rb +37 -0
  5. data/app/models/arturo/feature.rb +75 -0
  6. data/app/views/arturo/features/_feature.html.erb +5 -0
  7. data/app/views/arturo/features/_form.html.erb +16 -0
  8. data/app/views/arturo/features/edit.html.erb +2 -0
  9. data/app/views/arturo/features/forbidden.html.erb +2 -0
  10. data/app/views/arturo/features/index.html.erb +29 -0
  11. data/app/views/arturo/features/new.html.erb +2 -0
  12. data/app/views/arturo/features/show.html.erb +2 -0
  13. data/config/locales/en.yml +40 -0
  14. data/config/routes.rb +14 -0
  15. data/lib/arturo.rb +37 -0
  16. data/lib/arturo/controller_filters.rb +33 -0
  17. data/lib/arturo/engine.rb +10 -0
  18. data/lib/arturo/feature_availability.rb +37 -0
  19. data/lib/arturo/feature_caching.rb +83 -0
  20. data/lib/arturo/feature_factories.rb +4 -0
  21. data/lib/arturo/feature_management.rb +23 -0
  22. data/lib/arturo/middleware.rb +60 -0
  23. data/lib/arturo/special_handling.rb +62 -0
  24. data/lib/arturo/test_support.rb +30 -0
  25. data/lib/generators/arturo/assets_generator.rb +18 -0
  26. data/lib/generators/arturo/initializer_generator.rb +13 -0
  27. data/lib/generators/arturo/migration_generator.rb +27 -0
  28. data/lib/generators/arturo/routes_generator.rb +15 -0
  29. data/lib/generators/arturo/templates/arturo.css +67 -0
  30. data/lib/generators/arturo/templates/arturo.js +23 -0
  31. data/lib/generators/arturo/templates/arturo_customizations.css +1 -0
  32. data/lib/generators/arturo/templates/initializer.rb +29 -0
  33. data/lib/generators/arturo/templates/migration.rb +17 -0
  34. data/lib/generators/arturo/templates/semicolon.png +0 -0
  35. metadata +156 -0
@@ -0,0 +1,60 @@
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
@@ -0,0 +1,62 @@
1
+ module Arturo
2
+
3
+ # Adds whitelist and blacklist support to individual features by name.
4
+ # Blacklists override whitelists. (In the world of Apache, Features
5
+ # are "(deny,allow)".)
6
+ # @example
7
+ # # allow admins:
8
+ # Arturo::Feature.whitelist(:some_feature) do |user|
9
+ # user.is_admin?
10
+ # end
11
+ #
12
+ # # disallow for small accounts:
13
+ # Arturo::Feature.blacklist(:another_feature) do |user|
14
+ # user.account.small?
15
+ # end
16
+ #
17
+ # Blacklists and whitelists can be defined before the feature exists
18
+ # and are not persisted, so they are best defined in initializers.
19
+ # This is particularly important if your application runs in several
20
+ # different processes or on several servers.
21
+ module SpecialHandling
22
+
23
+ def self.included(base)
24
+ base.extend Arturo::SpecialHandling::ClassMethods
25
+ end
26
+
27
+ module ClassMethods
28
+ def whitelists
29
+ @whitelists ||= {}
30
+ end
31
+
32
+ def blacklists
33
+ @blacklists ||= {}
34
+ end
35
+
36
+ def whitelist(feature_symbol, &block)
37
+ whitelists[feature_symbol.to_sym] = block
38
+ end
39
+
40
+ def blacklist(feature_symbol, &block)
41
+ blacklists[feature_symbol.to_sym] = block
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ def whitelisted?(feature_recipient)
48
+ x_listed?(self.class.whitelists, feature_recipient)
49
+ end
50
+
51
+ def blacklisted?(feature_recipient)
52
+ x_listed?(self.class.blacklists, feature_recipient)
53
+ end
54
+
55
+ def x_listed?(list_map, feature_recipient)
56
+ list = list_map[self.symbol.to_sym]
57
+ list.present? && list.call(feature_recipient)
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,30 @@
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.update_attributes(:deployment_percentage => 100)
13
+ else
14
+ Arturo::Feature.create!(:symbol => name, :deployment_percentage => 100)
15
+ end
16
+ end
17
+
18
+ # Disable a feature if it exists.
19
+ # For use in testing. Not auto-required on load. To load,
20
+ #
21
+ # require 'arturo/test_support'
22
+ #
23
+ # @param [Symbol, String] name the feature name
24
+ def disable_feature!(name)
25
+ if (feature = Arturo::Feature.to_feature(name))
26
+ feature.update_attributes(:deployment_percentage => 0)
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/generators'
2
+
3
+ module Arturo
4
+ class AssetsGenerator < Rails::Generators::Base
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), 'templates')
7
+ end
8
+
9
+ def copy_assets
10
+ copy_file 'arturo.css', 'public/stylesheets/arturo.css', :force => true
11
+ copy_file 'arturo_customizations.css', 'public/stylesheets/arturo_customizations.css', :skip => true
12
+ copy_file 'arturo.js', 'public/javascripts/arturo.js'
13
+ copy_file 'semicolon.png', 'public/images/semicolon.png'
14
+ end
15
+
16
+ end
17
+ end
18
+
@@ -0,0 +1,13 @@
1
+ require 'rails/generators'
2
+
3
+ module Arturo
4
+ class InitializerGenerator < Rails::Generators::Base
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), 'templates')
7
+ end
8
+
9
+ def copy_initializer_file
10
+ copy_file "initializer.rb", "config/initializers/arturo_initializer.rb"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Arturo
5
+ class MigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ def self.source_root
9
+ File.join(File.dirname(__FILE__), 'templates')
10
+ end
11
+
12
+ # Implement the required interface for Rails::Generators::Migration.
13
+ # taken from
14
+ # http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
15
+ def self.next_migration_number(dirname) #:nodoc:
16
+ if ActiveRecord::Base.timestamped_migrations
17
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
18
+ else
19
+ "%.3d" % (current_migration_number(dirname) + 1)
20
+ end
21
+ end
22
+
23
+ def create_migration_file
24
+ migration_template 'migration.rb', 'db/migrate/create_features.rb'
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+
3
+ module Arturo
4
+ class RoutesGenerator < Rails::Generators::Base
5
+
6
+ def add_mount
7
+ if Arturo::Engine.respond_to?(:routes)
8
+ route "mount Arturo::Engine => ''"
9
+ else
10
+ puts "This version of Rails doesn't support Engine-specific routing. Nothing to do."
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,67 @@
1
+ /*
2
+ WARNING:
3
+
4
+ Do not edit this file. Any changes you make to this file will be overwritten
5
+ when you regenerate the arturo assets (which happens when you upgrade the gem).
6
+ Instead, make customizations to arturo_customizations.css.
7
+ */
8
+
9
+ .features code.symbol:before { content: ":"; }
10
+
11
+ .features { border-collapse: collapse; }
12
+
13
+ .features thead tr:last-child th { border-bottom: 1px solid; }
14
+ .features tfoot tr:first-child th { border-top: 1px solid; }
15
+
16
+ .features th, .features td {
17
+ margin: 0;
18
+ padding: 0.5em 1.5em;
19
+ text-align: left;
20
+ }
21
+
22
+ input.deployment_percentage[type=range] { width: 200px; }
23
+
24
+ output.deployment_percentage.no_js { display: none; }
25
+ output.deployment_percentage { margin-left: 1em; }
26
+ output.deployment_percentage:after { content: "%"; }
27
+
28
+ .features a[rel=edit] { visibility: hidden; }
29
+ .features tr:hover a[rel=edit] { visibility: inherit; }
30
+
31
+ .features tfoot th {
32
+ text-align: right;
33
+ }
34
+
35
+ .features tfoot th * + * {
36
+ margin-left: 2em;
37
+ }
38
+
39
+ .feature_new label, .feature_edit label { font-weight: bold; }
40
+
41
+ .feature_new label, .feature_new .errors,
42
+ .feature_edit label, .feature_edit .errors {
43
+ display: block;
44
+ }
45
+
46
+ .feature_new label + input, .feature_new label + textarea, .feature_new label + select,
47
+ .feature_edit label + input, .feature_edit label + textarea, .feature_edit label + select {
48
+ margin-top: 0.5em;
49
+ }
50
+
51
+ .feature_new input + label, .feature_new textarea + label, .feature_new select + label,
52
+ .feature_edit input + label, .feature_edit textarea + label, .feature_edit select + label {
53
+ margin-top: 1.5em;
54
+ }
55
+
56
+ .feature_new input[type=text], .feature_edit input[type=text] { padding: 0.5em; }
57
+
58
+ .feature_new input.symbol, .feature_edit input.symbol {
59
+ background: transparent url('/images/semicolon.png') no-repeat 3px 4px;
60
+ font-family: "DejaVu Sans Mono", "Droid Sans Mono", "Mondale", monospace;
61
+ padding-left: 9px;
62
+ }
63
+
64
+ .feature_new .errors, .feature_edit .errors { color: red; }
65
+ .feature_new :invalid { border-color: red; }
66
+
67
+ .feature_new footer, .feature_edit footer { margin-top: 2em; }
@@ -0,0 +1,23 @@
1
+ if (typeof(jQuery) === 'function') {
2
+ jQuery.arturo = {
3
+ agentSupportsHTML5Output: ('for' in jQuery('<output />')),
4
+
5
+ linkAndShowOutputs: function() {
6
+ if (jQuery.arturo.agentSupportsHTML5Output) {
7
+ jQuery('.features output,.feature_new output,.feature_edit output').each(function(i, output) {
8
+ var output = jQuery(output);
9
+ var input = jQuery('#' + output.attr('for'));
10
+ input.change(function() {
11
+ console.log('input value changed to ' + input.val());
12
+ output.val(input.val());
13
+ });
14
+ output.removeClass('no_js');
15
+ });
16
+ }
17
+ }
18
+ };
19
+
20
+ jQuery(function() {
21
+ jQuery.arturo.linkAndShowOutputs();
22
+ });
23
+ }
@@ -0,0 +1 @@
1
+ /* Make any customizations to the Arturo styles here */
@@ -0,0 +1,29 @@
1
+ require 'arturo'
2
+
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
10
+
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
18
+
19
+ # Whitelists and Blacklists:
20
+ #
21
+ # Enable feature one for all admins:
22
+ # Arturo::Feature.whitelist(:feature_one) do |user|
23
+ # user.admin?
24
+ # end
25
+ #
26
+ # Disable feature two for all small accounts:
27
+ # Arturo::Feature.blacklist(:feature_two) do |user|
28
+ # user.account.small?
29
+ # end
@@ -0,0 +1,17 @@
1
+ require 'active_support/core_ext'
2
+
3
+ class CreateFeatures < ActiveRecord::Migration
4
+ def self.up
5
+ create_table :features do |t|
6
+ t.string :symbol, :null => false
7
+ t.integer :deployment_percentage, :null => false
8
+ #Any additional fields here
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+
14
+ def self.down
15
+ drop_table :features
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: enphase-arturo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James A. Rosen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: &2177639760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2177639760
25
+ - !ruby/object:Gem::Dependency
26
+ name: mocha
27
+ requirement: &2177639360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2177639360
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2177638900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2177638900
47
+ - !ruby/object:Gem::Dependency
48
+ name: redgreen
49
+ requirement: &2177638400 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2177638400
58
+ - !ruby/object:Gem::Dependency
59
+ name: sqlite3-ruby
60
+ requirement: &2177637900 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '1.3'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2177637900
69
+ - !ruby/object:Gem::Dependency
70
+ name: factory_girl
71
+ requirement: &2177637440 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '1.3'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2177637440
80
+ - !ruby/object:Gem::Dependency
81
+ name: timecop
82
+ requirement: &2177636980 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ~>
86
+ - !ruby/object:Gem::Version
87
+ version: '0.3'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2177636980
91
+ description: Enphase specific fork. Deploy features incrementally to your users
92
+ email: james.a.rosen@gmail.com
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - lib/arturo/controller_filters.rb
98
+ - lib/arturo/engine.rb
99
+ - lib/arturo/feature_availability.rb
100
+ - lib/arturo/feature_caching.rb
101
+ - lib/arturo/feature_factories.rb
102
+ - lib/arturo/feature_management.rb
103
+ - lib/arturo/middleware.rb
104
+ - lib/arturo/special_handling.rb
105
+ - lib/arturo/test_support.rb
106
+ - lib/arturo.rb
107
+ - lib/generators/arturo/assets_generator.rb
108
+ - lib/generators/arturo/initializer_generator.rb
109
+ - lib/generators/arturo/migration_generator.rb
110
+ - lib/generators/arturo/routes_generator.rb
111
+ - lib/generators/arturo/templates/arturo.css
112
+ - lib/generators/arturo/templates/arturo.js
113
+ - lib/generators/arturo/templates/arturo_customizations.css
114
+ - lib/generators/arturo/templates/initializer.rb
115
+ - lib/generators/arturo/templates/migration.rb
116
+ - lib/generators/arturo/templates/semicolon.png
117
+ - app/controllers/arturo/features_controller.rb
118
+ - app/helpers/arturo/features_helper.rb
119
+ - app/models/arturo/feature.rb
120
+ - app/views/arturo/features/_feature.html.erb
121
+ - app/views/arturo/features/_form.html.erb
122
+ - app/views/arturo/features/edit.html.erb
123
+ - app/views/arturo/features/forbidden.html.erb
124
+ - app/views/arturo/features/index.html.erb
125
+ - app/views/arturo/features/new.html.erb
126
+ - app/views/arturo/features/show.html.erb
127
+ - config/locales/en.yml
128
+ - config/routes.rb
129
+ - README.md
130
+ - HISTORY.md
131
+ homepage: http://github.com/jamesarosen/arturo
132
+ licenses: []
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - .
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ! '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 1.8.11
153
+ signing_key:
154
+ specification_version: 2
155
+ summary: Enphase specific fork. Feature sliders, wrapped up in an engine
156
+ test_files: []