diff --git a/.gitignore b/.gitignore index 9fc6054..6feda87 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 30ca637..0bc8f38 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/active_decorator.gemspec b/active_decorator.gemspec index 4d4e253..bf2ae2a 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 diff --git a/gemfiles/Gemfile-sinatra b/gemfiles/Gemfile-sinatra new file mode 100644 index 0000000..96cc384 --- /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/lib/active_decorator.rb b/lib/active_decorator.rb index 0d20e7a..75bb9fb 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 0000000..b9c3f85 --- /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 0000000..801c339 --- /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 cb1a2c6..5ad7a9c 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 diff --git a/spec/fake_app/decorators.rb b/spec/fake_app/decorators.rb new file mode 100644 index 0000000..4cf2b32 --- /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 0000000..d6c72b8 --- /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/fake_app.rb b/spec/fake_app/rails_app.rb similarity index 59% rename from spec/fake_app/fake_app.rb rename to spec/fake_app/rails_app.rb index ae9b00c..34a20ae 100644 --- a/spec/fake_app/fake_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,48 +24,12 @@ 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__) + self.append_view_path File.dirname(__FILE__)+"/rails_views" end class AuthorsController < ApplicationController def index @@ -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 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 diff --git a/spec/fake_app/sinatra_app.rb b/spec/fake_app/sinatra_app.rb new file mode 100644 index 0000000..627ea93 --- /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 0000000..2eb2137 --- /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 0000000..761ec66 --- /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 0000000..2986258 --- /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 0000000..0b11f3d --- /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 0000000..c56015b --- /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 a5d1d22..6e30be4 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 6a9c1bd..7995c76 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/fake_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}