Permalink
Browse files

Arturo now supports Rails 2.3

  - combined all generators into one ArturoGenerator
  - re-ran generators
  - FeaturesController uses Rails 2's respond_to { |format| ... }
  - changed Feature.where to Rails 2's Feature.find(:first, :conditions => ...)
  - changed <%= form_for... %> to <% form_for... %>
  - added range_field to FormBuilder
  - Feature stores :symbol as a String
  - added note about declaring Arturo as a Gem/Plugin in environment.rb
  • Loading branch information...
1 parent 13de080 commit 68746ab6c9b62870fb0d1f3b4f4d9b7a58f6451a James A. Rosen committed Oct 30, 2010
Showing with 328 additions and 128 deletions.
  1. +8 −0 README.md
  2. +20 −8 app/controllers/arturo/features_controller.rb
  3. +3 −2 app/helpers/arturo/features_helper.rb
  4. +11 −2 app/models/arturo/feature.rb
  5. +1 −1 app/views/arturo/features/_form.html.erb
  6. +1 −1 app/views/arturo/features/index.html.erb
  7. +0 −14 config/routes.rb
  8. +2 −1 lib/arturo.rb
  9. +14 −9 lib/arturo/engine.rb
  10. +17 −0 lib/arturo/range_form_support.rb
  11. +40 −0 lib/generators/arturo/arturo_generator.rb
  12. +0 −18 lib/generators/arturo/assets_generator.rb
  13. +0 −13 lib/generators/arturo/initializer_generator.rb
  14. +0 −27 lib/generators/arturo/migration_generator.rb
  15. +0 −15 lib/generators/arturo/routes_generator.rb
  16. +37 −0 test/dummy_app/Gemfile.lock
  17. +0 −1 test/dummy_app/app/views/layouts/application.html.erb
  18. +1 −0 test/dummy_app/config/environment.rb
  19. +29 −0 test/dummy_app/config/initializers/arturo_initializer.rb
  20. +2 −1 test/dummy_app/config/routes.rb
  21. +17 −0 test/dummy_app/db/migrate/20101031170337_create_features.rb
  22. +21 −0 test/dummy_app/db/schema.rb
  23. +23 −0 test/dummy_app/public/javascripts/arturo.js
  24. +67 −0 test/dummy_app/public/stylesheets/arturo.css
  25. +1 −0 test/dummy_app/public/stylesheets/arturo_customizations.css
  26. +1 −1 test/dummy_app/test/functional/features_controller_admin_test.rb
  27. +0 −9 test/dummy_app/test/performance/browsing_test.rb
  28. +10 −2 test/dummy_app/test/unit/engine_test.rb
  29. +1 −2 test/dummy_app/test/unit/feature_availability_test.rb
  30. +1 −1 test/dummy_app/test/unit/feature_test.rb
View
8 README.md
@@ -80,9 +80,17 @@ latest version of Rails 2.3 support.
### In Rails 2.3, with Bundler
+In your `Gemfile`:
+
gem 'arturo', :git => 'git://github.com/jamesarosen/arturo.git',
:version => '~> 0.2.3'
+Unfortunately, Rails 2.3 won't automatically load Arturo as a Plugin
+(and thus not pick up its "engine-ness") without **also** declaring
+it as a Rails gem. Thus, in `config/environment.rb`:
+
+ config.gem 'arturo', :version => '~> 0.2.3'
+
### In Rails 2.3, without Bundler
Put the `rails_2_3` branch of `git://github.com/jamesarosen/arturo.git` into
View
28 app/controllers/arturo/features_controller.rb
@@ -1,8 +1,5 @@
require 'action_controller'
-# TODO: this doesn't do anything radically out of the ordinary.
-# Are there Rails 3 patterns/mixins/methods I can use
-# to clean it up a bit?
module Arturo
begin
@@ -20,13 +17,16 @@ class FeaturesController < base
include Arturo::FeatureManagement
unloadable
- respond_to :html, :json, :xml
before_filter :require_permission
before_filter :load_feature, :only => [ :show, :edit, :update, :destroy ]
def index
@features = Arturo::Feature.all
- respond_with @features
+ respond_to do |format|
+ format.html { }
+ format.json { render :json => @features }
+ format.xml { render :xml => @features }
+ end
end
def update_all
@@ -52,12 +52,20 @@ def update_all
end
def show
- respond_with @feature
+ respond_to do |format|
+ format.html { }
+ format.json { render :json => @feature }
+ format.xml { render :xml => @feature }
+ end
end
def new
@feature = Arturo::Feature.new(params[:feature])
- respond_with @feature
+ respond_to do |format|
+ format.html { }
+ format.json { render :json => @feature }
+ format.xml { render :xml => @feature }
+ end
end
def create
@@ -72,7 +80,11 @@ def create
end
def edit
- respond_with @feature
+ respond_to do |format|
+ format.html { }
+ format.json { render :json => @feature }
+ format.xml { render :xml => @feature }
+ end
end
def update
View
5 app/helpers/arturo/features_helper.rb
@@ -25,9 +25,10 @@ def deployment_percentage_output_tag(id, value)
end
def error_messages_for(feature, attribute)
- if feature.errors[attribute].any?
+ errors = feature.errors.on(attribute)
+ if errors && errors.any?
content_tag(:ul, :class => 'errors') do
- feature.errors[attribute].map { |msg| content_tag(:li, msg, :class => 'error') }.join(''.html_safe)
+ errors.map { |msg| content_tag(:li, msg, :class => 'error') }.join(''.html_safe)
end
else
''
View
13 app/models/arturo/feature.rb
@@ -25,7 +25,7 @@ class Feature < ::ActiveRecord::Base
# @return [Arturo::Feature, nil] the Feature if found, else nil
def self.to_feature(feature_or_symbol)
return feature_or_symbol if feature_or_symbol.kind_of?(self)
- self.where(:symbol => feature_or_symbol.to_sym).first
+ self.find(:first, :conditions => { :symbol => feature_or_symbol.to_s })
end
# Create a new Feature
@@ -56,13 +56,22 @@ def to_s
end
def to_param
- persisted? ? "#{id}-#{symbol.to_s.parameterize}" : nil
+ new_record? ? nil : "#{id}-#{symbol.to_s.parameterize}"
end
def inspect
"<Arturo::Feature #{name}, deployed to #{deployment_percentage}%>"
end
+ def symbol
+ sym = read_attribute(:symbol).to_s
+ sym.blank? ? nil : sym.to_sym
+ end
+
+ def symbol=(sym)
+ write_attribute(:symbol, sym.to_s)
+ end
+
protected
def passes_threshold?(feature_recipient)
View
2 app/views/arturo/features/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for(feature, :as => 'feature', :url => (feature.new_record? ? features_path : feature_path(feature))) do |form| %>
+<% form_for(feature, :as => 'feature', :url => (feature.new_record? ? features_path : feature_path(feature))) do |form| %>
<fieldset>
<legend><%= legend %></legend>
View
2 app/views/arturo/features/index.html.erb
@@ -1,5 +1,5 @@
<h2><%= t('.title') %></h2>
-<%= form_tag(features_path, :method => 'put', 'data-update-path' => feature_path(:id => ':id'), :remote => true) do %>
+<% form_tag(features_path, :method => 'put', 'data-update-path' => feature_path(:id => ':id'), :remote => true) do %>
<fieldset>
<legend><%= t('.title') %></legend>
<table class='features'>
View
14 config/routes.rb
@@ -1,14 +0,0 @@
-# In Rails edge, the engine can have its own route set
-# and be mounted within an application at a sub-URL.
-# In 3.0.1, this is not yet available.
-
-# TODO replace this with the commented-out version below
-Rails.application.routes.draw do
- resources :features, :controller => 'arturo/features'
- put 'features', :to => 'arturo/features#update_all', :as => 'features'
-end
-
-# Arturo::Engine.routes.draw do
-# resources :features, :controller => 'arturo/features'
-# put 'features', :to => 'arturo/features#update_all', :as => 'features'
-# end
View
3 lib/arturo.rb
@@ -4,6 +4,7 @@ module Arturo
require 'arturo/feature_availability'
require 'arturo/feature_management'
require 'arturo/controller_filters'
- require 'arturo/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
+ require 'arturo/range_form_support'
+ require 'arturo/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 2 && Rails::VERSION::MINOR == 3
end
View
23 lib/arturo/engine.rb
@@ -1,10 +1,15 @@
-module Arturo
- class Engine < ::Rails::Engine
- ActiveSupport.on_load(:action_controller) do
- include Arturo::FeatureAvailability
- helper Arturo::FeatureAvailability
- include Arturo::ControllerFilters
- helper Arturo::FeatureManagement
- end
- end
+ActionController::Base.class_eval do
+ include Arturo::FeatureAvailability
+ helper Arturo::FeatureAvailability
+ include Arturo::ControllerFilters
+ helper Arturo::FeatureManagement
+ helper Arturo::RangeFormSupport::HelperMethods
end
+
+ActionView::Helpers::FormBuilder.instance_eval do
+ include Arturo::RangeFormSupport::FormBuilderMethods
+end
+
+require 'rails_generator'
+generators_path = File.expand_path('../../generators', __FILE__)
+Rails::Generator::Base.sources << Rails::Generator::PathSource.new(:arturo, generators_path)
View
17 lib/arturo/range_form_support.rb
@@ -0,0 +1,17 @@
+module Arturo
+ module RangeFormSupport
+
+ module HelperMethods
+ def range_field(object_name, method, options = {})
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("range", options)
+ end
+ end
+
+ module FormBuilderMethods
+ def range_field(method, options = {})
+ @template.send('range_field', @object_name, method, objectify_options(options))
+ end
+ end
+
+ end
+end
View
40 lib/generators/arturo/arturo_generator.rb
@@ -0,0 +1,40 @@
+require 'rails_generator'
+
+class ArturoGenerator < Rails::Generator::Base
+ def manifest
+ record do |m|
+ m.file 'initializer.rb', 'config/initializers/arturo_initializer.rb'
+ m.file 'arturo.css', 'public/stylesheets/arturo.css', :collision => :force
+ m.file 'arturo_customizations.css', 'public/stylesheets/arturo_customizations.css', :collision => :skip
+ m.file 'arturo.js', 'public/javascripts/arturo.js'
+ m.file 'semicolon.png', 'public/images/semicolon.png'
+ m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => 'create_features'
+ add_feature_routes(m)
+ end
+ end
+
+ protected
+
+ def source_root
+ File.expand_path('../templates', __FILE__)
+ end
+
+ def banner
+ %{Usage: #{$0} #{spec.name}\nCopies an initializer; copies CSS, JS, and PNG assets; generates a migration; adds routes/}
+ end
+
+ def add_feature_routes(manifest)
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
+ logger.route "map.resources features"
+ unless options[:pretend]
+ manifest.gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
+ "#{match}#{feature_routes}\n"
+ end
+ end
+ end
+
+ def feature_routes
+ "\n map.resources :features, :controller => 'arturo/features'" +
+ "\n map.features 'features', :controller => 'arturo/features', :action => 'update_all', :conditions => { :method => :put }"
+ end
+end
View
18 lib/generators/arturo/assets_generator.rb
@@ -1,18 +0,0 @@
-require 'rails/generators'
-
-module Arturo
- class AssetsGenerator < Rails::Generators::Base
- def self.source_root
- File.join(File.dirname(__FILE__), 'templates')
- end
-
- def copy_assets
- copy_file 'arturo.css', 'public/stylesheets/arturo.css', :force => true
- copy_file 'arturo_customizations.css', 'public/stylesheets/arturo_customizations.css', :skip => true
- copy_file 'arturo.js', 'public/javascripts/arturo.js'
- copy_file 'semicolon.png', 'public/images/semicolon.png'
- end
-
- end
-end
-
View
13 lib/generators/arturo/initializer_generator.rb
@@ -1,13 +0,0 @@
-require 'rails/generators'
-
-module Arturo
- class InitializerGenerator < Rails::Generators::Base
- def self.source_root
- File.join(File.dirname(__FILE__), 'templates')
- end
-
- def copy_initializer_file
- copy_file "initializer.rb", "config/initializers/arturo_initializer.rb"
- end
- end
-end
View
27 lib/generators/arturo/migration_generator.rb
@@ -1,27 +0,0 @@
-require 'rails/generators'
-require 'rails/generators/migration'
-
-module Arturo
- class MigrationGenerator < Rails::Generators::Base
- include Rails::Generators::Migration
-
- def self.source_root
- File.join(File.dirname(__FILE__), 'templates')
- end
-
- # Implement the required interface for Rails::Generators::Migration.
- # taken from
- # http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
- def self.next_migration_number(dirname) #:nodoc:
- if ActiveRecord::Base.timestamped_migrations
- Time.now.utc.strftime("%Y%m%d%H%M%S")
- else
- "%.3d" % (current_migration_number(dirname) + 1)
- end
- end
-
- def create_migration_file
- migration_template 'migration.rb', 'db/migrate/create_features.rb'
- end
- end
-end
View
15 lib/generators/arturo/routes_generator.rb
@@ -1,15 +0,0 @@
-require 'rails/generators'
-
-module Arturo
- class RoutesGenerator < Rails::Generators::Base
-
- def add_mount
- if Arturo::Engine.respond_to?(:routes)
- route "mount Arturo::Engine => ''"
- else
- puts "This version of Rails doesn't support Engine-specific routing. Nothing to do."
- end
- end
-
- end
-end
View
37 test/dummy_app/Gemfile.lock
@@ -0,0 +1,37 @@
+PATH
+ remote: /Users/jamesrosen/Projects/rmu/arturo
+ specs:
+ arturo (0.2.3.1)
+ rails (~> 2.3.8)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ actionmailer (2.3.10)
+ actionpack (= 2.3.10)
+ actionpack (2.3.10)
+ activesupport (= 2.3.10)
+ rack (~> 1.1.0)
+ activerecord (2.3.10)
+ activesupport (= 2.3.10)
+ activeresource (2.3.10)
+ activesupport (= 2.3.10)
+ activesupport (2.3.10)
+ rack (1.1.0)
+ rails (2.3.10)
+ actionmailer (= 2.3.10)
+ actionpack (= 2.3.10)
+ activerecord (= 2.3.10)
+ activeresource (= 2.3.10)
+ activesupport (= 2.3.10)
+ rake (>= 0.8.3)
+ rake (0.8.7)
+ sqlite3-ruby (1.3.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ arturo!
+ rails (~> 2.3.8)
+ sqlite3-ruby
View
1 test/dummy_app/app/views/layouts/application.html.erb
@@ -4,7 +4,6 @@
<title>DummyApp</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag 'jquery-1.4.3.min', 'arturo' %>
- <%= csrf_meta_tag %>
</head>
<body>
View
1 test/dummy_app/config/environment.rb
@@ -19,6 +19,7 @@
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
# config.gem "sqlite3-ruby", :lib => "sqlite3"
# config.gem "aws-s3", :lib => "aws/s3"
+ config.gem 'arturo', :version => '~> 0.2.3'
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named
View
29 test/dummy_app/config/initializers/arturo_initializer.rb
@@ -0,0 +1,29 @@
+require 'arturo'
+
+# Configure who may manage features here.
+# The following is the default implementation.
+# Arturo::FeatureManagement.class_eval do
+# def may_manage_features?
+# current_user.present? && current_user.admin?
+# end
+# end
+
+# Configure what receives features here.
+# The following is the default implementation.
+# Arturo::FeatureAvailability.class_eval do
+# def feature_recipient
+# current_user
+# end
+# end
+
+# Whitelists and Blacklists:
+#
+# Enable feature one for all admins:
+# Arturo::Feature.whitelist(:feature_one) do |user|
+# user.admin?
+# end
+#
+# Disable feature two for all small accounts:
+# Arturo::Feature.blacklist(:feature_two) do |user|
+# user.account.small?
+# end
View
3 test/dummy_app/config/routes.rb
@@ -1,6 +1,7 @@
ActionController::Routing::Routes.draw do |map|
+ map.resources :features, :controller => 'arturo/features'
+ map.features '/features', :controller => 'arturo/features', :action => 'update_all', :conditions => { :method => :put }
map.resources :books, :only => ['show'],
:member => { 'holds' => 'post' }
-
end
View
17 test/dummy_app/db/migrate/20101031170337_create_features.rb
@@ -0,0 +1,17 @@
+require 'active_support/core_ext'
+
+class CreateFeatures < ActiveRecord::Migration
+ def self.up
+ create_table :features do |t|
+ t.string :symbol, :null => false
+ t.integer :deployment_percentage, :null => false
+ #Any additional fields here
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :features
+ end
+end
View
21 test/dummy_app/db/schema.rb
@@ -0,0 +1,21 @@
+# This file is auto-generated from the current state of the database. Instead of editing this file,
+# please use the migrations feature of Active Record to incrementally modify your database, and
+# then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
+
+ActiveRecord::Schema.define(:version => 20101031170337) do
+
+ create_table "features", :force => true do |t|
+ t.string "symbol", :null => false
+ t.integer "deployment_percentage", :null => false
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+end
View
23 test/dummy_app/public/javascripts/arturo.js
@@ -0,0 +1,23 @@
+if (typeof(jQuery) === 'function') {
+ jQuery.arturo = {
+ agentSupportsHTML5Output: ('for' in jQuery('<output />')),
+
+ linkAndShowOutputs: function() {
+ if (jQuery.arturo.agentSupportsHTML5Output) {
+ jQuery('.features output,.feature_new output,.feature_edit output').each(function(i, output) {
+ var output = jQuery(output);
+ var input = jQuery('#' + output.attr('for'));
+ input.change(function() {
+ console.log('input value changed to ' + input.val());
+ output.val(input.val());
+ });
+ output.removeClass('no_js');
+ });
+ }
+ }
+ };
+
+ jQuery(function() {
+ jQuery.arturo.linkAndShowOutputs();
+ });
+}
View
67 test/dummy_app/public/stylesheets/arturo.css
@@ -0,0 +1,67 @@
+/*
+ WARNING:
+
+ Do not edit this file. Any changes you make to this file will be overwritten
+ when you regenerate the arturo assets (which happens when you upgrade the gem).
+ Instead, make customizations to arturo_customizations.css.
+ */
+
+.features code.symbol:before { content: ":"; }
+
+.features { border-collapse: collapse; }
+
+.features thead tr:last-child th { border-bottom: 1px solid; }
+.features tfoot tr:first-child th { border-top: 1px solid; }
+
+.features th, .features td {
+ margin: 0;
+ padding: 0.5em 1.5em;
+ text-align: left;
+}
+
+input.deployment_percentage[type=range] { width: 200px; }
+
+output.deployment_percentage.no_js { display: none; }
+output.deployment_percentage { margin-left: 1em; }
+output.deployment_percentage:after { content: "%"; }
+
+.features a[rel=edit] { visibility: hidden; }
+.features tr:hover a[rel=edit] { visibility: inherit; }
+
+.features tfoot th {
+ text-align: right;
+}
+
+.features tfoot th * + * {
+ margin-left: 2em;
+}
+
+.feature_new label, .feature_edit label { font-weight: bold; }
+
+.feature_new label, .feature_new .errors,
+.feature_edit label, .feature_edit .errors {
+ display: block;
+}
+
+.feature_new label + input, .feature_new label + textarea, .feature_new label + select,
+.feature_edit label + input, .feature_edit label + textarea, .feature_edit label + select {
+ margin-top: 0.5em;
+}
+
+.feature_new input + label, .feature_new textarea + label, .feature_new select + label,
+.feature_edit input + label, .feature_edit textarea + label, .feature_edit select + label {
+ margin-top: 1.5em;
+}
+
+.feature_new input[type=text], .feature_edit input[type=text] { padding: 0.5em; }
+
+.feature_new input.symbol, .feature_edit input.symbol {
+ background: transparent url('/images/semicolon.png') no-repeat 3px 4px;
+ font-family: "DejaVu Sans Mono", "Droid Sans Mono", "Mondale", monospace;
+ padding-left: 9px;
+}
+
+.feature_new .errors, .feature_edit .errors { color: red; }
+.feature_new :invalid { border-color: red; }
+
+.feature_new footer, .feature_edit footer { margin-top: 2em; }
View
1 test/dummy_app/public/stylesheets/arturo_customizations.css
@@ -0,0 +1 @@
+/* Make any customizations to the Arturo styles here */
View
2 test/dummy_app/test/functional/features_controller_admin_test.rb
@@ -20,7 +20,7 @@ def test_get_index
get :index
assert_response :success
assert_select 'table tbody tr input[type=range]'
- assert_select 'table tfoot a[href=?]', @controller.new_feature_path
+ assert_select 'table tfoot a[href=?]', @controller.send(:new_feature_path)
assert_select 'table tfoot input[type=submit]'
end
View
9 test/dummy_app/test/performance/browsing_test.rb
@@ -1,9 +0,0 @@
-require 'test_helper'
-require 'performance_test_help'
-
-# Profiling results for each test method are written to tmp/performance.
-class BrowsingTest < ActionController::PerformanceTest
- def test_homepage
- get '/'
- end
-end
View
12 test/dummy_app/test/unit/engine_test.rb
@@ -13,7 +13,7 @@ def test_feature_availability_methods_are_not_actions
end
def test_feature_availability_is_a_helper
- assert Arturo::FeaturesController._helpers < Arturo::FeatureAvailability
+ assert Arturo::FeaturesController.master_helper_module < Arturo::FeatureAvailability
end
def test_controllers_include_filters
@@ -25,11 +25,19 @@ def test_controller_filter_methods_are_not_actions
end
def test_feature_management_is_a_helper
- assert BooksController._helpers < Arturo::FeatureManagement
+ assert BooksController.master_helper_module < Arturo::FeatureManagement
end
def test_feature_management_methods_are_not_actions
assert !BooksController.action_methods.include?('may_manage_features?')
end
+ def test_range_support_is_a_helper
+ assert BooksController.master_helper_module < Arturo::RangeFormSupport::HelperMethods
+ end
+
+ def test_form_builder_has_range_tag
+ assert ActionView::Helpers::FormBuilder < Arturo::RangeFormSupport::FormBuilderMethods
+ end
+
end
View
3 test/dummy_app/test/unit/feature_availability_test.rb
@@ -31,8 +31,7 @@ def test_if_feature_enabled_with_disabled_feature
def test_if_feature_enabled_with_enabled_feature
@feature.stubs(:enabled_for?).returns(true)
- @block.expects(:call).once.returns('result')
- assert_equal 'result', @helper.if_feature_enabled(@feature, &@block)
+ assert_equal 'Content that requires a feature', @helper.if_feature_enabled(@feature, &@block)
end
end
View
2 test/dummy_app/test/unit/feature_test.rb
@@ -80,6 +80,6 @@ def test_to_s
end
def test_to_param
- assert_match feature.to_param, %r{^#{feature.id}-}
+ assert_match %r{^#{feature.id}-}, feature.to_param
end
end

0 comments on commit 68746ab

Please sign in to comment.