shutl_rails 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.rbc
2
+ *.sassc
3
+ .sass-cache
4
+ capybara-*.html
5
+ .rspec
6
+ /.bundle
7
+ /vendor/bundle
8
+ /log/*
9
+ /tmp/*
10
+ /db/*.sqlite3
11
+ /public/system/*
12
+ /coverage/
13
+ /spec/tmp/*
14
+ **.orig
15
+ rerun.txt
16
+ pickle-email-*.html
17
+ pkg
18
+
19
+
20
+ Gemfile.lock
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p125
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shutl_rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Mark Burns
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Shutl::Rails
2
+
3
+ ## Summary
4
+
5
+ A convenience gem for interacting with the Shutl API from a Rails application.
6
+ Provides a JSON API within your application as a proxy to the Shutl API
7
+ whilst handling OAuth token validation transparently.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+ gem 'shutl_rails'
13
+
14
+ And then execute:
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ ```
20
+ #Credentials setup
21
+ #config/initializers/shutl.rb
22
+ Shutl::Auth.config do |c|
23
+ c.client_id = "<CLIENT_ID>"
24
+ c.client_secret = "<CLIENT_SECRET>"
25
+ c.url = "http://shutl-api-url-goes-here"
26
+ end
27
+
28
+
29
+ #app/controllers/quote_collections_controller.rb
30
+ class QuoteCollectionsController < Shutl::Rails::BackendResourcesController
31
+ end
32
+
33
+ #config/routes
34
+ resources :quote_collections, only: [:create, :show]
35
+ ```
36
+
37
+ #Converters
38
+ In order to convert attributes from the API into ones that may want to be used
39
+ by your application you can add any files in /app/converters and they are
40
+ autoloaded based on the name of the Shutl resource
41
+
42
+ #/app/converters/quote_collection_converter.rb
43
+ module QuoteCollectionConverter
44
+ extend Shutl::Resource::Converter
45
+
46
+ convert :enabled,
47
+ with: BooleanConverter,
48
+ only: :to_back_end
49
+ end
50
+
51
+ #/app/converters/boolean_converter.rb
52
+ module BooleanConverter
53
+ extend self
54
+
55
+ def to_front_end b; b end
56
+ def to_back_end b; b == 'true' end
57
+ end
58
+
59
+ ```
60
+
61
+ ## Contributing
62
+
63
+ The usual: fork, branch, commit, pull request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,148 @@
1
+ module Shutl::Rails
2
+ class BackendResourcesController < ApplicationController
3
+ include Shutl::Auth::AuthenticatedRequest
4
+
5
+ respond_to :json
6
+ before_filter :request_access_token
7
+ before_filter :load_resource, only: [:show]
8
+
9
+ rescue_from Shutl::Resource::Error do |e|
10
+ case e.response.content_type
11
+ when "text/html"
12
+ response_hash = { debug_info: e.response.body.to_json}
13
+
14
+ when "application/json"
15
+ begin
16
+ response_hash = e.response.parsed_response
17
+ rescue
18
+ debug_info = ["Failed to parse JSON response from quote service:",
19
+ e.message, e.class, e.backtrace, e.response.body].join("\n\n")
20
+
21
+ response_hash = { debug_info: debug_info}
22
+ end
23
+ end
24
+
25
+ render status: e.response.code, json: response_hash
26
+ end
27
+
28
+ def index
29
+ authenticated_request do
30
+ instance_variable_set(
31
+ pluralized_instance_variable_name,
32
+ resource_klass.all(id_params.merge auth: access_token))
33
+ end
34
+
35
+ response_collection = pluralized_instance_variable.map do |o|
36
+ attributes_to_front_end o.attributes
37
+ end
38
+
39
+ render json: response_collection
40
+ end
41
+
42
+ def new
43
+ self.instance_variable= resource_klass.new
44
+ respond_with_json
45
+ end
46
+
47
+ def create
48
+ authenticated_request do
49
+ self.instance_variable= resource_klass.create id_params.merge(attributes_from_params), auth: access_token
50
+ end
51
+
52
+ render status: instance_variable.status, json: instance_variable.parsed
53
+ end
54
+
55
+ def show
56
+ respond_with_json
57
+ end
58
+
59
+ def update
60
+ authenticated_request do
61
+ resource_klass.update id_params.merge(attributes_from_params), auth: access_token
62
+ end
63
+
64
+ render nothing: true, status: 204
65
+ end
66
+
67
+ def destroy
68
+ authenticated_request do
69
+ resource_klass.destroy(id_params.merge(id: params[:id]), auth: access_token)
70
+ end
71
+
72
+ render nothing: true, status: 204
73
+ end
74
+
75
+ private
76
+
77
+ def id_params
78
+ params.dup.keep_if do |k, v|
79
+ k == 'id' or k =~ /_id\Z/
80
+ end
81
+ end
82
+
83
+ # Respond_with does not work in this case
84
+ def respond_with_json(status=200)
85
+ render status: status, json: attributes_to_front_end(instance_variable.attributes)
86
+ end
87
+
88
+ def load_resource
89
+ authenticated_request do
90
+ self.instance_variable = resource_klass.find(id_params, auth: access_token)
91
+ end
92
+
93
+ render nothing: true, status: 404 if instance_variable.nil?
94
+ end
95
+
96
+ def resource_klass
97
+ resource_klass_name.constantize
98
+ end
99
+
100
+ def instance_variable
101
+ instance_variable_get instance_variable_name
102
+ end
103
+
104
+ def instance_variable= value
105
+ instance_variable_set instance_variable_name, value
106
+ end
107
+
108
+ def pluralized_instance_variable
109
+ instance_variable_get pluralized_instance_variable_name
110
+ end
111
+
112
+ def pluralized_instance_variable= value
113
+ instance_variable_set pluralized_instance_variable_name, value
114
+ end
115
+
116
+ def pluralized_instance_variable_name
117
+ :"@#{pluralized_resource_name}"
118
+ end
119
+
120
+ def pluralized_resource_name
121
+ singular_resource_name.pluralize
122
+ end
123
+
124
+ def instance_variable_name
125
+ :"@#{singular_resource_name}"
126
+ end
127
+
128
+ def singular_resource_name
129
+ resource_klass_name.underscore
130
+ end
131
+
132
+ def resource_klass_name
133
+ self.class.to_s.gsub(/Controller/,"").singularize
134
+ end
135
+
136
+ def attributes_from_params
137
+ @attributes ||= converter_class.to_back_end params[singular_resource_name]
138
+ end
139
+
140
+ def converter_class
141
+ Shutl::Rails::Converter.class_for resource_klass
142
+ end
143
+
144
+ def attributes_to_front_end attrs
145
+ converter_class.to_front_end attrs
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,42 @@
1
+ module Shutl::Rails::Converter
2
+ def convert *attr_names, options
3
+ @converters ||= {}
4
+
5
+ attr_names.map(&:to_sym).each do |attr_name|
6
+ @converters[attr_name] = {
7
+ converter_classes: Array(options[:with]),
8
+ methods: Array(options[:only] || [:to_back_end, :to_front_end])
9
+ }
10
+ end
11
+ end
12
+
13
+ %w(to_front_end to_back_end).each do |conversion|
14
+ define_method conversion do |attrs|
15
+ attrs = attrs.dup.with_indifferent_access
16
+ @converters.each do |attr_name, o|
17
+ if o[:methods].include?(conversion.to_sym)
18
+ attrs = convert_attribute(
19
+ attr_name, conversion, o[:converter_classes], attrs)
20
+ end
21
+ end
22
+ attrs
23
+ end
24
+ end
25
+
26
+ def convert_attribute attr_name, method, converter_classes, attrs
27
+ converter_classes.each do |converter_class|
28
+ if attrs.has_key?(attr_name)
29
+ attrs[attr_name] = converter_class.send(method, attrs[attr_name])
30
+ end
31
+ end
32
+ attrs
33
+ end
34
+
35
+ def self.class_for resource_class
36
+ begin
37
+ "#{resource_class}Converter".constantize
38
+ rescue NameError => e
39
+ Shutl::Rails::NoConverter
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,2 @@
1
+ class Shutl::Rails::Engine < Rails::Engine
2
+ end
@@ -0,0 +1,6 @@
1
+ module Shutl::Rails::NoConverter
2
+ extend self
3
+
4
+ def to_front_end value; value end
5
+ def to_back_end value; value end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Shutl
2
+ module Rails
3
+ VERSION = "0.8.0"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "shutl/rails/version"
2
+
3
+ require 'shutl_resource'
4
+
5
+ require "shutl/rails/converter"
6
+ require "shutl/rails/no_converter"
7
+ require "shutl/rails/engine"
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shutl/rails/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "shutl_rails"
8
+ gem.version = Shutl::Rails::VERSION
9
+ gem.authors = ["Mark Burns"]
10
+ gem.email = ["markthedeveloper@gmail.com"]
11
+ gem.description = %q{A gem for using shutl_resources easily in a Rails app}
12
+ gem.summary = %q{Methods for easily authenticating with OAuth2 and interacting with shutl resources }
13
+ gem.homepage = ""
14
+
15
+ gem.add_dependency 'shutl_resource', '~> 0.8.0'
16
+
17
+ gem.add_dependency 'rails', '~> 3.2.11'
18
+
19
+ gem.add_development_dependency 'rspec-rails'
20
+ gem.add_development_dependency 'debugger'
21
+ gem.add_development_dependency 'webmock', '~> 1.8.7'
22
+
23
+
24
+ gem.files = `git ls-files`.split($/)
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
+ gem.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'action_controller'
3
+ require 'rails'
4
+ require 'shutl_rails'
5
+
6
+ class ApplicationController < ActionController::Base
7
+ include Shutl::Auth::AuthenticatedRequest
8
+ end
9
+
10
+ describe ApplicationController do
11
+ before { session[:access_token] = token_in_session }
12
+
13
+ describe '#request_access_token' do
14
+
15
+ context 'token in session' do
16
+ let(:token_in_session) { 'token' }
17
+
18
+ it "doesn't do anything" do
19
+ subject.request_access_token
20
+ end
21
+ end
22
+
23
+ context 'no token in session' do
24
+ let(:token_in_session) { nil }
25
+
26
+ let(:access_token_request) do
27
+ mock 'access token request', access_token!: access_token_response
28
+ end
29
+
30
+ let(:access_token_response) do
31
+ mock 'access token response', access_token: 'token from auth service'
32
+ end
33
+
34
+ before do
35
+ Shutl::Auth::AccessTokenRequest.should_receive(:new).
36
+ and_return access_token_request
37
+ end
38
+
39
+ it "gets token from auth service and saves it in session" do
40
+ subject.access_token
41
+
42
+ session[:access_token].should == 'token from auth service'
43
+ end
44
+ end
45
+ end
46
+
47
+ describe '#authenticated_request' do
48
+ let(:token_in_session) { 'token from session' }
49
+
50
+ context 'request successful' do
51
+ it 'just runs it once' do
52
+ v = 'block not called'
53
+ subject.authenticated_request do
54
+ v = 'block called'
55
+ end
56
+
57
+ v.should == 'block called'
58
+ end
59
+ end
60
+
61
+ context 'request unauthorized' do
62
+ let(:access_token_request) do
63
+ mock 'access token request', access_token!: access_token_response
64
+ end
65
+
66
+ let(:access_token_response) do
67
+ mock 'access token response', access_token: 'token from auth service'
68
+ end
69
+
70
+ let(:responses) do
71
+ [
72
+ -> { 'success' },
73
+ -> { raise Shutl::UnauthorizedAccess.new 'message', 'response' }
74
+ ]
75
+ end
76
+
77
+ before do
78
+ Shutl::Auth::AccessTokenRequest.should_receive(:new).
79
+ and_return access_token_request
80
+ end
81
+
82
+ it 'gets new token from auth service and stores it in the session' do
83
+ subject.authenticated_request do
84
+ responses.pop.call
85
+ end
86
+
87
+ session[:access_token].should == 'token from auth service'
88
+ end
89
+
90
+ it 'calls the block second time' do
91
+ v = nil
92
+ subject.authenticated_request do
93
+ v = responses.pop.call
94
+ end
95
+ v.should == 'success'
96
+ end
97
+ end
98
+ end
99
+ end
data/spec/rails_app.rb ADDED
@@ -0,0 +1,4 @@
1
+ #require File.expand_path('../boot', __FILE__)
2
+ require 'rails/all'
3
+ class WithoutThisWeCantTestControllersWithRSpecNotSureWhyYet < Rails::Application
4
+ end
@@ -0,0 +1,40 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ #require File.expand_path("../../config/environment", __FILE__)
4
+ require './spec/rails_app'
5
+ require 'active_model'
6
+ require 'rspec/rails'
7
+ require 'rspec/autorun'
8
+
9
+ # Requires supporting ruby files with custom matchers and macros, etc,
10
+ # in spec/support/ and its subdirectories.
11
+ Dir["spec/support/**/*.rb"].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+ # ## Mock Framework
15
+ #
16
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
17
+ #
18
+ # config.mock_with :mocha
19
+ # config.mock_with :flexmock
20
+ # config.mock_with :rr
21
+
22
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
23
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
24
+
25
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
26
+ # examples within a transaction, remove the following line or assign false
27
+ # instead of true.
28
+ config.use_transactional_fixtures = true
29
+
30
+ # If true, the base class of anonymous controllers will be inferred
31
+ # automatically. This will be the default behavior in future versions of
32
+ # rspec-rails.
33
+ config.infer_base_class_for_anonymous_controllers = false
34
+
35
+ # Run specs in random order to surface order dependencies. If you find an
36
+ # order dependency and want to debug it, you can fix the order by providing
37
+ # the seed, which is printed after each run.
38
+ # --seed 1234
39
+ config.order = "random"
40
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shutl_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark Burns
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shutl_resource
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.2.11
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.2.11
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: webmock
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.8.7
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.8.7
94
+ description: A gem for using shutl_resources easily in a Rails app
95
+ email:
96
+ - markthedeveloper@gmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - .rbenv-version
103
+ - Gemfile
104
+ - LICENSE.txt
105
+ - README.md
106
+ - Rakefile
107
+ - app/controllers/shutl/rails/backend_resources_controller.rb
108
+ - lib/shutl/rails/converter.rb
109
+ - lib/shutl/rails/engine.rb
110
+ - lib/shutl/rails/no_converter.rb
111
+ - lib/shutl/rails/version.rb
112
+ - lib/shutl_rails.rb
113
+ - shutl_rails.gemspec
114
+ - spec/controllers/access_token_management_spec.rb
115
+ - spec/rails_app.rb
116
+ - spec/spec_helper.rb
117
+ homepage: ''
118
+ licenses: []
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ segments:
130
+ - 0
131
+ hash: -1862001743972581603
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ segments:
139
+ - 0
140
+ hash: -1862001743972581603
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.23
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Methods for easily authenticating with OAuth2 and interacting with shutl
147
+ resources
148
+ test_files:
149
+ - spec/controllers/access_token_management_spec.rb
150
+ - spec/rails_app.rb
151
+ - spec/spec_helper.rb