Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Proof reading and copy tweaks. #3

Merged
merged 14 commits into from

2 participants

@tekin

No description provided.

@jonleighton jonleighton merged commit dce9389 into jonleighton:master
@jonleighton
Owner

Thanks. I merged and then made some further edits in 3789042. I wasn't keen on the "side effect no 3" because it's not a side effect of having multiple actions in one object - it's just a related problem. So I pulled that back out of the list.

@tekin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 20, 2012
  1. @tekin

    Side effects

    tekin authored
  2. @tekin

    Move sub-heading to title

    tekin authored
  3. @tekin

    Reword side effect No. 1

    tekin authored
  4. @tekin

    Reword side effect No. 2

    tekin authored
  5. @tekin

    Reword opening paragraph

    tekin authored
  6. @tekin

    More tweaks to side effect No. 2

    tekin authored
  7. @tekin
  8. @tekin

    Tweak follow on sentence

    tekin authored
  9. @tekin

    Fullstop

    tekin authored
  10. @tekin

    simplify sentence

    tekin authored
  11. @tekin

    reword

    tekin authored
  12. @tekin

    Reword usage

    tekin authored
  13. @tekin

    Tweak example code

    tekin authored
  14. @tekin

    More minor tweaks

    tekin authored
This page is out of date. Refresh to see the latest.
Showing with 53 additions and 57 deletions.
  1. +53 −57 README.md
View
110 README.md
@@ -1,49 +1,44 @@
-# Focused Controller #
+# Focused Controller: Bringing Real OOP to Rails Controllers #
[![Build Status](https://secure.travis-ci.org/jonleighton/focused_controller.png?branch=master)](http://travis-ci.org/jonleighton/focused_controller)
-Bringing real OOP to Rails controllers.
## Description ##
Classical Rails controllers violate the Single Responsibility Principle.
-Each different "action" has separate responsibilities. The "create"
-action does something entirely different to the "destroy" action, yet
-they are lumped into the same object.
-
-This has two unfortunate effects:
-
-1. *Instance variables are shared with the view*. We don't declare public
- methods in the controller and call them from the view, because not all
- of these methods would make sense for every action. Using instance
- variables for this purpose makes it hard to change your implementation
- and can lead to subtle bugs (for example referencing an undeclared
- instance variable is not an error in ruby, but calling an undefined
- method is, so you catch the mistake sooner).
-
-2. *Action-specific APIs are needed*. For example, you might write a
- `before_filter` which only applies to certain actions, so we have an
- API which allows you to write `before_filter :find_post, except:
- :index`. *If each 'action' is a separate object this is entirely
- unnecessary*. Just use the normal methods of sharing code -- inheritance
- and mixins -- to apply the filter to the actions which should receive
- them. A secondary effect is that filters are often themselves
- unnecessary; for example a `before_filter` might be replaced by a call
- to `super` to receive some shared behaviour.
-
-Out of the box, Rails controller tests are not unit tests. They actually
-generate a Rack environment hash and send it through the full stack. This is
-very slow, and rather unnecessary if you are also writing acceptance
-tests. Most of the code being exercised is in Rails rather than in your
-controllers.
-
-Focused Controller aims to make controllers like any other
-object. That means they:
-
-* Only have one reason to change
-* Are easy to instantiate, with minimal dependencies, and testable in the
- same way as any other object: by calling their methods.
+Each different "action" has separate responsibilities: A `create`
+action does something entirely different to a `destroy` action, yet
+they end up lumped into the same object.
+
+This has three unfortunate side effects:
+
+1. *We end up using instance variables to share data with our views when
+ we should really be using methods*. Using instance variables for this
+ purpose makes it harder to change your implementation and can lead to
+ subtle bugs. For example, referencing an undeclared instance variable
+ in a view will work, when it should probably raise an error. We could
+ define public methods in our controllers and access those in our views,
+ but this will quickly get unmaintainable.
+
+2. *We misuse before_filters to share functionality between actions*.
+ Instead of using proper OO patterns like inheritance and mixins to keep
+ our code DRY, we shoe-horn `before_filter` with `:only` or `:except` to
+ share chunks of code between specific actions.
+
+3. *Testing controller actions is slow and unit testing them is hard*.
+ Because classical Rails controller actions are bound to
+ ActionController::Base, you can only test them by excersinng the full
+ framework stack. This is both very slow and unnecessary. You should be
+ able to write simple unit tests that just exercice the actual behaviour
+ of your actions, relying on your acceptance/integration tests to exercise
+ the full stack.
+
+Focused Controller aims to address these issues by making controllers actions
+like any other object. That means they:
+
+* Only have one reason to change.
+* Are easy to instantiate and test in issolation.
## Feedback needed ##
@@ -52,23 +47,24 @@ successfully on a production application, I'm very keen for others to
start experimenting with it and providing feedback.
Note that I will follow SemVer, and the project is currently pre-1.0, so
-there could be API changes. However if the user base grows significantly
-then I will try not to make changes too painful.
+there could be API changes. However if the user base grows significantly,
+then I will try to avoid painful changes.
## Usage ##
Focused Controller changes Rails' conventions. Rather than controllers
-being objects which contain one method per action, controllers become
-namespaces which contain one class per action. Objects which wish to use
-this convention include the `FocusedController::Mixin` module. This
-means that you can start using Focused Controller in an existing
-project without having to rewrite all your controller code.
+being classes that contain one method per action, controllers are now
+simply namespaces and each action is a class within that namespace.
+Controllers which wish to use this convention simply include the
+`FocusedController::Mixin` module. This means that you can start using
+Focused Controller in an existing project without having to rewrite all
+your existing controller code.
An example:
``` ruby
module PostsController
- # Action is just used as a common superclass for all the actions
+ # Action is a common superclass for all the actions
# inside `PostsController`.
class Action < ApplicationController
include FocusedController::Mixin
@@ -76,7 +72,7 @@ module PostsController
class Index < Action
def run
- # your code here
+ # your code here.
end
# No instance variables are shared with the view. Instead,
@@ -106,8 +102,8 @@ end
## Routing ##
-Rails' routing assumes your actions are methods inside an object whose
-name ends with 'controller'. For example:
+Rails' normal routing assumes your actions are methods inside an object
+whose name ends with 'controller'. For example:
``` ruby
get '/posts/new' => 'posts#new'
@@ -115,7 +111,7 @@ get '/posts/new' => 'posts#new'
will route `GET /posts/new` to `PostsController#new`.
-To get around this, we can use the `focused_controller_routes` helper:
+To get around this, we use the `focused_controller_routes` helper:
``` ruby
focused_controller_routes do
@@ -131,7 +127,7 @@ This is similar to writing:
get '/posts/new' => proc { |env| PostsController::New.call(env) }
```
-All the normal routing macros are supported:
+All the normal routing macros are also supported:
``` ruby
focused_controller_routes do
@@ -182,7 +178,7 @@ end
## Unit Testing ##
A better way to test your controllers is with unit tests. This involves
-creating an instance of your action object and calling methods on it. For
+creating an instance of your action class and calling methods on it. For
example, to test that your `user` method finds the correct user, you
might write:
@@ -216,12 +212,12 @@ depending on what's in it. For example, your `#run` method may use
To make the experience smoother, Focused Controller sets up mock
versions of these objects, much like with classical functional testing.
-It also provides accessors for these objects to your test class.
+It also provides accessors for these objects in your test class.
The fact that we have to do this is an indication of high coupling
between the controller and these other objects. In the future, I want to
-look at ways to reduce this coupling and make the testing more
-straightforward and obvious.
+look at ways to reduce this coupling and make testing more straightforward
+and obvious.
In the mean time, here is an example:
@@ -309,8 +305,8 @@ end
## Isolated unit tests ##
-It is possible to completely decouple your focused controller tests from the
-Rails application. This means you don't have to pay the penalty of
+It is possible to completely decouple your focused controller tests from
+the Rails application. This means you don't have to pay the penalty of
starting up Rails every time you want to run a test. The benefit this
brings will depend on how coupled your controllers/tests are to other
dependencies.
Something went wrong with that request. Please try again.