From f28e60d275a7ae166a16e57a3f8e8e3af5505af0 Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Tue, 30 Apr 2013 16:54:26 +0300 Subject: [PATCH 1/6] adding stubbable helpers --- lib/grape.rb | 1 + lib/grape/api.rb | 10 +++++++++- lib/grape/endpoint.rb | 16 +++++++++++++++- lib/grape/helper_object.rb | 20 ++++++++++++++++++++ spec/grape/api_spec.rb | 20 ++++++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 lib/grape/helper_object.rb diff --git a/lib/grape.rb b/lib/grape.rb index 869153fc8..657379893 100644 --- a/lib/grape.rb +++ b/lib/grape.rb @@ -19,6 +19,7 @@ I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__) module Grape + autoload :HelperObject, 'grape/helper_object' autoload :API, 'grape/api' autoload :Endpoint, 'grape/endpoint' autoload :Route, 'grape/route' diff --git a/lib/grape/api.rb b/lib/grape/api.rb index bd6164a02..85e9502f3 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -268,6 +268,12 @@ def helpers(new_mod = nil, &block) change! mod end + helper.extend(mod) + mod + end + + def helper + @helper ||= HelperObject.new end # Add an authentication type to the API. Currently @@ -307,7 +313,8 @@ def mount(mounts) endpoints << Grape::Endpoint.new(settings.clone, { :method => :any, :path => path, - :app => app + :app => app, + :klass => self }) end end @@ -328,6 +335,7 @@ def route(methods, paths = ['/'], route_options = {}, &block) endpoint_options = { :method => methods, :path => paths, + :klass => self, :route_options => (@namespace_description || {}).deep_merge(@last_description || {}).deep_merge(route_options || {}) } endpoints << Grape::Endpoint.new(settings.clone, endpoint_options, &block) diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 2652d630f..363354c33 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -45,6 +45,7 @@ def initialize(settings, options = {}, &block) @block = self.class.generate_api_method(method_name, &block) end @options = options + @klass = options[:klass] raise Grape::Exceptions::MissingOption.new(:path) unless options.key?(:path) options[:path] = Array(options[:path]) @@ -358,12 +359,25 @@ def endpoints end end + def helper + @klass.helper if @klass + end + + def method_missing(method_name, *args, &block) + if helper.respond_to?(method_name) + helper.set_context(params, request, env) + helper.send(method_name, *args, &block) + else + super + end + end + def run(env) @env = env @header = {} @request = Grape::Request.new(@env) - self.extend helpers + #self.extend helpers cookies.read(@request) run_filters befores diff --git a/lib/grape/helper_object.rb b/lib/grape/helper_object.rb new file mode 100644 index 000000000..56e336569 --- /dev/null +++ b/lib/grape/helper_object.rb @@ -0,0 +1,20 @@ +module Grape + class HelperObject + + def set_context(params, request, env) + reset_context + @params = params + @request = request + @env = env + end + + def reset_context + instance_variables.each do |name| + next if name == :@mock_proxy + instance_variable_set(name, nil) + end + end + + attr_reader :params, :request, :env + end +end \ No newline at end of file diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index 4ca863782..7f6cc6ada 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -835,6 +835,26 @@ def secret last_response.body.should eql 'always there:only in admin' end + it "allows stubbing helpers" do + subject.helpers do + def foo + 'foo' + end + end + + subject.get "/" do + foo + end + + get "/" + last_response.body.should eql "foo" + + subject.helper.stub!(:foo).and_return("bar") + + get "/" + last_response.body.should eql "bar" + end + it 'is reopenable' do subject.helpers do def one From 29c0b5b8ad7b1f3ccc71b10ce742f68c20018cf4 Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Tue, 30 Apr 2013 16:59:41 +0300 Subject: [PATCH 2/6] bumping version, adding readme about stubs --- README.md | 28 ++++++++++++++++++++++++++++ lib/grape/version.rb | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 76b6d6c4c..603197ae0 100644 --- a/README.md +++ b/README.md @@ -1194,6 +1194,34 @@ RSpec.configure do |config| end ``` +### Mocking and Stubbing helper methods + +helpers are defined on a helper object which can be stubbed out. + +```ruby +require 'spec_helper' + +describe Twitter::API do + describe "logged in behaviour" do + it "returns an the user's statuses" do + user = double(statuses: [1]) + Twitter::API.helper.stub(:current_user).and_return(user) + get "/api/v1/statuses" + response.status.should == 200 + JSON.parse(response.body).should == [1] + end + + it "returns an empty array when user's logged out" do + Twitter::API.helper.stub(:current_user).and_return(nil) + get "/api/v1/statuses" + response.status.should == 200 + JSON.parse(response.body).should == [] + end + end +end +```` + + ## Reloading API Changes in Development ### Rails 3.x diff --git a/lib/grape/version.rb b/lib/grape/version.rb index 425ca1696..55afac385 100644 --- a/lib/grape/version.rb +++ b/lib/grape/version.rb @@ -1,3 +1,3 @@ module Grape - VERSION = '0.4.2' + VERSION = '0.4.3' end From eae467dd0bcf743156e5c2a9a9d8166ab4c261be Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Tue, 30 Apr 2013 17:01:21 +0300 Subject: [PATCH 3/6] fixing readme, incorrect context --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 603197ae0..bd40f1fb0 100644 --- a/README.md +++ b/README.md @@ -1210,6 +1210,9 @@ describe Twitter::API do response.status.should == 200 JSON.parse(response.body).should == [1] end + end + + describe "logged out behaviour" do it "returns an empty array when user's logged out" do Twitter::API.helper.stub(:current_user).and_return(nil) From ec8e98784118d7c0b5c4586160e44bdbc934ca36 Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Tue, 30 Apr 2013 17:53:48 +0300 Subject: [PATCH 4/6] fixing specs for 1.8.7 and similar rubies --- lib/grape/helper_object.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/grape/helper_object.rb b/lib/grape/helper_object.rb index 56e336569..d0c18c01a 100644 --- a/lib/grape/helper_object.rb +++ b/lib/grape/helper_object.rb @@ -10,7 +10,7 @@ def set_context(params, request, env) def reset_context instance_variables.each do |name| - next if name == :@mock_proxy + next if name.to_s == "@mock_proxy" instance_variable_set(name, nil) end end From 897313f89b1224c765ed6dbd595170491f9536aa Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Wed, 1 May 2013 00:40:20 +0300 Subject: [PATCH 5/6] applying requested changes --- CHANGELOG.md | 2 +- README.md | 30 ++++++++---------------------- lib/grape.rb | 2 +- lib/grape/api.rb | 2 +- lib/grape/endpoint.rb | 9 ++++----- lib/grape/helper_object.rb | 20 -------------------- 6 files changed, 15 insertions(+), 50 deletions(-) delete mode 100644 lib/grape/helper_object.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a551a14..92e4ba2ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ Next Release ============ - +* [#398](https://github.com/intridea/grape/pull/398): Feature: apply helpers onto a helper object which can then be stubbed in tests * [#378](https://github.com/intridea/grape/pull/378): Fix: stop rescuing all exceptions during formatting - [@kbarrette](https://github.com/kbarrette). * [#380](https://github.com/intridea/grape/pull/380): Fix: `Formatter#read_body_input` when transfer encoding is chunked - [@paulnicholon](https://github.com/paulnicholson). * [#344](https://github.com/intridea/grape/pull/344): Added `parser :type, nil` which disables input parsing for a given content-type - [@dblock](https://github.com/dblock). diff --git a/README.md b/README.md index bd40f1fb0..c78f90ae3 100644 --- a/README.md +++ b/README.md @@ -1196,30 +1196,16 @@ end ### Mocking and Stubbing helper methods -helpers are defined on a helper object which can be stubbed out. +Helpers are defined on a helper object which can be stubbed out. ```ruby -require 'spec_helper' - -describe Twitter::API do - describe "logged in behaviour" do - it "returns an the user's statuses" do - user = double(statuses: [1]) - Twitter::API.helper.stub(:current_user).and_return(user) - get "/api/v1/statuses" - response.status.should == 200 - JSON.parse(response.body).should == [1] - end - end - - describe "logged out behaviour" do - - it "returns an empty array when user's logged out" do - Twitter::API.helper.stub(:current_user).and_return(nil) - get "/api/v1/statuses" - response.status.should == 200 - JSON.parse(response.body).should == [] - end +context "logged in" do + it "returns an the current_user's statuses" do + user = double(statuses: [1]) + Twitter::API.helper.stub(:current_user).and_return(user) + get "/api/v1/statuses" + response.status.should == 200 + JSON.parse(response.body).should == [1] end end ```` diff --git a/lib/grape.rb b/lib/grape.rb index 657379893..a5910a7d4 100644 --- a/lib/grape.rb +++ b/lib/grape.rb @@ -19,7 +19,7 @@ I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__) module Grape - autoload :HelperObject, 'grape/helper_object' + autoload :HelperContext, 'grape/helper_context' autoload :API, 'grape/api' autoload :Endpoint, 'grape/endpoint' autoload :Route, 'grape/route' diff --git a/lib/grape/api.rb b/lib/grape/api.rb index 85e9502f3..aeee9604e 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -273,7 +273,7 @@ def helpers(new_mod = nil, &block) end def helper - @helper ||= HelperObject.new + @helper ||= HelperContext.new end # Add an authentication type to the API. Currently diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 363354c33..46dfac4df 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -359,14 +359,14 @@ def endpoints end end - def helper + def helpers_container @klass.helper if @klass end def method_missing(method_name, *args, &block) - if helper.respond_to?(method_name) - helper.set_context(params, request, env) - helper.send(method_name, *args, &block) + if helpers_container.respond_to?(method_name) + helpers_container.set(params, request, env) + helpers_container.send(method_name, *args, &block) else super end @@ -377,7 +377,6 @@ def run(env) @header = {} @request = Grape::Request.new(@env) - #self.extend helpers cookies.read(@request) run_filters befores diff --git a/lib/grape/helper_object.rb b/lib/grape/helper_object.rb deleted file mode 100644 index d0c18c01a..000000000 --- a/lib/grape/helper_object.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Grape - class HelperObject - - def set_context(params, request, env) - reset_context - @params = params - @request = request - @env = env - end - - def reset_context - instance_variables.each do |name| - next if name.to_s == "@mock_proxy" - instance_variable_set(name, nil) - end - end - - attr_reader :params, :request, :env - end -end \ No newline at end of file From a13c729a786d9f67aa0d211ad17e070b2fbed912 Mon Sep 17 00:00:00 2001 From: Tom Caspy Date: Wed, 1 May 2013 00:43:49 +0300 Subject: [PATCH 6/6] forgot to add the renamed file --- lib/grape/helper_context.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lib/grape/helper_context.rb diff --git a/lib/grape/helper_context.rb b/lib/grape/helper_context.rb new file mode 100644 index 000000000..62b105b8b --- /dev/null +++ b/lib/grape/helper_context.rb @@ -0,0 +1,20 @@ +module Grape + class HelperContext + + def set(params, request, env) + reset! + @params = params + @request = request + @env = env + end + + def reset! + instance_variables.each do |name| + next if name.to_s == "@mock_proxy" + instance_variable_set(name, nil) + end + end + + attr_reader :params, :request, :env + end +end \ No newline at end of file