focused_controller 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +15 -0
  3. data/Appraisals +11 -0
  4. data/Gemfile +4 -0
  5. data/README.md +1 -0
  6. data/Rakefile +21 -0
  7. data/focused_controller.gemspec +30 -0
  8. data/gemfiles/rails-3-0.gemfile +7 -0
  9. data/gemfiles/rails-3-1.gemfile +7 -0
  10. data/gemfiles/rails-3-2.gemfile +7 -0
  11. data/lib/focused_controller.rb +4 -0
  12. data/lib/focused_controller/action_name.rb +6 -0
  13. data/lib/focused_controller/functional_test_helper.rb +25 -0
  14. data/lib/focused_controller/mixin.rb +44 -0
  15. data/lib/focused_controller/route.rb +15 -0
  16. data/lib/focused_controller/route_mapper.rb +58 -0
  17. data/lib/focused_controller/rspec_controller_class.rb +15 -0
  18. data/lib/focused_controller/rspec_functional_helper.rb +25 -0
  19. data/lib/focused_controller/rspec_helper.rb +42 -0
  20. data/lib/focused_controller/test_helper.rb +183 -0
  21. data/lib/focused_controller/version.rb +3 -0
  22. data/test/acceptance/app_test.rb +156 -0
  23. data/test/app/.gitignore +15 -0
  24. data/test/app/Gemfile +6 -0
  25. data/test/app/README.rdoc +261 -0
  26. data/test/app/Rakefile +7 -0
  27. data/test/app/app/controllers/application_controller.rb +6 -0
  28. data/test/app/app/controllers/posts_controller.rb +60 -0
  29. data/test/app/app/models/.gitkeep +0 -0
  30. data/test/app/app/models/post.rb +88 -0
  31. data/test/app/app/views/layouts/application.html.erb +13 -0
  32. data/test/app/app/views/posts/_form.html.erb +31 -0
  33. data/test/app/app/views/posts/edit.html.erb +6 -0
  34. data/test/app/app/views/posts/index.html.erb +23 -0
  35. data/test/app/app/views/posts/new.html.erb +5 -0
  36. data/test/app/app/views/posts/show.html.erb +8 -0
  37. data/test/app/config.ru +4 -0
  38. data/test/app/config/application.rb +16 -0
  39. data/test/app/config/boot.rb +6 -0
  40. data/test/app/config/environment.rb +5 -0
  41. data/test/app/config/environments/development.rb +21 -0
  42. data/test/app/config/environments/test.rb +29 -0
  43. data/test/app/config/initializers/backtrace_silencers.rb +7 -0
  44. data/test/app/config/initializers/inflections.rb +15 -0
  45. data/test/app/config/initializers/mime_types.rb +5 -0
  46. data/test/app/config/initializers/secret_token.rb +7 -0
  47. data/test/app/config/initializers/session_store.rb +8 -0
  48. data/test/app/config/locales/en.yml +5 -0
  49. data/test/app/config/routes.rb +62 -0
  50. data/test/app/db/seeds.rb +7 -0
  51. data/test/app/doc/README_FOR_APP +2 -0
  52. data/test/app/lib/assets/.gitkeep +0 -0
  53. data/test/app/lib/tasks/.gitkeep +0 -0
  54. data/test/app/log/.gitkeep +0 -0
  55. data/test/app/public/404.html +26 -0
  56. data/test/app/public/422.html +26 -0
  57. data/test/app/public/500.html +25 -0
  58. data/test/app/public/favicon.ico +0 -0
  59. data/test/app/public/index.html +241 -0
  60. data/test/app/public/javascripts/application.js +9663 -0
  61. data/test/app/public/robots.txt +5 -0
  62. data/test/app/public/stylesheets/application.css +83 -0
  63. data/test/app/script/rails +6 -0
  64. data/test/app/spec/controllers/posts_controller_spec.rb +59 -0
  65. data/test/app/spec/isolated_spec_helper.rb +9 -0
  66. data/test/app/spec/spec_helper.rb +5 -0
  67. data/test/app/spec/unit/controllers/posts_controller_isolated_spec.rb +60 -0
  68. data/test/app/spec/unit/controllers/posts_controller_spec.rb +59 -0
  69. data/test/app/test/functional/.gitkeep +0 -0
  70. data/test/app/test/functional/posts_controller_test.rb +67 -0
  71. data/test/app/test/isolated_test_helper.rb +10 -0
  72. data/test/app/test/test_helper.rb +6 -0
  73. data/test/app/test/unit/.gitkeep +0 -0
  74. data/test/app/test/unit/controllers/posts_controller_isolated_test.rb +69 -0
  75. data/test/app/test/unit/controllers/posts_controller_test.rb +67 -0
  76. data/test/app/vendor/assets/javascripts/.gitkeep +0 -0
  77. data/test/app/vendor/assets/stylesheets/.gitkeep +0 -0
  78. data/test/app/vendor/plugins/.gitkeep +0 -0
  79. data/test/helper.rb +33 -0
  80. data/test/unit/functional_test_helper_test.rb +65 -0
  81. data/test/unit/mixin_test.rb +70 -0
  82. data/test/unit/route_mapper_test.rb +73 -0
  83. data/test/unit/route_test.rb +39 -0
  84. data/test/unit/rspec_functional_helper.rb +42 -0
  85. data/test/unit/rspec_helper_test.rb +91 -0
  86. data/test/unit/test_helper_test.rb +235 -0
  87. metadata +285 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ /gemfiles/*.lock
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
@@ -0,0 +1,11 @@
1
+ appraise 'rails-3-2' do
2
+ gem 'rails', '~> 3.2.0'
3
+ end
4
+
5
+ appraise 'rails-3-1' do
6
+ gem 'rails', '~> 3.1.0'
7
+ end
8
+
9
+ appraise 'rails-3-0' do
10
+ gem 'rails', '~> 3.0.0'
11
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in focused_controller.gemspec
4
+ gemspec
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,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.0.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.1.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,4 @@
1
+ require 'focused_controller/version'
2
+ require 'focused_controller/route'
3
+ require 'focused_controller/mixin'
4
+ require 'focused_controller/route_mapper'
@@ -0,0 +1,6 @@
1
+ module FocusedController
2
+ class << self
3
+ attr_accessor :action_name
4
+ end
5
+ self.action_name = 'run'
6
+ 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,15 @@
1
+ module FocusedController
2
+ class Route
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ end
8
+
9
+ def call(env)
10
+ name.constantize.call(env)
11
+ end
12
+
13
+ alias to_s name
14
+ end
15
+ 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