shutl_rails 0.8.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.
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