From 796ade8759b629f554f784f369f37743743fa955 Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Wed, 20 Mar 2013 18:26:19 +0900 Subject: [PATCH 1/6] Work with Sinatra To enable ActiveDecorator in Sinatra, register it like: require "active_decorator/sinatra" register ActiveDecorator::Sinatra Then ActiveDecorator::Sinatra reads `ROOT/decorators/*_decorator.rb`, and decorates instance variables in `scope` and local variables passed via `render` method with them. The `ROOT/decorators` directory is used by default. If you want to use another directory, you can set it as `:decorators` like: require "active_decorator/sinatra" set :decorators, "/path/to/your/decorators" register ActiveDecorator::Sinatra ActiveDecorator::Sinatra doesn't decorate following instance variables by default: @default_layout @env @params @preferred_extension @request @response @template_cache These are declared in an array named as `ActiveDecorator::Sinatra::DEFAULT_PROTECTED_INSTANCE_VARIABLES`. If you want to protect other instance variables, you can declare them as `:protected_instance_variables` like: require "active_decorator/sinatra" set :protected_instance_variables, %w[@foo @bar] register ActiveDecorator::Sinatra Note that these `set` statements must be placed before the `register` statement. --- lib/active_decorator.rb | 11 +++++++++- lib/active_decorator/monkey/sinatra.rb | 29 ++++++++++++++++++++++++++ lib/active_decorator/sinatra.rb | 27 ++++++++++++++++++++++++ lib/active_decorator/view_context.rb | 2 ++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 lib/active_decorator/monkey/sinatra.rb create mode 100644 lib/active_decorator/sinatra.rb diff --git a/lib/active_decorator.rb b/lib/active_decorator.rb index 0d20e7ac..75bb9fb9 100644 --- a/lib/active_decorator.rb +++ b/lib/active_decorator.rb @@ -1,3 +1,12 @@ +begin + require 'rails' +rescue LoadError + #do nothing +end + require 'active_decorator/version' require 'active_decorator/decorator' -require 'active_decorator/railtie' + +if defined? Rails + require 'active_decorator/railtie' +end diff --git a/lib/active_decorator/monkey/sinatra.rb b/lib/active_decorator/monkey/sinatra.rb new file mode 100644 index 00000000..b9c3f857 --- /dev/null +++ b/lib/active_decorator/monkey/sinatra.rb @@ -0,0 +1,29 @@ +module Sinatra + class Base + set :decorators, Proc.new { root && File.join(root, 'decorators') } + set :protected_instance_variables, + ActiveDecorator::Sinatra::DEFAULT_PROTECTED_INSTANCE_VARIABLES + + before do + ActiveDecorator::ViewContext.current = self + end + end + module Templates + def render_with_decorate(engine, data, options = {}, locals = {}, &block) + locals = options.delete(:locals) || locals || {} + locals.values.each do |v| + ActiveDecorator::Decorator.instance.decorate v + end + + scope = options.delete(:scope) || self + ivars = scope.instance_variable_names - settings.protected_instance_variables + ivars.each do |n| + ActiveDecorator::Decorator.instance.decorate scope.instance_variable_get(n) + end + options[:scope] = scope + + render_without_decorate(engine, data, options, locals, &block) + end + alias_method_chain :render, :decorate + end +end diff --git a/lib/active_decorator/sinatra.rb b/lib/active_decorator/sinatra.rb new file mode 100644 index 00000000..801c339d --- /dev/null +++ b/lib/active_decorator/sinatra.rb @@ -0,0 +1,27 @@ +require 'sinatra/base' +require 'active_decorator' +require 'active_decorator/view_context' +require 'active_support/core_ext/object' + +module ActiveDecorator + module Sinatra + DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w[ + @app @default_layout @env @params @preferred_extension + @request @response @template_cache + ].freeze + + class << self + def registered(app) + app.class_eval do + Dir[File.join(settings.decorators, '**/*_decorator.rb')].each do |path| + require path + end + end + end + + alias included registered + end + end +end + +require 'active_decorator/monkey/sinatra' diff --git a/lib/active_decorator/view_context.rb b/lib/active_decorator/view_context.rb index cb1a2c60..5ad7a9c0 100644 --- a/lib/active_decorator/view_context.rb +++ b/lib/active_decorator/view_context.rb @@ -1,3 +1,5 @@ +require 'active_support/concern' + module ActiveDecorator module ViewContext class << self From 18d59d1f958e7550499d8883f7425edafb70b9a0 Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Wed, 20 Mar 2013 17:55:05 +0900 Subject: [PATCH 2/6] Rename fake_app.rb to rails_app.rb --- spec/fake_app/{fake_app.rb => rails_app.rb} | 0 spec/spec_helper.rb | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename spec/fake_app/{fake_app.rb => rails_app.rb} (100%) diff --git a/spec/fake_app/fake_app.rb b/spec/fake_app/rails_app.rb similarity index 100% rename from spec/fake_app/fake_app.rb rename to spec/fake_app/rails_app.rb diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6a9c1bdc..f520ee61 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,7 +4,7 @@ require 'rails' require 'active_decorator' # needs to load the app before loading rspec/rails => capybara -require 'fake_app/fake_app' +require 'fake_app/rails_app' require 'rspec/rails' # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. From b3a1084d4830feeaf9625cd16315c5754c140462 Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Thu, 21 Mar 2013 23:01:12 +0900 Subject: [PATCH 3/6] Move views for into fake_app/rails_views --- spec/fake_app/rails_app.rb | 2 +- spec/fake_app/{ => rails_views}/authors/index.html.erb | 0 spec/fake_app/{ => rails_views}/authors/show.html.erb | 0 spec/fake_app/{ => rails_views}/books/_book.html.erb | 0 spec/fake_app/{ => rails_views}/books/_book_locals.html.erb | 0 spec/fake_app/{ => rails_views}/books/show.html.erb | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename spec/fake_app/{ => rails_views}/authors/index.html.erb (100%) rename spec/fake_app/{ => rails_views}/authors/show.html.erb (100%) rename spec/fake_app/{ => rails_views}/books/_book.html.erb (100%) rename spec/fake_app/{ => rails_views}/books/_book_locals.html.erb (100%) rename spec/fake_app/{ => rails_views}/books/show.html.erb (100%) diff --git a/spec/fake_app/rails_app.rb b/spec/fake_app/rails_app.rb index ae9b00cb..b70409d2 100644 --- a/spec/fake_app/rails_app.rb +++ b/spec/fake_app/rails_app.rb @@ -62,7 +62,7 @@ def cover_image # controllers class ApplicationController < ActionController::Base - self.append_view_path File.dirname(__FILE__) + self.append_view_path File.dirname(__FILE__)+"/rails_views" end class AuthorsController < ApplicationController def index diff --git a/spec/fake_app/authors/index.html.erb b/spec/fake_app/rails_views/authors/index.html.erb similarity index 100% rename from spec/fake_app/authors/index.html.erb rename to spec/fake_app/rails_views/authors/index.html.erb diff --git a/spec/fake_app/authors/show.html.erb b/spec/fake_app/rails_views/authors/show.html.erb similarity index 100% rename from spec/fake_app/authors/show.html.erb rename to spec/fake_app/rails_views/authors/show.html.erb diff --git a/spec/fake_app/books/_book.html.erb b/spec/fake_app/rails_views/books/_book.html.erb similarity index 100% rename from spec/fake_app/books/_book.html.erb rename to spec/fake_app/rails_views/books/_book.html.erb diff --git a/spec/fake_app/books/_book_locals.html.erb b/spec/fake_app/rails_views/books/_book_locals.html.erb similarity index 100% rename from spec/fake_app/books/_book_locals.html.erb rename to spec/fake_app/rails_views/books/_book_locals.html.erb diff --git a/spec/fake_app/books/show.html.erb b/spec/fake_app/rails_views/books/show.html.erb similarity index 100% rename from spec/fake_app/books/show.html.erb rename to spec/fake_app/rails_views/books/show.html.erb From e6cb9bd80b19d91ecf9eb5ba4940f531f5754a1f Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Thu, 21 Mar 2013 00:28:51 +0900 Subject: [PATCH 4/6] Divide specifications of decorators and models from rails_app --- spec/fake_app/decorators.rb | 27 +++++++++++++++++++++ spec/fake_app/models.rb | 15 ++++++++++++ spec/fake_app/rails_app.rb | 47 +++---------------------------------- 3 files changed, 45 insertions(+), 44 deletions(-) create mode 100644 spec/fake_app/decorators.rb create mode 100644 spec/fake_app/models.rb diff --git a/spec/fake_app/decorators.rb b/spec/fake_app/decorators.rb new file mode 100644 index 00000000..4cf2b32a --- /dev/null +++ b/spec/fake_app/decorators.rb @@ -0,0 +1,27 @@ +# decorators +module AuthorDecorator + def reverse_name + name.reverse + end + + def capitalized_name + name.capitalize + end +end +module BookDecorator + def reverse_title + title.reverse + end + + def upcased_title + title.upcase + end + + def link + link_to title, "#{request.protocol}#{request.host_with_port}/assets/sample.png" + end + + def cover_image + image_tag 'cover.png' + end +end diff --git a/spec/fake_app/models.rb b/spec/fake_app/models.rb new file mode 100644 index 00000000..d6c72b86 --- /dev/null +++ b/spec/fake_app/models.rb @@ -0,0 +1,15 @@ +# models +class Author < ActiveRecord::Base + has_many :books +end +class Book < ActiveRecord::Base + belongs_to :author +end + +# migrations +class CreateAllTables < ActiveRecord::Migration + def self.up + create_table(:authors) {|t| t.string :name} + create_table(:books) {|t| t.string :title; t.references :author} + end +end diff --git a/spec/fake_app/rails_app.rb b/spec/fake_app/rails_app.rb index b70409d2..34a20ae4 100644 --- a/spec/fake_app/rails_app.rb +++ b/spec/fake_app/rails_app.rb @@ -2,6 +2,9 @@ require 'action_controller/railtie' require 'action_view/railtie' +require 'fake_app/models' +require 'fake_app/decorators' + # config ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:') @@ -21,45 +24,9 @@ class Application < Rails::Application end end -# models -class Author < ActiveRecord::Base - has_many :books -end -class Book < ActiveRecord::Base - belongs_to :author -end - # helpers module ApplicationHelper; end -# decorators -module AuthorDecorator - def reverse_name - name.reverse - end - - def capitalized_name - name.capitalize - end -end -module BookDecorator - def reverse_title - title.reverse - end - - def upcased_title - title.upcase - end - - def link - link_to title, "#{request.protocol}#{request.host_with_port}/assets/sample.png" - end - - def cover_image - image_tag 'cover.png' - end -end - # controllers class ApplicationController < ActionController::Base self.append_view_path File.dirname(__FILE__)+"/rails_views" @@ -82,11 +49,3 @@ def show @book = Author.find(params[:author_id]).books.find(params[:id]) end end - -# migrations -class CreateAllTables < ActiveRecord::Migration - def self.up - create_table(:authors) {|t| t.string :name} - create_table(:books) {|t| t.string :title; t.references :author} - end -end From 3d0f761ba4a28286113266acf2100803f68a948f Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Thu, 21 Mar 2013 00:31:27 +0900 Subject: [PATCH 5/6] Add spec for Sinatra app It runs with the fllowing command: `BUNDLER_GEMFILE=gemfiles/Gemfile-sinatra bundle exec rake spec:sinatra` Sinatra doesn't support implicit object rendering (like `render @author`), so features about this won't tested in the partial spec. This switching is made with `rails?` method decrared in spec_helper.rb. --- .gitignore | 1 + .travis.yml | 1 + gemfiles/Gemfile-sinatra | 13 ++++++ spec/fake_app/sinatra_app.rb | 41 ++++++++++++++++++ .../sinatra_views/authors/index.html.erb | 8 ++++ .../sinatra_views/authors/show.html.erb | 2 + .../sinatra_views/books/_book.html.erb | 2 + .../sinatra_views/books/_book_locals.html.erb | 2 + .../sinatra_views/books/show.html.erb | 2 + spec/features/partial_spec.rb | 4 +- spec/spec_helper.rb | 42 ++++++++++++++++--- 11 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 gemfiles/Gemfile-sinatra create mode 100644 spec/fake_app/sinatra_app.rb create mode 100644 spec/fake_app/sinatra_views/authors/index.html.erb create mode 100644 spec/fake_app/sinatra_views/authors/show.html.erb create mode 100644 spec/fake_app/sinatra_views/books/_book.html.erb create mode 100644 spec/fake_app/sinatra_views/books/_book_locals.html.erb create mode 100644 spec/fake_app/sinatra_views/books/show.html.erb diff --git a/.gitignore b/.gitignore index 9fc60541..6feda87a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ Gemfile.lock gemfiles/Gemfile-rails.3.0.x.lock gemfiles/Gemfile-rails.3.1.x.lock +gemfiles/Gemfile-sinatra.lock pkg/* log diff --git a/.travis.yml b/.travis.yml index 30ca637b..0bc8f389 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ rvm: - 2.0.0 - ruby-head gemfile: + - gemfiles/Gemfile-sinatra - gemfiles/Gemfile-rails.3.0.x - gemfiles/Gemfile-rails.3.1.x - Gemfile diff --git a/gemfiles/Gemfile-sinatra b/gemfiles/Gemfile-sinatra new file mode 100644 index 00000000..96cc3846 --- /dev/null +++ b/gemfiles/Gemfile-sinatra @@ -0,0 +1,13 @@ +source :rubygems + +gem 'active_decorator', :path => '../' + +gem 'sinatra', '>= 1.3' +gem 'activerecord', '~> 3.2.0' +gem 'padrino-helpers' +gem 'rspec' +gem 'rack-test' +gem 'sinatra-contrib' +gem 'capybara', '>= 2' +gem 'sqlite3' +gem 'rake' diff --git a/spec/fake_app/sinatra_app.rb b/spec/fake_app/sinatra_app.rb new file mode 100644 index 00000000..627ea934 --- /dev/null +++ b/spec/fake_app/sinatra_app.rb @@ -0,0 +1,41 @@ +require 'active_record' +require 'sinatra/base' + +require 'fake_app/models' +require 'fake_app/decorators' + +# config +ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:') + +class Sinatra::Request < Rack::Request + def protocol + scheme + end +end + +class ActiveDecoratorSinatraTestApp < Sinatra::Base + register ActiveDecorator::Sinatra + set :views, File.dirname(__FILE__)+"/sinatra_views" + + register Padrino::Helpers + + get '/authors' do + if params[:variable_type] == 'array' + @authors = Author.all + else + @authors = Author.scoped + end + + erb :'authors/index.html' + end + + get '/authors/:id' do + @author = Author.find params[:id] + erb :'authors/show.html' + end + + get '/authors/:author_id/books/:id' do + @book = Author.find(params[:author_id]).books.find(params[:id]) + erb :'books/show.html' + end +end diff --git a/spec/fake_app/sinatra_views/authors/index.html.erb b/spec/fake_app/sinatra_views/authors/index.html.erb new file mode 100644 index 00000000..2eb21372 --- /dev/null +++ b/spec/fake_app/sinatra_views/authors/index.html.erb @@ -0,0 +1,8 @@ +<% @authors.each do |author| %> + <%= author.name %> + <%= author.reverse_name %> + + <% author.books.each do |book| %> + <%= erb :'books/_book_locals.html', :locals => {:b => book} %> + <% end %> +<% end %> diff --git a/spec/fake_app/sinatra_views/authors/show.html.erb b/spec/fake_app/sinatra_views/authors/show.html.erb new file mode 100644 index 00000000..761ec664 --- /dev/null +++ b/spec/fake_app/sinatra_views/authors/show.html.erb @@ -0,0 +1,2 @@ +<%= @author.name %> +<%= @author.capitalized_name %> diff --git a/spec/fake_app/sinatra_views/books/_book.html.erb b/spec/fake_app/sinatra_views/books/_book.html.erb new file mode 100644 index 00000000..2986258a --- /dev/null +++ b/spec/fake_app/sinatra_views/books/_book.html.erb @@ -0,0 +1,2 @@ +<%= book.title %> +<%= book.reverse_title %> diff --git a/spec/fake_app/sinatra_views/books/_book_locals.html.erb b/spec/fake_app/sinatra_views/books/_book_locals.html.erb new file mode 100644 index 00000000..0b11f3d1 --- /dev/null +++ b/spec/fake_app/sinatra_views/books/_book_locals.html.erb @@ -0,0 +1,2 @@ +<%= b.title %> +<%= b.upcased_title %> diff --git a/spec/fake_app/sinatra_views/books/show.html.erb b/spec/fake_app/sinatra_views/books/show.html.erb new file mode 100644 index 00000000..c56015b4 --- /dev/null +++ b/spec/fake_app/sinatra_views/books/show.html.erb @@ -0,0 +1,2 @@ +<%= @book.link %> +<%= @book.cover_image %> diff --git a/spec/features/partial_spec.rb b/spec/features/partial_spec.rb index a5d1d227..6e30be4e 100644 --- a/spec/features/partial_spec.rb +++ b/spec/features/partial_spec.rb @@ -11,13 +11,13 @@ Author.delete_all end - scenario 'decorating implicit @object' do + scenario 'decorating implicit @object', :if => rails? do visit '/authors' page.should have_content 'the gc book' page.should have_content 'the gc book'.reverse end - scenario 'decorating implicit @collection' do + scenario 'decorating implicit @collection', :if => rails? do visit '/authors?partial=collection' page.should have_content 'the gc book' page.should have_content 'the gc book'.reverse diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f520ee61..7995c76a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,11 +1,41 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) -# load Rails first -require 'rails' -require 'active_decorator' -# needs to load the app before loading rspec/rails => capybara -require 'fake_app/rails_app' -require 'rspec/rails' + +begin + # load Rails first + require 'rails' +rescue LoadError +end + +require 'bundler/setup' +Bundler.require + +require 'capybara/rspec' + +if defined? Rails + require 'active_decorator' + # needs to load the app before loading rspec/rails => capybara + require 'fake_app/rails_app' + require 'rspec/rails' + + def rails?; true end +end +if defined? Sinatra + require 'active_decorator/sinatra' + require 'fake_app/sinatra_app' + require 'rack/test' + require 'sinatra/test_helpers' + + def rails?; false end + + Capybara.app = ActiveDecoratorSinatraTestApp + + RSpec.configure do |config| + config.include Rack::Test::Methods + config.include Sinatra::TestHelpers + end +end + # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} From 920a3ee2f9b4163584893ca6ec4f69f40585a6e2 Mon Sep 17 00:00:00 2001 From: Masato Ikeda Date: Thu, 21 Mar 2013 02:00:50 +0900 Subject: [PATCH 6/6] Add activesupport (>= 3.0.0) dependency activesupport gem isn't installed with Sinatra by default. --- active_decorator.gemspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/active_decorator.gemspec b/active_decorator.gemspec index 4d4e2538..bf2ae2a8 100644 --- a/active_decorator.gemspec +++ b/active_decorator.gemspec @@ -17,4 +17,6 @@ Gem::Specification.new do |s| s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] + + s.add_runtime_dependency 'activesupport', ['>= 3.0.0'] end