commit
4d0181fb9c
16
README.md
16
README.md
|
@ -264,6 +264,22 @@ In your `Spork.prefork` block of `spec_helper.rb`, add this:
|
|||
require 'draper/test/rspec_integration'
|
||||
```
|
||||
|
||||
### Isolated tests
|
||||
|
||||
In tests, Draper needs to build a view context to access helper methods. By default, it will create an `ApplicationController` and then use its view context. If you are speeding up your test suite by testing each component in isolation, you can eliminate this dependency by putting the following in your `spec_helper` or similar:
|
||||
|
||||
```ruby
|
||||
Draper::ViewContext.test_strategy :fast
|
||||
```
|
||||
|
||||
In doing so, your decorators will no longer have access to your application's helpers. If you need to selectively include such helpers, you can pass a block:
|
||||
|
||||
```ruby
|
||||
Draper::ViewContext.test_strategy :fast do
|
||||
include ApplicationHelper
|
||||
end
|
||||
```
|
||||
|
||||
## Advanced usage
|
||||
|
||||
### Shared Decorator Methods
|
||||
|
|
1
Rakefile
1
Rakefile
|
@ -60,6 +60,7 @@ end
|
|||
namespace "db" do
|
||||
desc "Set up databases for integration testing"
|
||||
task "setup" do
|
||||
puts "Setting up databases"
|
||||
run_in_dummy_app "rm -f db/*.sqlite3"
|
||||
run_in_dummy_app "RAILS_ENV=development rake db:schema:load db:seed"
|
||||
run_in_dummy_app "RAILS_ENV=production rake db:schema:load db:seed"
|
||||
|
|
|
@ -24,10 +24,11 @@ module Draper
|
|||
base.class_eval do
|
||||
include Draper::ViewContext
|
||||
extend Draper::HelperSupport
|
||||
before_filter ->(controller) {
|
||||
Draper::ViewContext.current = nil
|
||||
Draper::ViewContext.current_controller = controller
|
||||
}
|
||||
|
||||
before_filter do |controller|
|
||||
Draper::ViewContext.clear!
|
||||
Draper::ViewContext.controller = controller
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,5 +8,9 @@ module Draper
|
|||
|
||||
RSpec.configure do |config|
|
||||
config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator
|
||||
|
||||
[:decorator, :controller, :mailer].each do |type|
|
||||
config.after(:each, type: type) { Draper::ViewContext.clear! }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,13 @@ module Draper
|
|||
end
|
||||
|
||||
class TestCase < active_support_test_case
|
||||
module ViewContextTeardown
|
||||
def teardown
|
||||
super
|
||||
Draper::ViewContext.clear!
|
||||
end
|
||||
end
|
||||
|
||||
module Behavior
|
||||
if defined?(::Devise)
|
||||
require 'draper/test/devise_helper'
|
||||
|
@ -29,5 +36,18 @@ module Draper
|
|||
end
|
||||
|
||||
include Behavior
|
||||
include ViewContextTeardown
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(ActionController::TestCase)
|
||||
class ActionController::TestCase
|
||||
include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(ActionMailer::TestCase)
|
||||
class ActionMailer::TestCase
|
||||
include Draper::TestCase::ViewContextTeardown
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,37 +1,89 @@
|
|||
require 'draper/view_context/build_strategy'
|
||||
require 'request_store'
|
||||
|
||||
module Draper
|
||||
module ViewContext
|
||||
# Hooks into a controller or mailer to save the view context in {current}.
|
||||
def view_context
|
||||
super.tap do |context|
|
||||
Draper::ViewContext.current = context
|
||||
end
|
||||
end
|
||||
|
||||
def self.current_controller
|
||||
RequestStore.store[:current_controller] || ApplicationController.new
|
||||
# Returns the current controller.
|
||||
def self.controller
|
||||
RequestStore.store[:current_controller]
|
||||
end
|
||||
|
||||
def self.current_controller=(controller)
|
||||
# Sets the current controller.
|
||||
def self.controller=(controller)
|
||||
RequestStore.store[:current_controller] = controller
|
||||
end
|
||||
|
||||
# Returns the current view context, or builds one if none is saved.
|
||||
def self.current
|
||||
RequestStore.store[:current_view_context] ||= build_view_context
|
||||
RequestStore.store[:current_view_context] ||= build
|
||||
end
|
||||
|
||||
def self.current=(context)
|
||||
RequestStore.store[:current_view_context] = context
|
||||
# Sets the current view context.
|
||||
def self.current=(view_context)
|
||||
RequestStore.store[:current_view_context] = view_context
|
||||
end
|
||||
|
||||
# Clears the saved controller and view context.
|
||||
def self.clear!
|
||||
self.controller = nil
|
||||
self.current = nil
|
||||
end
|
||||
|
||||
# Builds a new view context for usage in tests. See {test_strategy} for
|
||||
# details of how the view context is built.
|
||||
def self.build
|
||||
build_strategy.call
|
||||
end
|
||||
|
||||
# Configures the strategy used to build view contexts in tests, which
|
||||
# defaults to `:full` if `test_strategy` has not been called. Evaluates
|
||||
# the block, if given, in the context of the view context's class.
|
||||
#
|
||||
# @example Pass a block to add helper methods to the view context:
|
||||
# Draper::ViewContext.test_strategy :fast do
|
||||
# include ApplicationHelper
|
||||
# end
|
||||
#
|
||||
# @param [:full, :fast] name
|
||||
# the strategy to use:
|
||||
#
|
||||
# `:full` - build a fully-working view context. Your Rails environment
|
||||
# must be loaded, including your `ApplicationController`.
|
||||
#
|
||||
# `:fast` - build a minimal view context in tests, with no dependencies
|
||||
# on other components of your application.
|
||||
def self.test_strategy(name, &block)
|
||||
@build_strategy = Draper::ViewContext::BuildStrategy.new(name, &block)
|
||||
end
|
||||
|
||||
# @private
|
||||
def self.build_strategy
|
||||
@build_strategy ||= Draper::ViewContext::BuildStrategy.new(:full)
|
||||
end
|
||||
|
||||
# @deprecated Use {controller} instead.
|
||||
def self.current_controller
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller is deprecated (use controller instead)", caller)
|
||||
self.controller || ApplicationController.new
|
||||
end
|
||||
|
||||
# @deprecated Use {controller=} instead.
|
||||
def self.current_controller=(controller)
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller= is deprecated (use controller instead)", caller)
|
||||
self.controller = controller
|
||||
end
|
||||
|
||||
# @deprecated Use {build} instead.
|
||||
def self.build_view_context
|
||||
current_controller.view_context.tap do |context|
|
||||
if defined?(ActionController::TestRequest)
|
||||
context.controller.request ||= ActionController::TestRequest.new
|
||||
context.request ||= context.controller.request
|
||||
context.params ||= {}
|
||||
end
|
||||
end
|
||||
ActiveSupport::Deprecation.warn("Draper::ViewContext.build_view_context is deprecated (use build instead)", caller)
|
||||
build
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
module Draper
|
||||
module ViewContext
|
||||
# @private
|
||||
module BuildStrategy
|
||||
|
||||
def self.new(name, &block)
|
||||
const_get(name.to_s.camelize).new(&block)
|
||||
end
|
||||
|
||||
class Fast
|
||||
def initialize(&block)
|
||||
@view_context_class = Class.new(ActionView::Base, &block)
|
||||
end
|
||||
|
||||
def call
|
||||
view_context_class.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :view_context_class
|
||||
end
|
||||
|
||||
class Full
|
||||
def initialize(&block)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def call
|
||||
controller.view_context.tap do |context|
|
||||
context.singleton_class.class_eval(&block) if block
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :block
|
||||
|
||||
def controller
|
||||
(Draper::ViewContext.controller || ApplicationController.new).tap do |controller|
|
||||
controller.request ||= ActionController::TestRequest.new if defined?(ActionController::TestRequest)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -444,7 +444,7 @@ module Draper
|
|||
it "does not confuse Kernel#Array" do
|
||||
decorator = Decorator.new(Model.new)
|
||||
|
||||
expect(Array(decorator)).to be_a Array
|
||||
expect(Array(decorator)).to be_an Array
|
||||
end
|
||||
|
||||
it "delegates already-delegated methods" do
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
require 'spec_helper'
|
||||
|
||||
def fake_view_context
|
||||
double("ViewContext")
|
||||
end
|
||||
|
||||
def fake_controller(view_context = fake_view_context)
|
||||
double("Controller", view_context: view_context, request: double("Request"))
|
||||
end
|
||||
|
||||
module Draper
|
||||
describe ViewContext::BuildStrategy::Full do
|
||||
describe "#call" do
|
||||
context "when a current controller is set" do
|
||||
it "returns the controller's view context" do
|
||||
view_context = fake_view_context
|
||||
ViewContext.stub controller: fake_controller(view_context)
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(strategy.call).to be view_context
|
||||
end
|
||||
end
|
||||
|
||||
context "when a current controller is not set" do
|
||||
it "uses ApplicationController" do
|
||||
view_context = fake_view_context
|
||||
stub_const "ApplicationController", double(new: fake_controller(view_context))
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(strategy.call).to be view_context
|
||||
end
|
||||
end
|
||||
|
||||
it "adds a request if one is not defined" do
|
||||
controller = Class.new(ActionController::Base).new
|
||||
ViewContext.stub controller: controller
|
||||
strategy = ViewContext::BuildStrategy::Full.new
|
||||
|
||||
expect(controller.request).to be_nil
|
||||
strategy.call
|
||||
expect(controller.request).to be_an ActionController::TestRequest
|
||||
expect(controller.params).to eq({})
|
||||
|
||||
# sanity checks
|
||||
expect(controller.view_context.request).to be controller.request
|
||||
expect(controller.view_context.params).to be controller.params
|
||||
end
|
||||
|
||||
it "adds methods to the view context from the constructor block" do
|
||||
ViewContext.stub controller: fake_controller
|
||||
strategy = ViewContext::BuildStrategy::Full.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
|
||||
expect(strategy.call).to respond_to :a_helper_method
|
||||
end
|
||||
|
||||
it "includes modules into the view context from the constructor block" do
|
||||
view_context = Object.new
|
||||
ViewContext.stub controller: fake_controller(view_context)
|
||||
helpers = Module.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
strategy = ViewContext::BuildStrategy::Full.new do
|
||||
include helpers
|
||||
end
|
||||
|
||||
expect(strategy.call).to respond_to :a_helper_method
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ViewContext::BuildStrategy::Fast do
|
||||
describe "#call" do
|
||||
it "returns an instance of a subclass of ActionView::Base" do
|
||||
strategy = ViewContext::BuildStrategy::Fast.new
|
||||
|
||||
returned = strategy.call
|
||||
|
||||
expect(returned).to be_an ActionView::Base
|
||||
expect(returned.class).not_to be ActionView::Base
|
||||
end
|
||||
|
||||
it "returns different instances each time" do
|
||||
strategy = ViewContext::BuildStrategy::Fast.new
|
||||
|
||||
expect(strategy.call).not_to be strategy.call
|
||||
end
|
||||
|
||||
it "returns the same subclass each time" do
|
||||
strategy = ViewContext::BuildStrategy::Fast.new
|
||||
|
||||
expect(strategy.call.class).to be strategy.call.class
|
||||
end
|
||||
|
||||
it "adds methods to the view context from the constructor block" do
|
||||
strategy = ViewContext::BuildStrategy::Fast.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
|
||||
expect(strategy.call).to respond_to :a_helper_method
|
||||
end
|
||||
|
||||
it "includes modules into the view context from the constructor block" do
|
||||
helpers = Module.new do
|
||||
def a_helper_method; end
|
||||
end
|
||||
strategy = ViewContext::BuildStrategy::Fast.new do
|
||||
include helpers
|
||||
end
|
||||
|
||||
expect(strategy.call).to respond_to :a_helper_method
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,117 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Draper
|
||||
describe ViewContext do
|
||||
describe "#view_context" do
|
||||
let(:base) { Class.new { def view_context; :controller_view_context; end } }
|
||||
let(:controller) { Class.new(base) { include ViewContext } }
|
||||
|
||||
it "saves the superclass's view context" do
|
||||
controller.new.view_context
|
||||
expect(ViewContext.current).to be :controller_view_context
|
||||
end
|
||||
|
||||
it "returns the superclass's view context" do
|
||||
expect(controller.new.view_context).to be :controller_view_context
|
||||
end
|
||||
end
|
||||
|
||||
describe ".controller" do
|
||||
it "returns the stored controller from RequestStore" do
|
||||
RequestStore.stub store: {current_controller: :stored_controller}
|
||||
expect(ViewContext.controller).to be :stored_controller
|
||||
end
|
||||
end
|
||||
|
||||
describe ".controller=" do
|
||||
it "stores a controller in RequestStore" do
|
||||
store = {}
|
||||
RequestStore.stub store: store
|
||||
|
||||
ViewContext.controller = :stored_controller
|
||||
expect(store[:current_controller]).to be :stored_controller
|
||||
end
|
||||
end
|
||||
|
||||
describe ".current" do
|
||||
it "returns the stored view context from RequestStore" do
|
||||
RequestStore.stub store: {current_view_context: :stored_view_context}
|
||||
expect(ViewContext.current).to be :stored_view_context
|
||||
end
|
||||
|
||||
it "falls back to building a view context" do
|
||||
RequestStore.stub store: {}
|
||||
ViewContext.should_receive(:build).and_return(:new_view_context)
|
||||
expect(ViewContext.current).to be :new_view_context
|
||||
end
|
||||
end
|
||||
|
||||
describe ".current=" do
|
||||
it "stores a view context in RequestStore" do
|
||||
store = {}
|
||||
RequestStore.stub store: store
|
||||
|
||||
ViewContext.current = :stored_view_context
|
||||
expect(store[:current_view_context]).to be :stored_view_context
|
||||
end
|
||||
end
|
||||
|
||||
describe ".clear!" do
|
||||
it "clears the stored controller and view controller" do
|
||||
store = {current_controller: :stored_controller, current_view_context: :stored_view_context}
|
||||
RequestStore.stub store: store
|
||||
|
||||
ViewContext.clear!
|
||||
expect(store[:current_controller]).to be_nil
|
||||
expect(store[:current_view_context]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe ".build" do
|
||||
it "calls the build strategy" do
|
||||
ViewContext.stub build_strategy: ->{ :new_view_context }
|
||||
expect(ViewContext.build).to be :new_view_context
|
||||
end
|
||||
end
|
||||
|
||||
describe ".build_strategy" do
|
||||
it "defaults to full" do
|
||||
expect(ViewContext.build_strategy).to be_a ViewContext::BuildStrategy::Full
|
||||
end
|
||||
|
||||
it "memoizes" do
|
||||
expect(ViewContext.build_strategy).to be ViewContext.build_strategy
|
||||
end
|
||||
end
|
||||
|
||||
describe ".test_strategy" do
|
||||
protect_module ViewContext
|
||||
|
||||
context "with :fast" do
|
||||
it "creates a fast strategy" do
|
||||
ViewContext.test_strategy :fast
|
||||
expect(ViewContext.build_strategy).to be_a ViewContext::BuildStrategy::Fast
|
||||
end
|
||||
|
||||
it "passes a block to the strategy" do
|
||||
ViewContext::BuildStrategy::Fast.stub(:new).and_return{|&block| block.call}
|
||||
|
||||
expect(ViewContext.test_strategy(:fast){:passed}).to be :passed
|
||||
end
|
||||
end
|
||||
|
||||
context "with :full" do
|
||||
it "creates a full strategy" do
|
||||
ViewContext.test_strategy :full
|
||||
expect(ViewContext.build_strategy).to be_a ViewContext::BuildStrategy::Full
|
||||
end
|
||||
|
||||
it "passes a block to the strategy" do
|
||||
ViewContext::BuildStrategy::Full.stub(:new).and_return{|&block| block.call}
|
||||
|
||||
expect(ViewContext.test_strategy(:full){:passed}).to be :passed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,261 +0,0 @@
|
|||
== Welcome to Rails
|
||||
|
||||
Rails is a web-application framework that includes everything needed to create
|
||||
database-backed web applications according to the Model-View-Control pattern.
|
||||
|
||||
This pattern splits the view (also called the presentation) into "dumb"
|
||||
templates that are primarily responsible for inserting pre-built data in between
|
||||
HTML tags. The model contains the "smart" domain objects (such as Account,
|
||||
Product, Person, Post) that holds all the business logic and knows how to
|
||||
persist themselves to a database. The controller handles the incoming requests
|
||||
(such as Save New Account, Update Product, Show Post) by manipulating the model
|
||||
and directing data to the view.
|
||||
|
||||
In Rails, the model is handled by what's called an object-relational mapping
|
||||
layer entitled Active Record. This layer allows you to present the data from
|
||||
database rows as objects and embellish these data objects with business logic
|
||||
methods. You can read more about Active Record in
|
||||
link:files/vendor/rails/activerecord/README.html.
|
||||
|
||||
The controller and view are handled by the Action Pack, which handles both
|
||||
layers by its two parts: Action View and Action Controller. These two layers
|
||||
are bundled in a single package due to their heavy interdependence. This is
|
||||
unlike the relationship between the Active Record and Action Pack that is much
|
||||
more separate. Each of these packages can be used independently outside of
|
||||
Rails. You can read more about Action Pack in
|
||||
link:files/vendor/rails/actionpack/README.html.
|
||||
|
||||
|
||||
== Getting Started
|
||||
|
||||
1. At the command prompt, create a new Rails application:
|
||||
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
|
||||
|
||||
2. Change directory to <tt>myapp</tt> and start the web server:
|
||||
<tt>cd myapp; rails server</tt> (run with --help for options)
|
||||
|
||||
3. Go to http://localhost:3000/ and you'll see:
|
||||
"Welcome aboard: You're riding Ruby on Rails!"
|
||||
|
||||
4. Follow the guidelines to start developing your application. You can find
|
||||
the following resources handy:
|
||||
|
||||
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
|
||||
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
|
||||
|
||||
|
||||
== Debugging Rails
|
||||
|
||||
Sometimes your application goes wrong. Fortunately there are a lot of tools that
|
||||
will help you debug it and get it back on the rails.
|
||||
|
||||
First area to check is the application log files. Have "tail -f" commands
|
||||
running on the server.log and development.log. Rails will automatically display
|
||||
debugging and runtime information to these files. Debugging info will also be
|
||||
shown in the browser on requests from 127.0.0.1.
|
||||
|
||||
You can also log your own messages directly into the log file from your code
|
||||
using the Ruby logger class from inside your controllers. Example:
|
||||
|
||||
class WeblogController < ActionController::Base
|
||||
def destroy
|
||||
@weblog = Weblog.find(params[:id])
|
||||
@weblog.destroy
|
||||
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
|
||||
end
|
||||
end
|
||||
|
||||
The result will be a message in your log file along the lines of:
|
||||
|
||||
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
|
||||
|
||||
More information on how to use the logger is at http://www.ruby-doc.org/core/
|
||||
|
||||
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
|
||||
several books available online as well:
|
||||
|
||||
* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
|
||||
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
|
||||
|
||||
These two books will bring you up to speed on the Ruby language and also on
|
||||
programming in general.
|
||||
|
||||
|
||||
== Debugger
|
||||
|
||||
Debugger support is available through the debugger command when you start your
|
||||
Mongrel or WEBrick server with --debugger. This means that you can break out of
|
||||
execution at any point in the code, investigate and change the model, and then,
|
||||
resume execution! You need to install ruby-debug to run the server in debugging
|
||||
mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
|
||||
|
||||
class WeblogController < ActionController::Base
|
||||
def index
|
||||
@posts = Post.all
|
||||
debugger
|
||||
end
|
||||
end
|
||||
|
||||
So the controller will accept the action, run the first line, then present you
|
||||
with a IRB prompt in the server window. Here you can do things like:
|
||||
|
||||
>> @posts.inspect
|
||||
=> "[#<Post:0x14a6be8
|
||||
@attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
|
||||
#<Post:0x14a6620
|
||||
@attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
|
||||
>> @posts.first.title = "hello from a debugger"
|
||||
=> "hello from a debugger"
|
||||
|
||||
...and even better, you can examine how your runtime objects actually work:
|
||||
|
||||
>> f = @posts.first
|
||||
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
|
||||
>> f.
|
||||
Display all 152 possibilities? (y or n)
|
||||
|
||||
Finally, when you're ready to resume execution, you can enter "cont".
|
||||
|
||||
|
||||
== Console
|
||||
|
||||
The console is a Ruby shell, which allows you to interact with your
|
||||
application's domain model. Here you'll have all parts of the application
|
||||
configured, just like it is when the application is running. You can inspect
|
||||
domain models, change values, and save to the database. Starting the script
|
||||
without arguments will launch it in the development environment.
|
||||
|
||||
To start the console, run <tt>rails console</tt> from the application
|
||||
directory.
|
||||
|
||||
Options:
|
||||
|
||||
* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
|
||||
made to the database.
|
||||
* Passing an environment name as an argument will load the corresponding
|
||||
environment. Example: <tt>rails console production</tt>.
|
||||
|
||||
To reload your controllers and models after launching the console run
|
||||
<tt>reload!</tt>
|
||||
|
||||
More information about irb can be found at:
|
||||
link:http://www.rubycentral.org/pickaxe/irb.html
|
||||
|
||||
|
||||
== dbconsole
|
||||
|
||||
You can go to the command line of your database directly through <tt>rails
|
||||
dbconsole</tt>. You would be connected to the database with the credentials
|
||||
defined in database.yml. Starting the script without arguments will connect you
|
||||
to the development database. Passing an argument will connect you to a different
|
||||
database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
|
||||
PostgreSQL and SQLite 3.
|
||||
|
||||
== Description of Contents
|
||||
|
||||
The default directory structure of a generated Ruby on Rails application:
|
||||
|
||||
|-- app
|
||||
| |-- assets
|
||||
| |-- images
|
||||
| |-- javascripts
|
||||
| `-- stylesheets
|
||||
| |-- controllers
|
||||
| |-- helpers
|
||||
| |-- mailers
|
||||
| |-- models
|
||||
| `-- views
|
||||
| `-- layouts
|
||||
|-- config
|
||||
| |-- environments
|
||||
| |-- initializers
|
||||
| `-- locales
|
||||
|-- db
|
||||
|-- doc
|
||||
|-- lib
|
||||
| `-- tasks
|
||||
|-- log
|
||||
|-- public
|
||||
|-- script
|
||||
|-- test
|
||||
| |-- fixtures
|
||||
| |-- functional
|
||||
| |-- integration
|
||||
| |-- performance
|
||||
| `-- unit
|
||||
|-- tmp
|
||||
| |-- cache
|
||||
| |-- pids
|
||||
| |-- sessions
|
||||
| `-- sockets
|
||||
`-- vendor
|
||||
|-- assets
|
||||
`-- stylesheets
|
||||
`-- plugins
|
||||
|
||||
app
|
||||
Holds all the code that's specific to this particular application.
|
||||
|
||||
app/assets
|
||||
Contains subdirectories for images, stylesheets, and JavaScript files.
|
||||
|
||||
app/controllers
|
||||
Holds controllers that should be named like weblogs_controller.rb for
|
||||
automated URL mapping. All controllers should descend from
|
||||
ApplicationController which itself descends from ActionController::Base.
|
||||
|
||||
app/models
|
||||
Holds models that should be named like post.rb. Models descend from
|
||||
ActiveRecord::Base by default.
|
||||
|
||||
app/views
|
||||
Holds the template files for the view that should be named like
|
||||
weblogs/index.html.erb for the WeblogsController#index action. All views use
|
||||
eRuby syntax by default.
|
||||
|
||||
app/views/layouts
|
||||
Holds the template files for layouts to be used with views. This models the
|
||||
common header/footer method of wrapping views. In your views, define a layout
|
||||
using the <tt>layout :default</tt> and create a file named default.html.erb.
|
||||
Inside default.html.erb, call <% yield %> to render the view using this
|
||||
layout.
|
||||
|
||||
app/helpers
|
||||
Holds view helpers that should be named like weblogs_helper.rb. These are
|
||||
generated for you automatically when using generators for controllers.
|
||||
Helpers can be used to wrap functionality for your views into methods.
|
||||
|
||||
config
|
||||
Configuration files for the Rails environment, the routing map, the database,
|
||||
and other dependencies.
|
||||
|
||||
db
|
||||
Contains the database schema in schema.rb. db/migrate contains all the
|
||||
sequence of Migrations for your schema.
|
||||
|
||||
doc
|
||||
This directory is where your application documentation will be stored when
|
||||
generated using <tt>rake doc:app</tt>
|
||||
|
||||
lib
|
||||
Application specific libraries. Basically, any kind of custom code that
|
||||
doesn't belong under controllers, models, or helpers. This directory is in
|
||||
the load path.
|
||||
|
||||
public
|
||||
The directory available for the web server. Also contains the dispatchers and the
|
||||
default HTML files. This should be set as the DOCUMENT_ROOT of your web
|
||||
server.
|
||||
|
||||
script
|
||||
Helper scripts for automation and generation.
|
||||
|
||||
test
|
||||
Unit and functional tests along with fixtures. When using the rails generate
|
||||
command, template test files will be generated for you and placed in this
|
||||
directory.
|
||||
|
||||
vendor
|
||||
External libraries that the application depends on. Also includes the plugins
|
||||
subdirectory. If the app has frozen rails, those gems also go here, under
|
||||
vendor/rails/. This directory is in the load path.
|
|
@ -2,6 +2,9 @@
|
|||
<dt>Environment:</dt>
|
||||
<dd id="environment"><%= Rails.env %></dd>
|
||||
|
||||
<dt>Draper view context controller:</dt>
|
||||
<dd id="controller"><%= Draper::ViewContext.current.controller.class %></dd>
|
||||
|
||||
<dt>Posted:</dt>
|
||||
<dd id="posted_date"><%= post.posted_date %></dd>
|
||||
|
||||
|
|
|
@ -62,3 +62,4 @@ module Dummy
|
|||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Migration.verbose = false
|
||||
|
|
|
@ -6,9 +6,6 @@ Dummy::Application.configure do
|
|||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# Log error messages when you accidentally call methods on nil.
|
||||
config.whiny_nils = true
|
||||
|
||||
# Show full error reports and disable caching
|
||||
config.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
|
@ -19,6 +16,8 @@ Dummy::Application.configure do
|
|||
# Only use best-standards-support built into browsers
|
||||
config.action_dispatch.best_standards_support = :builtin
|
||||
|
||||
config.eager_load = false
|
||||
|
||||
# Raise exception on mass assignment protection for Active Record models
|
||||
# config.active_record.mass_assignment_sanitizer = :strict
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ Dummy::Application.configure do
|
|||
# Disable Rails's static asset server (Apache or nginx will already do this)
|
||||
config.serve_static_assets = false
|
||||
|
||||
config.eager_load = true
|
||||
|
||||
# Defaults to nil and saved in location specified by config.assets.prefix
|
||||
# config.assets.manifest = YOUR_PATH
|
||||
|
||||
|
|
|
@ -9,10 +9,7 @@ Dummy::Application.configure do
|
|||
|
||||
# Configure static asset server for tests with Cache-Control for performance
|
||||
# config.serve_static_assets = true
|
||||
config.static_cache_control = "public, max-age=3600"
|
||||
|
||||
# Log error messages when you accidentally call methods on nil
|
||||
config.whiny_nils = true
|
||||
# config.static_cache_control = "public, max-age=3600"
|
||||
|
||||
# Show full error reports and disable caching
|
||||
config.consider_all_requests_local = true
|
||||
|
@ -29,4 +26,6 @@ Dummy::Application.configure do
|
|||
|
||||
# Print deprecation notices to the stderr
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
config.eager_load = false
|
||||
end
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
# If you change this key, all old signed cookies will become invalid!
|
||||
# Make sure the secret is at least 30 characters and all random,
|
||||
# no regular words or you'll be exposed to dictionary attacks.
|
||||
Dummy::Application.config.secret_key_base = 'c2e3474d3816f60bf6dd0f3b983e7283c7ff5373e11a96935340b544a31964dbe5ee077136165ee2975e0005f5e80207c0059e6d5589699031242ba5a06dcb87'
|
||||
Dummy::Application.config.secret_token = 'c2e3474d3816f60bf6dd0f3b983e7283c7ff5373e11a96935340b544a31964dbe5ee077136165ee2975e0005f5e80207c0059e6d5589699031242ba5a06dcb87'
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
require 'draper'
|
||||
require 'rspec'
|
||||
|
||||
require 'active_model/naming'
|
||||
require_relative '../app/decorators/post_decorator'
|
||||
|
||||
Draper::ViewContext.test_strategy :fast
|
||||
|
||||
Post = Struct.new(:id) { extend ActiveModel::Naming }
|
||||
|
||||
describe PostDecorator do
|
||||
let(:decorator) { PostDecorator.new(source) }
|
||||
let(:source) { Post.new(42) }
|
||||
|
||||
it "can use built-in helpers" do
|
||||
expect(decorator.truncated).to eq "Once upon a..."
|
||||
end
|
||||
|
||||
it "can use built-in private helpers" do
|
||||
expect(decorator.html_escaped).to eq "<script>danger</script>"
|
||||
end
|
||||
|
||||
it "can't use user-defined helpers from app/helpers" do
|
||||
expect{decorator.hello_world}.to raise_error NoMethodError, /hello_world/
|
||||
end
|
||||
|
||||
it "can't use path helpers" do
|
||||
expect{decorator.path_with_model}.to raise_error NoMethodError, /post_path/
|
||||
end
|
||||
|
||||
it "can't use url helpers" do
|
||||
expect{decorator.url_with_model}.to raise_error NoMethodError, /post_url/
|
||||
end
|
||||
|
||||
it "can't be passed implicitly to url_for" do
|
||||
expect{decorator.link}.to raise_error
|
||||
end
|
||||
end
|
|
@ -1,10 +1,16 @@
|
|||
require 'rspec/core/rake_task'
|
||||
require 'rake/testtask'
|
||||
require 'rspec/core/rake_task'
|
||||
|
||||
RSpec::Core::RakeTask.new :rspec
|
||||
|
||||
Rake::TestTask.new :mini_test do |t|
|
||||
t.test_files = ["mini_test/mini_test_integration_test.rb"]
|
||||
Rake::Task[:test].clear
|
||||
Rake::TestTask.new :test do |t|
|
||||
t.libs << "test"
|
||||
t.pattern = "test/**/*_test.rb"
|
||||
end
|
||||
|
||||
task :default => [:rspec, :mini_test]
|
||||
RSpec::Core::RakeTask.new :spec
|
||||
|
||||
RSpec::Core::RakeTask.new :fast_spec do |t|
|
||||
t.pattern = "fast_spec/**/*_spec.rb"
|
||||
end
|
||||
|
||||
task :default => [:test, :spec, :fast_spec]
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
describe "A spec in this folder" do
|
||||
it "is a decorator spec" do
|
||||
expect(example.metadata[:type]).to be :decorator
|
||||
end
|
||||
end
|
||||
require 'spec_helper'
|
||||
|
||||
describe "A decorator spec" do
|
||||
it "can access helpers through `helper`" do
|
|
@ -39,4 +39,8 @@ describe PostDecorator do
|
|||
it "serializes overriden attributes" do
|
||||
expect(decorator.serializable_hash["updated_at"]).to be :overridden
|
||||
end
|
||||
|
||||
it "uses a test view context from ApplicationController" do
|
||||
expect(Draper::ViewContext.current.controller).to be_an ApplicationController
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "A spec in this folder" do
|
||||
it "is a decorator spec" do
|
||||
expect(example.metadata[:type]).to be :decorator
|
||||
end
|
||||
end
|
|
@ -0,0 +1,22 @@
|
|||
require 'spec_helper'
|
||||
|
||||
def it_does_not_leak_view_context
|
||||
2.times do
|
||||
it "has an independent view context" do
|
||||
expect(Draper::ViewContext.current).not_to be :leaked
|
||||
Draper::ViewContext.current = :leaked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "A decorator spec", type: :decorator do
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A controller spec", type: :controller do
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A mailer spec", type: :mailer do
|
||||
it_does_not_leak_view_context
|
||||
end
|
|
@ -25,5 +25,9 @@ describe PostMailer do
|
|||
it "can use url helpers with an id" do
|
||||
expect(email_body).to have_css "#url_with_id", text: "http://www.example.com:12345/en/posts/#{post.id}"
|
||||
end
|
||||
|
||||
it "uses the correct view context controller" do
|
||||
expect(email_body).to have_css "#controller", text: "PostMailer"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
require 'minitest_helper'
|
||||
|
||||
describe "A decorator test" do
|
||||
it "can access helpers through `helper`" do
|
||||
assert_equal "<p>Help!</p>", helper.content_tag(:p, "Help!")
|
||||
end
|
||||
|
||||
it "can access helpers through `helpers`" do
|
||||
assert_equal "<p>Help!</p>", helpers.content_tag(:p, "Help!")
|
||||
end
|
||||
|
||||
it "can access helpers through `h`" do
|
||||
assert_equal "<p>Help!</p>", h.content_tag(:p, "Help!")
|
||||
end
|
||||
end
|
|
@ -1,6 +1,4 @@
|
|||
require File.expand_path('../../config/environment', __FILE__)
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/rails'
|
||||
require 'minitest_helper'
|
||||
|
||||
def it_is_a_decorator_test
|
||||
it "is a decorator test" do
|
||||
|
@ -33,6 +31,14 @@ describe "AnyDecorator" do
|
|||
it_is_a_decorator_test
|
||||
end
|
||||
|
||||
describe "Any decorator" do
|
||||
it_is_a_decorator_test
|
||||
end
|
||||
|
||||
describe "AnyDecoratorTest" do
|
||||
it_is_a_decorator_test
|
||||
end
|
||||
|
||||
describe "Any decorator test" do
|
||||
it_is_a_decorator_test
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
require 'minitest_helper'
|
||||
|
||||
def it_does_not_leak_view_context
|
||||
2.times do
|
||||
it "has an independent view context" do
|
||||
refute_equal :leaked, Draper::ViewContext.current
|
||||
Draper::ViewContext.current = :leaked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "A decorator test" do
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A controller test" do
|
||||
tests Class.new(ActionController::Base)
|
||||
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
describe "A mailer test" do
|
||||
it_does_not_leak_view_context
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
require 'test_helper'
|
||||
|
||||
class HelpersTest < Draper::TestCase
|
||||
def test_access_helpers_through_helper
|
||||
assert_equal "<p>Help!</p>", helper.content_tag(:p, "Help!")
|
||||
end
|
||||
|
||||
def test_access_helpers_through_helpers
|
||||
assert_equal "<p>Help!</p>", helpers.content_tag(:p, "Help!")
|
||||
end
|
||||
|
||||
def test_access_helpers_through_h
|
||||
assert_equal "<p>Help!</p>", h.content_tag(:p, "Help!")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
require 'test_helper'
|
||||
|
||||
def it_does_not_leak_view_context
|
||||
2.times do |n|
|
||||
define_method("test_has_independent_view_context_#{n}") do
|
||||
refute_equal :leaked, Draper::ViewContext.current
|
||||
Draper::ViewContext.current = :leaked
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DecoratorTest < Draper::TestCase
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
class ControllerTest < ActionController::TestCase
|
||||
tests Class.new(ActionController::Base)
|
||||
|
||||
it_does_not_leak_view_context
|
||||
end
|
||||
|
||||
class MailerTest < ActionMailer::TestCase
|
||||
it_does_not_leak_view_context
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.expand_path('../../config/environment', __FILE__)
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/rails'
|
|
@ -0,0 +1,3 @@
|
|||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.expand_path('../../config/environment', __FILE__)
|
||||
require 'rails/test_help'
|
|
@ -3,9 +3,13 @@ require 'support/dummy_app'
|
|||
require 'support/matchers/have_text'
|
||||
|
||||
app = DummyApp.new(ENV["RAILS_ENV"])
|
||||
spec_types = {
|
||||
view: ["/posts/1", "PostsController"],
|
||||
mailer: ["/posts/1/mail", "PostMailer"]
|
||||
}
|
||||
|
||||
app.start_server do
|
||||
{view: "/posts/1", mailer: "/posts/1/mail"}.each do |type, path|
|
||||
spec_types.each do |type, (path, controller)|
|
||||
page = app.get(path)
|
||||
|
||||
describe "in a #{type}" do
|
||||
|
@ -13,6 +17,10 @@ app.start_server do
|
|||
expect(page).to have_text(app.environment).in("#environment")
|
||||
end
|
||||
|
||||
it "uses the correct view context controller" do
|
||||
expect(page).to have_text(controller).in("#controller")
|
||||
end
|
||||
|
||||
it "can use built-in helpers" do
|
||||
expect(page).to have_text("Once upon a...").in("#truncated")
|
||||
end
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
require 'bundler/setup'
|
||||
require 'draper'
|
||||
require 'action_controller'
|
||||
require 'action_controller/test_case'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
|
@ -28,3 +30,7 @@ end
|
|||
def protect_class(klass)
|
||||
before { stub_const klass.name, Class.new(klass) }
|
||||
end
|
||||
|
||||
def protect_module(mod)
|
||||
before { stub_const mod.name, mod.dup }
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue