class-action 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +33 -16
- data/.travis.yml +6 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +62 -0
- data/README.md +42 -11
- data/class-action.gemspec +1 -0
- data/lib/class-action/rspec.rb +2 -0
- data/lib/class_action/action.rb +113 -63
- data/lib/class_action/rspec/class_action_example_group.rb +57 -0
- data/lib/class_action/rspec/have_class_action_matcher.rb +67 -0
- data/lib/class_action/rspec/respond_to_format_matcher.rb +72 -0
- data/lib/class_action/rspec/respond_with_matcher.rb +60 -0
- data/lib/class_action/rspec.rb +3 -0
- data/lib/class_action/version.rb +1 -1
- data/lib/class_action.rb +12 -8
- data/spec/class_action/action_spec.rb +202 -29
- data/spec/class_action/rspec/have_class_action_matcher_spec.rb +69 -0
- data/spec/class_action/rspec/respond_to_format_matcher_spec.rb +76 -0
- data/spec/class_action/rspec/respond_with_matcher_spec.rb +84 -0
- data/spec/class_action_spec.rb +8 -1
- data/spec/spec_helper.rb +6 -0
- metadata +32 -7
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/ClassAction.sublime-project +0 -10
- data/ClassAction.sublime-workspace +0 -1873
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2d7c4f6566ad04e2d10d0cfcc3e107302afb367d
         | 
| 4 | 
            +
              data.tar.gz: 1fc331ef010a59d7723ca3faa206dd72b4322b17
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 2ae15f473e25cdf638d5aaa51daf9239f61d4ee157544ce55d5184019405795584a8538eb693d0377730117dca662de16fcfdd96e3cbb96e3b3ebcc7c434f83b
         | 
| 7 | 
            +
              data.tar.gz: 16c0a85b1a3798f913171cecc68830cb5f046d6689a33ddeb316458b85d9168ea019d174fcc2049c6a6358ff3684b722f2c398824d6b4391d59cd9014e6af00b
         | 
    
        data/.gitignore
    CHANGED
    
    | @@ -1,17 +1,34 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 1 | 
            +
            # See http://help.github.com/ignore-files/ for more about ignoring files.
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # If you find yourself ignoring temporary files generated by your text editor
         | 
| 4 | 
            +
            # or operating system, you probably want to add a global ignore instead:
         | 
| 5 | 
            +
            #   git config --global core.excludesfile ~/.gitignore_global
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # Ignore .DS_Store
         | 
| 8 | 
            +
            .DS_Store
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            # Ignore the output files.
         | 
| 11 | 
            +
            /pkg
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            # Ignore bundler and database config and precompiled assets
         | 
| 14 | 
            +
            /.bundle
         | 
| 15 | 
            +
            /.env
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            # RVM files
         | 
| 18 | 
            +
            .rvmrc
         | 
| 19 | 
            +
            .ruby-version
         | 
| 20 | 
            +
            .ruby-gemset
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            # Ignore all logfiles and tempfiles.
         | 
| 23 | 
            +
            /tmp
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            # Ignore TextMate projects
         | 
| 26 | 
            +
            *.tmproj
         | 
| 27 | 
            +
            *.sublime-project
         | 
| 28 | 
            +
            *.sublime-workspace
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            # Documentation files and other stuff
         | 
| 5 31 | 
             
            .yardoc
         | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
            coverage
         | 
| 10 | 
            -
            doc/
         | 
| 11 | 
            -
            lib/bundler/man
         | 
| 12 | 
            -
            pkg
         | 
| 13 | 
            -
            rdoc
         | 
| 14 | 
            -
            spec/reports
         | 
| 15 | 
            -
            test/tmp
         | 
| 16 | 
            -
            test/version_tmp
         | 
| 17 | 
            -
            tmp
         | 
| 32 | 
            +
            /doc
         | 
| 33 | 
            +
            /nbproject
         | 
| 34 | 
            +
            /coverage
         | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/CHANGELOG.md
    ADDED
    
    | @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            ## 1.1.0 ##
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            *   Early class action resolution
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                - Allow access to `class_action` method from before-filters.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            *   Add helper methods onto controller instance as well
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## 1.0.0.rc1 ##
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            *   Automatic controller method inferral
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                - No need to write `controller_method`
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            *   Instance variable references allowed in `respond_with`
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ## 0.0.2 ##
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            *   Extension of responses
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                - Differentiation of responses for XHR requests
         | 
| 22 | 
            +
                - Differentiation of responses based on state
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            ## 0.0.1 ##
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            *   Initial development
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                - Support for execution
         | 
| 29 | 
            +
                - Rudimentary response support
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                *Joost Lubach*
         | 
    
        data/Gemfile.lock
    ADDED
    
    | @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            PATH
         | 
| 2 | 
            +
              remote: .
         | 
| 3 | 
            +
              specs:
         | 
| 4 | 
            +
                class-action (1.1.0)
         | 
| 5 | 
            +
                  activesupport (~> 3.2)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            GEM
         | 
| 8 | 
            +
              remote: https://rubygems.org/
         | 
| 9 | 
            +
              specs:
         | 
| 10 | 
            +
                actionpack (3.2.14)
         | 
| 11 | 
            +
                  activemodel (= 3.2.14)
         | 
| 12 | 
            +
                  activesupport (= 3.2.14)
         | 
| 13 | 
            +
                  builder (~> 3.0.0)
         | 
| 14 | 
            +
                  erubis (~> 2.7.0)
         | 
| 15 | 
            +
                  journey (~> 1.0.4)
         | 
| 16 | 
            +
                  rack (~> 1.4.5)
         | 
| 17 | 
            +
                  rack-cache (~> 1.2)
         | 
| 18 | 
            +
                  rack-test (~> 0.6.1)
         | 
| 19 | 
            +
                  sprockets (~> 2.2.1)
         | 
| 20 | 
            +
                activemodel (3.2.14)
         | 
| 21 | 
            +
                  activesupport (= 3.2.14)
         | 
| 22 | 
            +
                  builder (~> 3.0.0)
         | 
| 23 | 
            +
                activesupport (3.2.14)
         | 
| 24 | 
            +
                  i18n (~> 0.6, >= 0.6.4)
         | 
| 25 | 
            +
                  multi_json (~> 1.0)
         | 
| 26 | 
            +
                builder (3.0.4)
         | 
| 27 | 
            +
                diff-lcs (1.2.5)
         | 
| 28 | 
            +
                erubis (2.7.0)
         | 
| 29 | 
            +
                hike (1.2.3)
         | 
| 30 | 
            +
                i18n (0.6.9)
         | 
| 31 | 
            +
                journey (1.0.4)
         | 
| 32 | 
            +
                multi_json (1.8.2)
         | 
| 33 | 
            +
                rack (1.4.5)
         | 
| 34 | 
            +
                rack-cache (1.2)
         | 
| 35 | 
            +
                  rack (>= 0.4)
         | 
| 36 | 
            +
                rack-test (0.6.2)
         | 
| 37 | 
            +
                  rack (>= 1.0)
         | 
| 38 | 
            +
                rake (10.1.0)
         | 
| 39 | 
            +
                rspec (2.14.1)
         | 
| 40 | 
            +
                  rspec-core (~> 2.14.0)
         | 
| 41 | 
            +
                  rspec-expectations (~> 2.14.0)
         | 
| 42 | 
            +
                  rspec-mocks (~> 2.14.0)
         | 
| 43 | 
            +
                rspec-core (2.14.7)
         | 
| 44 | 
            +
                rspec-expectations (2.14.4)
         | 
| 45 | 
            +
                  diff-lcs (>= 1.1.3, < 2.0)
         | 
| 46 | 
            +
                rspec-mocks (2.14.4)
         | 
| 47 | 
            +
                sprockets (2.2.2)
         | 
| 48 | 
            +
                  hike (~> 1.2)
         | 
| 49 | 
            +
                  multi_json (~> 1.0)
         | 
| 50 | 
            +
                  rack (~> 1.0)
         | 
| 51 | 
            +
                  tilt (~> 1.1, != 1.3.0)
         | 
| 52 | 
            +
                tilt (1.4.1)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            PLATFORMS
         | 
| 55 | 
            +
              ruby
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            DEPENDENCIES
         | 
| 58 | 
            +
              actionpack (~> 3.2)
         | 
| 59 | 
            +
              bundler (~> 1.3)
         | 
| 60 | 
            +
              class-action!
         | 
| 61 | 
            +
              rake
         | 
| 62 | 
            +
              rspec (~> 2.14)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -39,16 +39,11 @@ In your controller, make sure you have included `ClassAction`, and declare which | |
| 39 39 |  | 
| 40 40 | 
             
            Then, create your `show` action class (the default is to name this class `PostsController::Show`, but you may customize this).
         | 
| 41 41 |  | 
| 42 | 
            -
            All *public* methods are executed in order when the action is run. Any support methods you need, you will need to make protected.  | 
| 43 | 
            -
             | 
| 44 | 
            -
            Some default controller methods (`params`, `request`, `render`, `redirect_to`, `respond_to` and `respond_with`) are available at all times.
         | 
| 42 | 
            +
            All *public* methods are executed in order when the action is run. Any support methods you need, you will need to make protected. Also, all controller methods are available in the action.
         | 
| 45 43 |  | 
| 46 44 | 
             
                class PostController
         | 
| 47 45 | 
             
                  class Show < ClassAction::Action
         | 
| 48 46 |  | 
| 49 | 
            -
                    # We need this method from the controller.
         | 
| 50 | 
            -
                    controller_method :current_user
         | 
| 51 | 
            -
             | 
| 52 47 | 
             
                    def prepare
         | 
| 53 48 | 
             
                      load_post
         | 
| 54 49 | 
             
                    end
         | 
| @@ -124,9 +119,7 @@ This employs the use of `ActionController#respond_to`. Additionally, there is su | |
| 124 119 |  | 
| 125 120 | 
             
                class Show < ClassAction::Action
         | 
| 126 121 |  | 
| 127 | 
            -
                   | 
| 128 | 
            -
             | 
| 129 | 
            -
                  respond_with :post
         | 
| 122 | 
            +
                  respond_with :@post
         | 
| 130 123 | 
             
                  respond_to :html, :json
         | 
| 131 124 |  | 
| 132 125 | 
             
                  respond_to :text do
         | 
| @@ -142,7 +135,7 @@ is roughly equivalent to: | |
| 142 135 | 
             
                  respond_to :html, :json, :text, :only => [ :show ]
         | 
| 143 136 |  | 
| 144 137 | 
             
                  def show
         | 
| 145 | 
            -
                    respond_with post do |format|
         | 
| 138 | 
            +
                    respond_with @post do |format|
         | 
| 146 139 | 
             
                      format.text do
         | 
| 147 140 | 
             
                        render :text => @post.to_yaml
         | 
| 148 141 | 
             
                      end
         | 
| @@ -151,14 +144,52 @@ is roughly equivalent to: | |
| 151 144 |  | 
| 152 145 | 
             
                end
         | 
| 153 146 |  | 
| 147 | 
            +
            Note that the value you pass to `respond_with` may be a simple symbol (e.g. `:post`) for a method or a reference to an instance variable (e.g. `:@post`).
         | 
| 148 | 
            +
             | 
| 154 149 | 
             
            In other words, using `respond_with` in conjunction with `respond_to` allows you to:
         | 
| 155 150 |  | 
| 156 | 
            -
            1. Specify which method to use to obtain the response object (the first argument to `ActionController#respond_with`). Note that this method must exist on the action | 
| 151 | 
            +
            1. Specify which method to use to obtain the response object (the first argument to `ActionController#respond_with`). Note that this method must exist on the action or controller.
         | 
| 157 152 | 
             
            2. Specify the formats that this action responds to. `ClassAction` will make sure that the controller mime types are modified accordingly.
         | 
| 158 153 | 
             
            3. Create a custom responder block in one breath.
         | 
| 159 154 |  | 
| 160 155 | 
             
            The only caveat is that you have to specify all your controller-level `respond_to` declarations *before* defining your actions using `class_action`, or you might override the `respond_to` array of your controller.
         | 
| 161 156 |  | 
| 157 | 
            +
            ### State based responses
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            In some cases you may want a certain response method (`respond_with`) or responder block (`respond_to`) to be only available in a certain case. For example, in some update action, you may want a different response based on whether the object was saved successfully.
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            Limiting a response method or reponder block this way is possible through the `on:` option in the methods `respond_with` and `respond_to`. The value of this option should correspond to a question-mark method on your action (or controller).
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            For example:
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                class Update < ClassAction::Action
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  respond_with :@post
         | 
| 168 | 
            +
                  respond_to :html, on: :failure do
         | 
| 169 | 
            +
                    render :edit, :status => :unprocessable_entity
         | 
| 170 | 
            +
                  end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                  protected
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                    def success?
         | 
| 175 | 
            +
                      @post.errors.blank?
         | 
| 176 | 
            +
                    end
         | 
| 177 | 
            +
                    def failure?
         | 
| 178 | 
            +
                      @post.errors.present?
         | 
| 179 | 
            +
                    end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
            This will effectively perform the following response logic on the controller:
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                if @post.errors.blank?
         | 
| 186 | 
            +
                  respond_with @post
         | 
| 187 | 
            +
                elsif @post.errors.present?
         | 
| 188 | 
            +
                  respond_with @post do |format|
         | 
| 189 | 
            +
                    format.html { render :edit, :status => :unprocessable_entity }
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
                end
         | 
| 192 | 
            +
             | 
| 162 193 | 
             
            ## Contributing
         | 
| 163 194 |  | 
| 164 195 | 
             
            1. Fork it
         | 
    
        data/class-action.gemspec
    CHANGED
    
    | @@ -23,5 +23,6 @@ Gem::Specification.new do |spec| | |
| 23 23 | 
             
              spec.add_development_dependency "bundler", "~> 1.3"
         | 
| 24 24 | 
             
              spec.add_development_dependency "rake"
         | 
| 25 25 | 
             
              spec.add_development_dependency "rspec", "~> 2.14"
         | 
| 26 | 
            +
              spec.add_development_dependency "simplecov", "~> 2.14"
         | 
| 26 27 | 
             
              spec.add_development_dependency "actionpack", "~> 3.2"
         | 
| 27 28 | 
             
            end
         | 
    
        data/lib/class_action/action.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require 'active_support/core_ext/object/try'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ClassAction
         | 
| 2 4 |  | 
| 3 5 | 
             
              # Base class for controller actions.
         | 
| @@ -8,12 +10,15 @@ module ClassAction | |
| 8 10 |  | 
| 9 11 | 
             
                  def initialize(controller)
         | 
| 10 12 | 
             
                    @_controller = controller
         | 
| 13 | 
            +
                    @_controller.singleton_class.send :include, self.class.helpers
         | 
| 11 14 | 
             
                  end
         | 
| 12 15 |  | 
| 13 16 | 
             
                ######
         | 
| 14 17 | 
             
                # Attributes
         | 
| 15 18 |  | 
| 16 | 
            -
                   | 
| 19 | 
            +
                  def controller
         | 
| 20 | 
            +
                    @_controller
         | 
| 21 | 
            +
                  end
         | 
| 17 22 |  | 
| 18 23 | 
             
                  def available?
         | 
| 19 24 | 
             
                    true
         | 
| @@ -26,68 +31,55 @@ module ClassAction | |
| 26 31 | 
             
                  class << self
         | 
| 27 32 |  | 
| 28 33 | 
             
                    # Exposes the given controller methods into the action.
         | 
| 29 | 
            -
                    def  | 
| 30 | 
            -
                       | 
| 31 | 
            -
                         | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
                         | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
                            controller.send :#{method}, *args, &block
         | 
| 40 | 
            -
                          ensure
         | 
| 41 | 
            -
                            #{assigns_copy_from}
         | 
| 42 | 
            -
                          end
         | 
| 43 | 
            -
                          protected :#{method}
         | 
| 44 | 
            -
                        RUBY
         | 
| 45 | 
            -
                      end
         | 
| 34 | 
            +
                    def _controller_method(method)
         | 
| 35 | 
            +
                      class_eval <<-RUBY, __FILE__, __LINE__+1
         | 
| 36 | 
            +
                        def #{method}(*args, &block)
         | 
| 37 | 
            +
                          copy_assigns_to_controller
         | 
| 38 | 
            +
                          controller.send :#{method}, *args, &block
         | 
| 39 | 
            +
                        ensure
         | 
| 40 | 
            +
                          copy_assigns_from_controller
         | 
| 41 | 
            +
                        end
         | 
| 42 | 
            +
                        protected :#{method}
         | 
| 43 | 
            +
                      RUBY
         | 
| 46 44 | 
             
                    end
         | 
| 47 45 |  | 
| 48 | 
            -
             | 
| 49 | 
            -
                      methods  = public_instance_methods
         | 
| 50 | 
            -
                      methods -= [ :_execute ]
         | 
| 51 | 
            -
                      methods -= Object.public_instance_methods
         | 
| 52 | 
            -
                      methods
         | 
| 53 | 
            -
                    end
         | 
| 46 | 
            +
                  end
         | 
| 54 47 |  | 
| 48 | 
            +
                  def respond_to?(method, include_private = false)
         | 
| 49 | 
            +
                    super || (include_private && controller.respond_to?(method, true))
         | 
| 55 50 | 
             
                  end
         | 
| 56 51 |  | 
| 57 | 
            -
                   | 
| 58 | 
            -
             | 
| 52 | 
            +
                  def method_missing(method, *args, &block)
         | 
| 53 | 
            +
                    if controller.respond_to?(method, true)
         | 
| 54 | 
            +
                      self.class._controller_method method
         | 
| 55 | 
            +
                      send method, *args, &block
         | 
| 56 | 
            +
                    else
         | 
| 57 | 
            +
                      super
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                  private :method_missing
         | 
| 59 61 |  | 
| 60 62 | 
             
                ######
         | 
| 61 | 
            -
                #  | 
| 63 | 
            +
                # Execution
         | 
| 62 64 |  | 
| 63 65 | 
             
                  class << self
         | 
| 64 66 |  | 
| 65 | 
            -
                     | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                       | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
                    def helper_method(*methods)
         | 
| 72 | 
            -
                      methods.each do |method|
         | 
| 73 | 
            -
                        helpers.class_eval <<-RUBY, __FILE__, __LINE__+1
         | 
| 74 | 
            -
                          def #{method}(*args, &block)
         | 
| 75 | 
            -
                            controller.class_action.send(:#{method}, *args, &block)
         | 
| 76 | 
            -
                          end
         | 
| 77 | 
            -
                        RUBY
         | 
| 78 | 
            -
                      end
         | 
| 67 | 
            +
                    def _action_methods
         | 
| 68 | 
            +
                      methods  = public_instance_methods
         | 
| 69 | 
            +
                      methods -= [ :_execute ]
         | 
| 70 | 
            +
                      methods -= Object.public_instance_methods
         | 
| 71 | 
            +
                      methods
         | 
| 79 72 | 
             
                    end
         | 
| 80 73 |  | 
| 81 74 | 
             
                  end
         | 
| 82 75 |  | 
| 83 | 
            -
                ######
         | 
| 84 | 
            -
                # Execution
         | 
| 85 | 
            -
             | 
| 86 76 | 
             
                  def _execute
         | 
| 87 77 | 
             
                    raise ActionNotAvailable unless available?
         | 
| 88 78 |  | 
| 89 79 | 
             
                    # Execute the action by running all public methods in order.
         | 
| 90 | 
            -
                    self.class. | 
| 80 | 
            +
                    self.class._action_methods.each do |method|
         | 
| 81 | 
            +
                      next if self.method(method).arity != 0
         | 
| 82 | 
            +
             | 
| 91 83 | 
             
                      send method
         | 
| 92 84 |  | 
| 93 85 | 
             
                      # Break execution of the action when some response body is set.
         | 
| @@ -104,8 +96,17 @@ module ClassAction | |
| 104 96 | 
             
                  def _respond
         | 
| 105 97 | 
             
                    copy_assigns_to_controller
         | 
| 106 98 |  | 
| 107 | 
            -
                     | 
| 108 | 
            -
                       | 
| 99 | 
            +
                    response = self.class._responses.find do |on, response|
         | 
| 100 | 
            +
                      !on || send(:"#{on}?")
         | 
| 101 | 
            +
                    end.try(:last)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    if response
         | 
| 104 | 
            +
                      response_object = if response =~ /^@/
         | 
| 105 | 
            +
                        instance_variable_get(response)
         | 
| 106 | 
            +
                      else
         | 
| 107 | 
            +
                        send(response)
         | 
| 108 | 
            +
                      end
         | 
| 109 | 
            +
             | 
| 109 110 | 
             
                      controller.respond_with response_object, &_respond_block
         | 
| 110 111 | 
             
                    elsif _respond_block
         | 
| 111 112 | 
             
                      controller.respond_to &_respond_block
         | 
| @@ -113,46 +114,95 @@ module ClassAction | |
| 113 114 | 
             
                  end
         | 
| 114 115 |  | 
| 115 116 | 
             
                  def _respond_block
         | 
| 116 | 
            -
                    responders =  | 
| 117 | 
            -
                     | 
| 117 | 
            +
                    responders = {}
         | 
| 118 | 
            +
                    self.class._responders.each do |(format, on), block|
         | 
| 119 | 
            +
                      # Select only those responders that have a block, and for which no precondition is set, or
         | 
| 120 | 
            +
                      # one that matches the current action state.
         | 
| 121 | 
            +
                      responders[format] ||= block if block && (!on || send(:"#{on}?"))
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                    return if responders.empty?
         | 
| 118 124 |  | 
| 119 125 | 
             
                    action = self
         | 
| 120 126 | 
             
                    proc do |collector|
         | 
| 121 127 | 
             
                      responders.each do |format, block|
         | 
| 122 | 
            -
                        next unless block
         | 
| 123 128 | 
             
                        collector.send(format) do
         | 
| 124 129 | 
             
                          action.instance_exec &block
         | 
| 130 | 
            +
                          copy_assigns_to_controller
         | 
| 125 131 | 
             
                        end
         | 
| 126 132 | 
             
                      end
         | 
| 127 133 | 
             
                    end
         | 
| 128 134 | 
             
                  end
         | 
| 129 135 |  | 
| 136 | 
            +
                class << self
         | 
| 130 137 |  | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 138 | 
            +
                  ######
         | 
| 139 | 
            +
                  # Helpers
         | 
| 133 140 |  | 
| 134 | 
            -
             | 
| 141 | 
            +
                    attr_accessor :helpers
         | 
| 142 | 
            +
                    def helpers
         | 
| 143 | 
            +
                      @helpers ||= Module.new.tap do |helpers|
         | 
| 144 | 
            +
                        helpers.send :include, superclass.helpers if superclass.respond_to?(:helpers)
         | 
| 145 | 
            +
                      end
         | 
| 146 | 
            +
                    end
         | 
| 135 147 |  | 
| 136 | 
            -
                     | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 148 | 
            +
                    def helper_method(*methods)
         | 
| 149 | 
            +
                      methods.each do |method|
         | 
| 150 | 
            +
                        helpers.class_eval <<-RUBY, __FILE__, __LINE__+1
         | 
| 151 | 
            +
                          def #{method}(*args, &block)
         | 
| 152 | 
            +
                            controller = if respond_to?(:class_action)
         | 
| 153 | 
            +
                              self
         | 
| 154 | 
            +
                            else
         | 
| 155 | 
            +
                              self.controller
         | 
| 156 | 
            +
                            end
         | 
| 157 | 
            +
                            controller.class_action.send(:#{method}, *args, &block)
         | 
| 158 | 
            +
                          end
         | 
| 159 | 
            +
                        RUBY
         | 
| 160 | 
            +
                      end
         | 
| 161 | 
            +
                    end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                  ######
         | 
| 164 | 
            +
                  # Responders
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                    attr_reader :_responders, :_responses
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                    def _responses
         | 
| 169 | 
            +
                      @_responses ||= {}.tap do |responses|
         | 
| 170 | 
            +
                        responses.reverse_merge! superclass._responses if superclass.respond_to?(:_responses)
         | 
| 171 | 
            +
                      end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                      # Keep the hash in such an order that the 'nil' condition is always *last*.
         | 
| 174 | 
            +
                      # { :ok => 1, nil => 2, :invalid => 3 } => { :ok => 1, :invalid => 3, nil => 2 }
         | 
| 175 | 
            +
                      @_responses = Hash[ *@_responses.sort_by { |on, _method| on.nil? ? 1 : 0 }.flatten ]
         | 
| 176 | 
            +
                    end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                    def _responders
         | 
| 179 | 
            +
                      @_responders ||= {}.tap do |responders|
         | 
| 180 | 
            +
                        responders.reverse_merge! superclass._responders if superclass.respond_to?(:_responders)
         | 
| 181 | 
            +
                      end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                      # Keep the hash in such an order that the 'nil' conditions are always *last*.
         | 
| 184 | 
            +
                      # { [ (:html, nil) => 1, (:json, nil) => 2, (:html, :ok) => 3 } => { (:html, :ok) => 3, (:html, nil) => 1, (:json, nil) => 2 }
         | 
| 185 | 
            +
                      @_responders = Hash[ *@_responders.sort_by { |(format, on), _method| on.nil? ? 1 : 0 }.inject([]) { |arr, (key, value)| arr << key << value } ]
         | 
| 139 186 | 
             
                    end
         | 
| 140 187 |  | 
| 141 | 
            -
                     | 
| 142 | 
            -
             | 
| 188 | 
            +
                    # Defines a method that returns the response. Specify an optional precondition in the `on` parameter.
         | 
| 189 | 
            +
                    def respond_with(method, on: nil)
         | 
| 190 | 
            +
                      _responses[on.try(:to_sym)] = method
         | 
| 143 191 | 
             
                    end
         | 
| 144 192 |  | 
| 145 | 
            -
                     | 
| 193 | 
            +
                    # Defines a response block for the given format(s). Specify an optional precondition in the `on` parameter.
         | 
| 194 | 
            +
                    def respond_to(*formats, on: nil, &block)
         | 
| 146 195 | 
             
                      formats.each do |format|
         | 
| 147 | 
            -
                         | 
| 196 | 
            +
                        _responders[ [format.to_sym, on.try(:to_sym)] ] = block
         | 
| 148 197 | 
             
                      end
         | 
| 149 198 | 
             
                    end
         | 
| 150 199 |  | 
| 151 | 
            -
                     | 
| 152 | 
            -
             | 
| 200 | 
            +
                    # Defines a response block for any remaining format. Specify an optional precondition in the `on` parameter.
         | 
| 201 | 
            +
                    def respond_to_any(on: nil, &block)
         | 
| 202 | 
            +
                      respond_to :any, on: on, &block
         | 
| 153 203 | 
             
                    end
         | 
| 154 204 |  | 
| 155 | 
            -
             | 
| 205 | 
            +
                end
         | 
| 156 206 |  | 
| 157 207 | 
             
                ######
         | 
| 158 208 | 
             
                # Assigns
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            module ClassAction
         | 
| 2 | 
            +
              module RSpec
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                # Adds support for speccing Class Actions. Sets up the example as
         | 
| 5 | 
            +
                module ClassActionExampleGroup
         | 
| 6 | 
            +
                  def self.included(target)
         | 
| 7 | 
            +
                    target.send :include, ::RSpec::Rails::ControllerExampleGroup
         | 
| 8 | 
            +
                    target.extend ClassMethods
         | 
| 9 | 
            +
                    target.send :include, InstanceMethods
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    target.class_eval do
         | 
| 12 | 
            +
                      # I don't know why ControllerExampleGroup overrides this.
         | 
| 13 | 
            +
                      metadata[:type] = :class_action
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      subject { action }
         | 
| 16 | 
            +
                      before do
         | 
| 17 | 
            +
                        # This is required for response testing, as we won't use
         | 
| 18 | 
            +
                        # ActionController::TestCase#process
         | 
| 19 | 
            +
                        @controller.instance_variable_set '@_response', @response
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  module ClassMethods
         | 
| 25 | 
            +
                    def action_class
         | 
| 26 | 
            +
                      described_class
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                    def controller_class
         | 
| 29 | 
            +
                      # Controller::Action => Controller
         | 
| 30 | 
            +
                      described_class.name.sub(/(.*)::.*$/, '\1').constantize
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  module InstanceMethods
         | 
| 35 | 
            +
                    def action
         | 
| 36 | 
            +
                      @action ||= self.class.action_class.new(@controller)
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def assigns(*)
         | 
| 40 | 
            +
                      action.send :copy_assigns_to_controller
         | 
| 41 | 
            +
                      super
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  def assigns
         | 
| 46 | 
            +
                    @action.send :copy_assigns_to_controller
         | 
| 47 | 
            +
                    super
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            RSpec.configure do |c|
         | 
| 56 | 
            +
              c.include ClassAction::RSpec::ClassActionExampleGroup, type: :class_action
         | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            module ClassAction
         | 
| 2 | 
            +
              module RSpec
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                class HaveClassActionMatcher
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize(action_name)
         | 
| 7 | 
            +
                    @action_name = action_name.to_s
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def using_class(klass)
         | 
| 11 | 
            +
                    @klass = klass
         | 
| 12 | 
            +
                    self
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def matches?(controller)
         | 
| 16 | 
            +
                    @controller = controller
         | 
| 17 | 
            +
                    @reason = :unsupported and return false unless controller.respond_to?(:_class_action, true)
         | 
| 18 | 
            +
                    @reason = :not_an_action and return false unless controller.respond_to?(@action_name)
         | 
| 19 | 
            +
                    @reason = :not_a_class_action and return false unless controller.respond_to?(:"_#{@action_name}_action_class", true)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    if @klass
         | 
| 22 | 
            +
                      @found_class = controller.send(:"_#{@action_name}_action_class").class
         | 
| 23 | 
            +
                      @reason = :incorrect_class and return false if @found_class != @klass
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    true
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def description
         | 
| 30 | 
            +
                    if @klass
         | 
| 31 | 
            +
                      "have class action :#{@action_name} using class #{@klass}"
         | 
| 32 | 
            +
                    else
         | 
| 33 | 
            +
                      "have class action :#{@action_name}"
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def failure_message_for_should
         | 
| 38 | 
            +
                    case @reason
         | 
| 39 | 
            +
                    when :unsupported
         | 
| 40 | 
            +
                      "expected controller of class #{@controller.class} to have class action :#{@action_name}, but it does not support class actions"
         | 
| 41 | 
            +
                    when :incorrect_class
         | 
| 42 | 
            +
                      "expected action #{@controller.class}##{@action_name} to use class #{@klass}, but it used #{@found_class}"
         | 
| 43 | 
            +
                    when :not_a_class_action
         | 
| 44 | 
            +
                      "expected action #{@controller.class}##{@action_name} to be a class action"
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      "expected controller of class #{@controller.class} to have class action :#{@action_name}"
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def failure_message_for_should_not
         | 
| 51 | 
            +
                    if @klass
         | 
| 52 | 
            +
                      "expected #{@controller.class}##{@action_name} not to be a class action using class #{@klass}"
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      "expected #{@controller.class}##{@action_name} not to be a class action"
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            RSpec::Matchers.module_eval do
         | 
| 64 | 
            +
              def have_class_action(action_name)
         | 
| 65 | 
            +
                ClassAction::RSpec::HaveClassActionMatcher.new(action_name)
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         |