edh 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,12 @@
1
+ # Override autotest default magic to rerun all tests every time a
2
+ # change is detected on the file system.
3
+ class Autotest
4
+
5
+ def get_to_green
6
+ begin
7
+ rerun_all_tests
8
+ wait_for_changes unless all_good
9
+ end until all_good
10
+ end
11
+
12
+ end
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ pkg
2
+ .project
3
+ Gemfile.lock
4
+ .rvmrc
5
+ *.rbc
6
+ *~
7
+ .yardoc/
8
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --order rand
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - ruby-head
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ - ree
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ --readme readme.md
2
+ --title "Koala Documentation"
3
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :development do
4
+ gem "yard"
5
+ end
6
+
7
+ group :development, :test do
8
+ gem "typhoeus" unless defined? JRUBY_VERSION
9
+
10
+ # Testing infrastructure
11
+ gem 'guard'
12
+ gem 'guard-rspec'
13
+
14
+ if RUBY_PLATFORM =~ /darwin/
15
+ # OS X integration
16
+ gem "ruby_gntp"
17
+ gem "rb-fsevent"
18
+ end
19
+ end
20
+
21
+ gem "jruby-openssl" if defined? JRUBY_VERSION
22
+
23
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010-2011 Alex Koppel, originally Koala Gem
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 NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,25 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ Rakefile
5
+ init.rb
6
+ edh.gemspec
7
+ lib/edh.rb
8
+ lib/edh/http_services.rb
9
+ lib/edh/rest_api.rb
10
+ lib/edh/test_users.rb
11
+ readme.md
12
+ spec/edh/api_base_tests.rb
13
+ spec/edh/assets/beach.jpg
14
+ spec/edh/http_services/http_service_tests.rb
15
+ spec/edh/http_services/net_http_service_tests.rb
16
+ spec/edh/http_services/typhoeus_service_tests.rb
17
+ spec/edh/live_testing_data_helper.rb
18
+ spec/edh/rest_api/rest_api_no_access_token_tests.rb
19
+ spec/edh/rest_api/rest_api_tests.rb
20
+ spec/edh/rest_api/rest_api_with_access_token_tests.rb
21
+ spec/edh/test_users/test_users_tests.rb
22
+ spec/edh_spec.rb
23
+ spec/edh_spec_helper.rb
24
+ spec/edh_spec_without_mocks.rb
25
+ spec/mock_http_service.rb
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ Bundler::GemHelper.install_tasks
6
+ rescue LoadError
7
+ puts 'although not required, bundler is recommened for running the tests'
8
+ end
9
+
10
+ task :default => :spec
11
+
12
+ require 'rspec/core/rake_task'
13
+ RSpec::Core::RakeTask.new do |t|
14
+ t.rspec_opts = ["--color", '--format doc']
15
+ end
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
data/changelog.md ADDED
@@ -0,0 +1,4 @@
1
+ v0.1
2
+ ====
3
+
4
+ * Initial rework of EDH gem to work for Passport API, based off Koala gem by Alex Koppel.
data/edh.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+ require 'edh/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "edh"
7
+ gem.summary = "A lightweight, flexible library for EDH Passport"
8
+ gem.description = "A lightweight, flexible library for EDH Passport"
9
+ gem.homepage = "http://github.com/everydayhero/edh"
10
+ gem.version = EDH::VERSION
11
+
12
+ gem.authors = ["Alex Koppel", "Joel Richards"]
13
+ gem.email = "joelr@everydayhero.com.au"
14
+
15
+ gem.require_paths = ["lib"]
16
+ gem.files = `git ls-files`.split("\n")
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+
20
+ gem.extra_rdoc_files = ["readme.md", "changelog.md"]
21
+ gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "EDH"]
22
+
23
+ gem.add_runtime_dependency("multi_json")
24
+ gem.add_runtime_dependency("faraday")
25
+ gem.add_runtime_dependency("addressable")
26
+ gem.add_development_dependency("rspec")
27
+ gem.add_development_dependency("rake")
28
+ end
data/lib/edh.rb ADDED
@@ -0,0 +1,47 @@
1
+ # useful tools
2
+ require 'digest/md5'
3
+ require 'multi_json'
4
+
5
+ # include edh modules
6
+ require 'edh/errors'
7
+ require 'edh/api'
8
+ require 'edh/test_users'
9
+
10
+ # HTTP module so we can communicate with Passport
11
+ require 'edh/http_service'
12
+
13
+ # miscellaneous
14
+ require 'edh/utils'
15
+ require 'edh/version'
16
+
17
+ module EDH
18
+ # A Ruby client library for the Passport Platform.
19
+
20
+ # Making HTTP requests
21
+ class << self
22
+ # Control which HTTP service framework EDH uses.
23
+ attr_accessor :http_service
24
+ end
25
+
26
+ # @private
27
+ # For current HTTPServices, switch the service as expected.
28
+ def self.http_service=(service)
29
+ if service.respond_to?(:deprecated_interface)
30
+ # if this is a deprecated module, support the old interface
31
+ # by changing the default adapter so the right library is used
32
+ # we continue to use the single HTTPService module for everything
33
+ service.deprecated_interface
34
+ else
35
+ # if it's a real http_service, use it
36
+ @http_service = service
37
+ end
38
+ end
39
+
40
+ # An convenenient alias to EDH.http_service.make_request.
41
+ def self.make_request(path, args, verb, options = {})
42
+ http_service.make_request(path, args, verb, options)
43
+ end
44
+
45
+ # we use Faraday as our main service, with mock as the other main one
46
+ self.http_service = HTTPService
47
+ end
data/lib/edh/api.rb ADDED
@@ -0,0 +1,93 @@
1
+ # graph_batch_api and legacy are required at the bottom, since they depend on API being defined
2
+ require 'edh/api/rest_api'
3
+
4
+ module EDH
5
+ module Passport
6
+ class API
7
+ # Creates a new API client.
8
+ # @param [String] access_token access token
9
+ # @note If no access token is provided, you can only access some public information.
10
+ # @return [EDH::Passport::API] the API client
11
+ def initialize(access_token = nil, server = nil)
12
+ @access_token = access_token
13
+ @server = server
14
+ end
15
+
16
+ attr_accessor :access_token
17
+
18
+ include RestAPIMethods
19
+
20
+ # Makes a request to the appropriate Passport API.
21
+ # @note You'll rarely need to call this method directly.
22
+ #
23
+ # @see RestAPIMethods#rest_call
24
+ #
25
+ # @param path the server path for this request (leading / is prepended if not present)
26
+ # @param args arguments to be sent to Passport
27
+ # @param verb the HTTP method to use
28
+ # @param options request-related options for EDH and Faraday.
29
+ # @option options [Symbol] :http_component which part of the response (headers, body, or status) to return
30
+ # @option options [Boolean] :use_ssl force SSL for this request, even if it's tokenless.
31
+ # (All API requests with access tokens use SSL.)
32
+ # @param error_checking_block a block to evaluate the response status for additional JSON-encoded errors
33
+ #
34
+ # @yield The response for evaluation
35
+ #
36
+ # @raise [EDH::Passport::ServerError] if Passport returns an error (response status >= 500)
37
+ #
38
+ # @return the body of the response from Passport (unless another http_component is requested)
39
+ def api(path, args = {}, verb = "get", options = {}, &error_checking_block)
40
+ # If a access token is explicitly provided, use that
41
+ # This is explicitly needed in batch requests so GraphCollection
42
+ # results preserve any specific access tokens provided
43
+ args["access_token"] ||= @access_token || @app_access_token if @access_token || @app_access_token
44
+ options.merge!({:server => @server}) unless @server.nil?
45
+
46
+ # Translate any arrays in the params into comma-separated strings
47
+ args = sanitize_request_parameters(args)
48
+
49
+ # add a leading /
50
+ path = "/#{path}" unless path =~ /^\//
51
+
52
+ # make the request via the provided service
53
+ result = EDH.make_request(path, args, verb, options)
54
+
55
+ if result.status.to_i >= 500
56
+ raise EDH::Passport::ServerError.new(result.status.to_i, result.body)
57
+ end
58
+
59
+ yield result if error_checking_block
60
+
61
+ # if we want a component other than the body (e.g. redirect header for images), return that
62
+ if component = options[:http_component]
63
+ component == :response ? result : result.send(options[:http_component])
64
+ else
65
+ # parse the body as JSON and run it through the error checker (if provided)
66
+ # Note: Passport sometimes sends results like "true" and "false", which aren't strictly objects
67
+ # and cause MultiJson.load to fail -- so we account for that by wrapping the result in []
68
+ MultiJson.load("[#{result.body.to_s}]")[0]
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # Sanitizes Ruby objects into Passport-compatible string values.
75
+ #
76
+ # @param parameters a hash of parameters.
77
+ #
78
+ # Returns a hash in which values that are arrays of non-enumerable values
79
+ # (Strings, Symbols, Numbers, etc.) are turned into comma-separated strings.
80
+ def sanitize_request_parameters(parameters)
81
+ parameters.reduce({}) do |result, (key, value)|
82
+ # if the parameter is an array that contains non-enumerable values,
83
+ # turn it into a comma-separated list
84
+ # in Ruby 1.8.7, strings are enumerable, but we don't care
85
+ if value.is_a?(Array) && value.none? {|entry| entry.is_a?(Enumerable) && !entry.is_a?(String)}
86
+ value = value.join(",")
87
+ end
88
+ result.merge(key => value)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,58 @@
1
+ module EDH
2
+ module Passport
3
+ REST_SERVER = "passport.everydayhero.com/api/v1"
4
+
5
+ # Methods used to interact with Passport's REST API.
6
+
7
+ module RestAPIMethods
8
+ #convenience methods
9
+ def create pp_method, args = {}, options = {}
10
+ rest_call("me/#{pp_method}", args, options, :post)
11
+ end
12
+
13
+ def delete pp_action_uid, args = {}, options = {}
14
+ rest_call("actions/#{pp_action_uid}", args, options, :delete)
15
+ end
16
+
17
+ def update pp_action_uid, args = {}, options = {}
18
+ rest_call("actions/#{pp_action_uid}", args, options, :put)
19
+ end
20
+
21
+ # Make a call to the REST API.
22
+ #
23
+ # @note The order of the last two arguments is non-standard (for historical reasons). Sorry.
24
+ #
25
+ # @param pp_method the API call you want to make
26
+ #
27
+ # @raise [EDH::Passport::APIError] if Passport returns an error
28
+ #
29
+ # @return the result from Passport
30
+ def rest_call(pp_method, args = {}, options = {}, verb = "get")
31
+ args = MultiJson.load(args) if args.is_a?(String)
32
+ api("#{pp_method}", args.merge('format' => 'json'), verb, options) do |response|
33
+ # check for REST API-specific errors
34
+ if response.status >= 400
35
+ begin
36
+ response_hash = MultiJson.load(response.body)
37
+ rescue MultiJson::DecodeError
38
+ response_hash = {}
39
+ end
40
+
41
+ error_info = {
42
+ 'code' => response_hash['error_code'],
43
+ 'error_subcode' => response_hash['error_subcode'],
44
+ 'message' => response_hash['error_msg']
45
+ }
46
+
47
+ if response.status >= 500
48
+ raise ServerError.new(response.status, response.body, error_info)
49
+ else
50
+ raise ClientError.new(response.status, response.body, error_info)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ end # module Passport
58
+ end # module EDH
data/lib/edh/errors.rb ADDED
@@ -0,0 +1,78 @@
1
+ module EDH
2
+
3
+ class EDHError < StandardError; end
4
+
5
+ module Passport
6
+
7
+ class APIError < ::EDH::EDHError
8
+ attr_accessor :pp_error_type, :pp_error_code, :pp_error_subcode, :pp_error_message,
9
+ :http_status, :response_body
10
+
11
+ # Create a new API Error
12
+ #
13
+ # @param http_status [Integer] The HTTP status code of the response
14
+ # @param response_body [String] The response body
15
+ # @param error_info One of the following:
16
+ # [Hash] The error information extracted from the request
17
+ # ("type", "code", "error_subcode", "message")
18
+ # [String] The error description
19
+ # If error_info is nil or not provided, the method will attempt to extract
20
+ # the error info from the response_body
21
+ #
22
+ # @return the newly created APIError
23
+ def initialize(http_status, response_body, error_info = nil)
24
+ if response_body
25
+ self.response_body = response_body.strip
26
+ else
27
+ self.response_body = ''
28
+ end
29
+ self.http_status = http_status
30
+
31
+ if error_info && error_info.is_a?(String)
32
+ message = error_info
33
+ else
34
+ unless error_info
35
+ begin
36
+ error_info = MultiJson.load(response_body)['error'] if response_body
37
+ rescue
38
+ end
39
+ error_info ||= {}
40
+ end
41
+
42
+ self.pp_error_type = error_info["type"]
43
+ self.pp_error_code = error_info["code"]
44
+ self.pp_error_subcode = error_info["error_subcode"]
45
+ self.pp_error_message = error_info["message"]
46
+
47
+ error_array = []
48
+ %w(type code error_subcode message).each do |key|
49
+ error_array << "#{key}: #{error_info[key]}" if error_info[key]
50
+ end
51
+
52
+ if error_array.empty?
53
+ message = self.response_body
54
+ else
55
+ message = error_array.join(', ')
56
+ end
57
+ end
58
+ message += " [HTTP #{http_status}]" if http_status
59
+
60
+ super(message)
61
+ end
62
+ end
63
+
64
+ # Passport returned an invalid response body
65
+ class BadPassportResponse < APIError; end
66
+
67
+ # Any error with a 5xx HTTP status code
68
+ class ServerError < APIError; end
69
+
70
+ # Any error with a 4xx HTTP status code
71
+ class ClientError < APIError; end
72
+
73
+ # All graph API authentication failures.
74
+ class AuthenticationError < ClientError; end
75
+
76
+ end
77
+
78
+ end