focused_controller 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.travis.yml +15 -0
- data/Appraisals +11 -0
- data/Gemfile +4 -0
- data/README.md +1 -0
- data/Rakefile +21 -0
- data/focused_controller.gemspec +30 -0
- data/gemfiles/rails-3-0.gemfile +7 -0
- data/gemfiles/rails-3-1.gemfile +7 -0
- data/gemfiles/rails-3-2.gemfile +7 -0
- data/lib/focused_controller.rb +4 -0
- data/lib/focused_controller/action_name.rb +6 -0
- data/lib/focused_controller/functional_test_helper.rb +25 -0
- data/lib/focused_controller/mixin.rb +44 -0
- data/lib/focused_controller/route.rb +15 -0
- data/lib/focused_controller/route_mapper.rb +58 -0
- data/lib/focused_controller/rspec_controller_class.rb +15 -0
- data/lib/focused_controller/rspec_functional_helper.rb +25 -0
- data/lib/focused_controller/rspec_helper.rb +42 -0
- data/lib/focused_controller/test_helper.rb +183 -0
- data/lib/focused_controller/version.rb +3 -0
- data/test/acceptance/app_test.rb +156 -0
- data/test/app/.gitignore +15 -0
- data/test/app/Gemfile +6 -0
- data/test/app/README.rdoc +261 -0
- data/test/app/Rakefile +7 -0
- data/test/app/app/controllers/application_controller.rb +6 -0
- data/test/app/app/controllers/posts_controller.rb +60 -0
- data/test/app/app/models/.gitkeep +0 -0
- data/test/app/app/models/post.rb +88 -0
- data/test/app/app/views/layouts/application.html.erb +13 -0
- data/test/app/app/views/posts/_form.html.erb +31 -0
- data/test/app/app/views/posts/edit.html.erb +6 -0
- data/test/app/app/views/posts/index.html.erb +23 -0
- data/test/app/app/views/posts/new.html.erb +5 -0
- data/test/app/app/views/posts/show.html.erb +8 -0
- data/test/app/config.ru +4 -0
- data/test/app/config/application.rb +16 -0
- data/test/app/config/boot.rb +6 -0
- data/test/app/config/environment.rb +5 -0
- data/test/app/config/environments/development.rb +21 -0
- data/test/app/config/environments/test.rb +29 -0
- data/test/app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/app/config/initializers/inflections.rb +15 -0
- data/test/app/config/initializers/mime_types.rb +5 -0
- data/test/app/config/initializers/secret_token.rb +7 -0
- data/test/app/config/initializers/session_store.rb +8 -0
- data/test/app/config/locales/en.yml +5 -0
- data/test/app/config/routes.rb +62 -0
- data/test/app/db/seeds.rb +7 -0
- data/test/app/doc/README_FOR_APP +2 -0
- data/test/app/lib/assets/.gitkeep +0 -0
- data/test/app/lib/tasks/.gitkeep +0 -0
- data/test/app/log/.gitkeep +0 -0
- data/test/app/public/404.html +26 -0
- data/test/app/public/422.html +26 -0
- data/test/app/public/500.html +25 -0
- data/test/app/public/favicon.ico +0 -0
- data/test/app/public/index.html +241 -0
- data/test/app/public/javascripts/application.js +9663 -0
- data/test/app/public/robots.txt +5 -0
- data/test/app/public/stylesheets/application.css +83 -0
- data/test/app/script/rails +6 -0
- data/test/app/spec/controllers/posts_controller_spec.rb +59 -0
- data/test/app/spec/isolated_spec_helper.rb +9 -0
- data/test/app/spec/spec_helper.rb +5 -0
- data/test/app/spec/unit/controllers/posts_controller_isolated_spec.rb +60 -0
- data/test/app/spec/unit/controllers/posts_controller_spec.rb +59 -0
- data/test/app/test/functional/.gitkeep +0 -0
- data/test/app/test/functional/posts_controller_test.rb +67 -0
- data/test/app/test/isolated_test_helper.rb +10 -0
- data/test/app/test/test_helper.rb +6 -0
- data/test/app/test/unit/.gitkeep +0 -0
- data/test/app/test/unit/controllers/posts_controller_isolated_test.rb +69 -0
- data/test/app/test/unit/controllers/posts_controller_test.rb +67 -0
- data/test/app/vendor/assets/javascripts/.gitkeep +0 -0
- data/test/app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/test/app/vendor/plugins/.gitkeep +0 -0
- data/test/helper.rb +33 -0
- data/test/unit/functional_test_helper_test.rb +65 -0
- data/test/unit/mixin_test.rb +70 -0
- data/test/unit/route_mapper_test.rb +73 -0
- data/test/unit/route_test.rb +39 -0
- data/test/unit/rspec_functional_helper.rb +42 -0
- data/test/unit/rspec_helper_test.rb +91 -0
- data/test/unit/test_helper_test.rb +235 -0
- metadata +285 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 1.9.2
|
5
|
+
- 1.8.7
|
6
|
+
- rbx-18mode
|
7
|
+
# - rbx-19mode # Seems to hang, try again another day
|
8
|
+
# Add JRuby support when poltergeist handles JRuby
|
9
|
+
gemfile:
|
10
|
+
- gemfiles/rails-3-2.gemfile
|
11
|
+
- gemfiles/rails-3-1.gemfile
|
12
|
+
- gemfiles/rails-3-0.gemfile
|
13
|
+
before_script:
|
14
|
+
- "export DISPLAY=:99.0"
|
15
|
+
- "sh -e /etc/init.d/xvfb start"
|
data/Appraisals
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/jonleighton/focused_controller.png?branch=master)](http://travis-ci.org/jonleighton/focused_controller)
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'appraisal'
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
namespace :test do
|
7
|
+
Rake::TestTask.new(:acceptance) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.test_files = FileList['test/acceptance/*_test.rb']
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
Rake::TestTask.new(:unit) do |t|
|
14
|
+
t.libs << "test"
|
15
|
+
t.test_files = FileList['test/unit/*_test.rb']
|
16
|
+
t.verbose = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
task :test => ['test:unit', 'test:acceptance']
|
21
|
+
task :default => :test
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "focused_controller/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "focused_controller"
|
7
|
+
s.version = FocusedController::VERSION
|
8
|
+
s.authors = ["Jon Leighton"]
|
9
|
+
s.email = ["j@jonathanleighton.com"]
|
10
|
+
s.homepage = "http://github.com/jonleighton/focused_controller"
|
11
|
+
s.summary = %q{Write Rails controllers that don't violate SRP}
|
12
|
+
s.description = %q{Write Rails controllers that don't violate SRP}
|
13
|
+
|
14
|
+
s.rubyforge_project = "focused_controller"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency 'actionpack', '~> 3.0'
|
22
|
+
|
23
|
+
s.add_development_dependency 'minitest', '~> 2.11.2'
|
24
|
+
s.add_development_dependency 'capybara', '~> 1.1.2'
|
25
|
+
s.add_development_dependency 'capybara_minitest_spec', '~> 0.2.1'
|
26
|
+
s.add_development_dependency 'poltergeist', '~> 0.4.0'
|
27
|
+
s.add_development_dependency 'rspec', '~> 2.8.0'
|
28
|
+
s.add_development_dependency 'rspec-rails', '~> 2.8.0'
|
29
|
+
s.add_development_dependency 'appraisal', '~> 0.4.1'
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module FocusedController
|
4
|
+
module FunctionalTestHelper
|
5
|
+
def get(*args)
|
6
|
+
super(FocusedController.action_name, *args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def post(*args)
|
10
|
+
super(FocusedController.action_name, *args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def put(*args)
|
14
|
+
super(FocusedController.action_name, *args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(*args)
|
18
|
+
super(FocusedController.action_name, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def head(*args)
|
22
|
+
super(FocusedController.action_name, *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'focused_controller/action_name'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/string/conversions'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
|
6
|
+
module FocusedController
|
7
|
+
module Mixin
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :allow_view_assigns
|
12
|
+
self.allow_view_assigns = false
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def controller_path
|
17
|
+
@focused_controller_path ||= name && name.sub(/\:\:[^\:]+$/, '').sub(/Controller$/, '').underscore
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
action(FocusedController.action_name).call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def action_name
|
26
|
+
self.class.name.demodulize.underscore
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_for_action(name)
|
30
|
+
FocusedController.action_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def view_assigns
|
34
|
+
if self.class.allow_view_assigns
|
35
|
+
super
|
36
|
+
else
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'focused_controller/action_name'
|
2
|
+
require 'action_dispatch'
|
3
|
+
|
4
|
+
module FocusedController
|
5
|
+
# The monkey-patching in this file makes me sadface but I can't see
|
6
|
+
# another way ;(
|
7
|
+
class RouteMapper
|
8
|
+
def initialize(scope, options)
|
9
|
+
@scope, @options = scope, options
|
10
|
+
end
|
11
|
+
|
12
|
+
def options
|
13
|
+
options = @options.dup
|
14
|
+
|
15
|
+
if to = to_option
|
16
|
+
options[:to] = FocusedController::Route.new(to)
|
17
|
+
options[:action] = FocusedController.action_name # necessary for rails <= 3.1
|
18
|
+
end
|
19
|
+
|
20
|
+
options
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def to_option
|
26
|
+
if @options[:to] && !@options[:to].respond_to?(:call)
|
27
|
+
@options[:to]
|
28
|
+
elsif @options[:action] && @scope[:controller]
|
29
|
+
name = ''
|
30
|
+
name << @scope[:module].camelize << '::' if @scope[:module]
|
31
|
+
name << @scope[:controller].camelize << 'Controller::'
|
32
|
+
name << @options[:action].to_s.camelize
|
33
|
+
name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ActionDispatch::Routing::Mapper
|
39
|
+
def focused_controller_routes(&block)
|
40
|
+
prev, @scope[:focused_controller_routes] = @scope[:focused_controller_routes], true
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
@scope[:focused_controller_routes] = false
|
44
|
+
end
|
45
|
+
|
46
|
+
class Mapping
|
47
|
+
def initialize_with_focused_controller(set, scope, path, options)
|
48
|
+
if scope[:focused_controller_routes]
|
49
|
+
options = FocusedController::RouteMapper.new(scope, options).options
|
50
|
+
end
|
51
|
+
|
52
|
+
initialize_without_focused_controller(set, scope, path, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method_chain :initialize, :focused_controller
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module FocusedController
|
2
|
+
module RSpecControllerClass
|
3
|
+
def controller_class
|
4
|
+
metadata = self.metadata[:example_group]
|
5
|
+
klass = nil
|
6
|
+
|
7
|
+
until metadata.nil? || klass.respond_to?(:new)
|
8
|
+
klass = metadata[:description_args].first
|
9
|
+
metadata = metadata[:example_group]
|
10
|
+
end
|
11
|
+
|
12
|
+
klass.respond_to?(:new) ? klass : super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
require 'action_view'
|
3
|
+
require 'action_dispatch'
|
4
|
+
require 'rspec/rails'
|
5
|
+
require 'focused_controller/functional_test_helper'
|
6
|
+
require 'focused_controller/rspec_controller_class'
|
7
|
+
|
8
|
+
module FocusedController
|
9
|
+
module RSpecFunctionalHelper
|
10
|
+
def self.append_features(base)
|
11
|
+
base.class_eval do
|
12
|
+
# This must be included first
|
13
|
+
include RSpec::Rails::ControllerExampleGroup
|
14
|
+
extend ClassMethods
|
15
|
+
include FocusedController::FunctionalTestHelper
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
include FocusedController::RSpecControllerClass
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'focused_controller/test_helper'
|
2
|
+
require 'focused_controller/rspec_controller_class'
|
3
|
+
|
4
|
+
begin
|
5
|
+
# Requiring specific files rather than just 'rspec/rails' because I don't
|
6
|
+
# want to force the configuration that 'rspec/rails' adds on people if they
|
7
|
+
# haven't specifically chosen to receive it.
|
8
|
+
require 'rspec/rails/matchers'
|
9
|
+
require 'rspec/rails/adapters'
|
10
|
+
require 'rspec/rails/example/rails_example_group'
|
11
|
+
rescue LoadError
|
12
|
+
end
|
13
|
+
|
14
|
+
module FocusedController
|
15
|
+
module RSpecHelper
|
16
|
+
def self.append_features(base)
|
17
|
+
base.class_eval do
|
18
|
+
# This must get included higher in the ancestor chain than
|
19
|
+
# this module so that inheritance works as desired
|
20
|
+
include FocusedController::TestHelper
|
21
|
+
extend ClassMethods
|
22
|
+
subject { controller }
|
23
|
+
end
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
if defined?(RSpec::Rails)
|
29
|
+
include RSpec::Rails::RailsExampleGroup
|
30
|
+
include RSpec::Rails::Matchers::RedirectTo
|
31
|
+
include RSpec::Rails::Matchers::RenderTemplate
|
32
|
+
end
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
include FocusedController::RSpecControllerClass
|
36
|
+
|
37
|
+
def stub_url(*helper_names)
|
38
|
+
before { stub_url(*helper_names) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'action_dispatch'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/hash_with_indifferent_access'
|
5
|
+
|
6
|
+
module FocusedController
|
7
|
+
module TestHooks
|
8
|
+
attr_reader :_render_options
|
9
|
+
|
10
|
+
def render_to_body(options = {})
|
11
|
+
_process_options(options)
|
12
|
+
@_render_options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def url_for(options = nil)
|
16
|
+
if options.is_a?(StubbedURL)
|
17
|
+
options
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class TestRequest < ActionDispatch::TestRequest
|
25
|
+
def initialize(env = {})
|
26
|
+
super
|
27
|
+
self.session = HashWithIndifferentAccess.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def cookie_jar
|
31
|
+
@cookie_jar ||= ActionDispatch::Cookies::CookieJar.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def flash
|
35
|
+
session['flash'] ||= ActionDispatch::Flash::FlashHash.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TestResponse < ActionDispatch::TestResponse
|
40
|
+
end
|
41
|
+
|
42
|
+
class StubbedURL
|
43
|
+
attr_reader :helper_name, :args
|
44
|
+
|
45
|
+
def initialize(helper_name, args)
|
46
|
+
@helper_name = helper_name.to_s
|
47
|
+
@args = args
|
48
|
+
end
|
49
|
+
|
50
|
+
def ==(other)
|
51
|
+
other.is_a?(self.class) &&
|
52
|
+
helper_name == other.helper_name &&
|
53
|
+
args == other.args
|
54
|
+
end
|
55
|
+
|
56
|
+
# Deals with _compute_redirect_to_location in action_controller/metal/redirecting
|
57
|
+
# (I don't feel proud about this...)
|
58
|
+
def gsub(*)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"#{helper_name}(#{args.each(&:to_s).join(', ')})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module TestHelper
|
68
|
+
extend ActiveSupport::Concern
|
69
|
+
include ActionDispatch::Assertions::ResponseAssertions
|
70
|
+
|
71
|
+
included do
|
72
|
+
class_attribute :_controller_class, :instance_reader => false, :instance_writer => false
|
73
|
+
end
|
74
|
+
|
75
|
+
module ClassMethods
|
76
|
+
def controller_class=(klass)
|
77
|
+
self._controller_class = klass
|
78
|
+
end
|
79
|
+
|
80
|
+
def controller_class
|
81
|
+
_controller_class || name.sub(/Test$/, '').constantize
|
82
|
+
end
|
83
|
+
|
84
|
+
def include_routes
|
85
|
+
if controller_class.respond_to?(:_routes) && controller_class._routes
|
86
|
+
include controller_class._routes.named_routes.module
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def stub_url(*names)
|
91
|
+
setup { stub_url(*names) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def controller
|
96
|
+
@controller ||= begin
|
97
|
+
controller = self.class.controller_class.new
|
98
|
+
controller.singleton_class.send :include, TestHooks
|
99
|
+
controller.request = request
|
100
|
+
controller.response = response
|
101
|
+
controller
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def request
|
106
|
+
@request ||= TestRequest.new
|
107
|
+
end
|
108
|
+
|
109
|
+
def response
|
110
|
+
@response ||= TestResponse.new
|
111
|
+
end
|
112
|
+
|
113
|
+
def req(params = nil, session = nil, flash = nil)
|
114
|
+
controller.params = params if params
|
115
|
+
controller.session.update session if session
|
116
|
+
controller.flash.update flash if flash
|
117
|
+
controller.run
|
118
|
+
end
|
119
|
+
|
120
|
+
def session
|
121
|
+
controller.session
|
122
|
+
end
|
123
|
+
|
124
|
+
def flash
|
125
|
+
controller.flash
|
126
|
+
end
|
127
|
+
|
128
|
+
def cookies
|
129
|
+
request.cookie_jar
|
130
|
+
end
|
131
|
+
|
132
|
+
def assert_template(template, message = nil)
|
133
|
+
assert_equal template.to_s, controller._render_options[:template], message
|
134
|
+
end
|
135
|
+
|
136
|
+
def assert_response(type, message = nil)
|
137
|
+
controller # make sure controller is initialized
|
138
|
+
super
|
139
|
+
end
|
140
|
+
|
141
|
+
def assert_redirected_to(location, message = nil)
|
142
|
+
controller # make sure controller is initialized
|
143
|
+
super
|
144
|
+
end
|
145
|
+
|
146
|
+
def url_for(*args)
|
147
|
+
controller.url_for(*args)
|
148
|
+
end
|
149
|
+
|
150
|
+
def respond_to?(method_name)
|
151
|
+
unless defined?(@_routes_included) && @_routes_included
|
152
|
+
self.class.include_routes
|
153
|
+
@_routes_included = true
|
154
|
+
end
|
155
|
+
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
def method_missing(method_name, *args, &block)
|
160
|
+
if respond_to?(method_name)
|
161
|
+
send(method_name, *args, &block)
|
162
|
+
else
|
163
|
+
super
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def stub_url(*names)
|
168
|
+
[self, controller].each do |host|
|
169
|
+
host.singleton_class.class_eval do
|
170
|
+
names.each do |name|
|
171
|
+
define_method("#{name}_url") do |*args|
|
172
|
+
StubbedURL.new("#{name}_url", args)
|
173
|
+
end
|
174
|
+
|
175
|
+
define_method("#{name}_path") do |*args|
|
176
|
+
StubbedURL.new("#{name}_path", args)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|