openstax_exchange 0.0.0 → 0.0.1

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.
Files changed (77) hide show
  1. data/.gitignore +15 -0
  2. data/.rspec +1 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +4 -0
  7. data/MIT-LICENSE +3 -1
  8. data/README.md +58 -20
  9. data/Rakefile +5 -13
  10. data/exchange-client.gemspec +30 -0
  11. data/lib/openstax/exchange/client_instance.rb +23 -0
  12. data/lib/openstax/exchange/configuration.rb +12 -0
  13. data/lib/openstax/exchange/exceptions.rb +21 -0
  14. data/lib/openstax/exchange/exchange.rb +65 -0
  15. data/lib/openstax/exchange/fake_client/configuration.rb +13 -0
  16. data/lib/openstax/exchange/fake_client/fake_client.rb +71 -0
  17. data/lib/openstax/exchange/real_client/real_client.rb +76 -0
  18. data/lib/openstax/exchange/version.rb +1 -1
  19. data/lib/openstax_exchange.rb +8 -112
  20. data/spec/cassettes/OpenStax_Exchange/client_instance_configuration/can_be_configured_to_use_a_real_exchange_client.yml +48 -0
  21. data/spec/cassettes/OpenStax_Exchange/internal_client_instance/_client_defaults_to_a_real_exchange_client.yml +48 -0
  22. data/spec/cassettes/OpenStax_Exchange/internal_client_instance/_reset_causes_a_new_client_object_to_be_returned_by_client.yml +48 -0
  23. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_create_identifier/success/creates_a_distinct_identifer_per_invokation.yml +146 -0
  24. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_create_identifier/success/creates_and_returns_a_new_identifier.yml +97 -0
  25. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_initialize/invalid_platform_id/raises_an_exception.yml +52 -0
  26. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_initialize/invalid_platform_secret/raises_an_exception.yml +52 -0
  27. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_initialize/success/initializes_the_authentication_token.yml +48 -0
  28. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/duplicate_identifer_resource_trial_triplet/raises_an_exception.yml +194 -0
  29. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/invalid_resource_string/raises_an_exception.yml +145 -0
  30. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/success/allows_answers_with_distinct_identifiers_to_be_saved.yml +244 -0
  31. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/success/allows_answers_with_distinct_resources_to_be_saved.yml +195 -0
  32. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/success/allows_answers_with_distinct_trials_to_be_saved.yml +195 -0
  33. data/spec/cassettes/OpenStax_Exchange_RealClient/behaves_like_exchange_client_api_v1/_record_multiple_choice_answer/success/creates_a_multiple_choice_answer_associated_with_the_given_identifier.yml +146 -0
  34. data/spec/lib/openstax/exchange/client_configuration_spec.rb +58 -0
  35. data/spec/lib/openstax/exchange/fake_client_v1_spec.rb +14 -0
  36. data/spec/lib/openstax/exchange/real_client_v1_spec.rb +9 -0
  37. data/spec/lib/openstax/exchange/shared_examples_for_exchange_client_v1.rb +208 -0
  38. data/spec/spec_helper.rb +52 -38
  39. metadata +97 -100
  40. checksums.yaml +0 -15
  41. data/spec/dummy/README.md +0 -1
  42. data/spec/dummy/Rakefile +0 -7
  43. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  44. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  45. data/spec/dummy/app/controllers/api/dummy_controller.rb +0 -11
  46. data/spec/dummy/app/controllers/api/events_controller.rb +0 -7
  47. data/spec/dummy/app/controllers/application_controller.rb +0 -7
  48. data/spec/dummy/app/controllers/oauth_controller.rb +0 -8
  49. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  50. data/spec/dummy/config.ru +0 -4
  51. data/spec/dummy/config/application.rb +0 -55
  52. data/spec/dummy/config/boot.rb +0 -10
  53. data/spec/dummy/config/database.yml +0 -25
  54. data/spec/dummy/config/environment.rb +0 -5
  55. data/spec/dummy/config/environments/development.rb +0 -29
  56. data/spec/dummy/config/environments/production.rb +0 -69
  57. data/spec/dummy/config/environments/test.rb +0 -35
  58. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  59. data/spec/dummy/config/initializers/inflections.rb +0 -15
  60. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  61. data/spec/dummy/config/initializers/openstax_exchange.rb +0 -10
  62. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  63. data/spec/dummy/config/initializers/session_store.rb +0 -8
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  65. data/spec/dummy/config/locales/en.yml +0 -5
  66. data/spec/dummy/config/routes.rb +0 -11
  67. data/spec/dummy/db/development.sqlite3 +0 -0
  68. data/spec/dummy/db/schema.rb +0 -16
  69. data/spec/dummy/db/test.sqlite3 +0 -0
  70. data/spec/dummy/log/development.log +0 -929
  71. data/spec/dummy/log/test.log +0 -46753
  72. data/spec/dummy/public/404.html +0 -26
  73. data/spec/dummy/public/422.html +0 -26
  74. data/spec/dummy/public/500.html +0 -25
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/dummy/script/rails +0 -6
  77. data/spec/lib/openstax_exchange_spec.rb +0 -23
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ coverage
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ exchange-ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p545
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3-p545"
4
+ install:
5
+ - gem install bundler
6
+ - bundle install
7
+ script:
8
+ - bundle exec rake spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exchange-client.gemspec
4
+ gemspec
data/MIT-LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright 2014 Rice University
1
+ Copyright (c) 2015 Rice University
2
+
3
+ MIT License
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  exchange-ruby
2
2
  =============
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/exchange_ruby.svg)](http://badge.fury.io/rb/exchange_ruby)
4
+ [![Gem Version](https://badge.fury.io/rb/openstax_exchange.svg)](http://badge.fury.io/rb/openstax_exchange)
5
5
  [![Build Status](https://travis-ci.org/openstax/exchange-ruby.svg?branch=master)](https://travis-ci.org/openstax/exchange-ruby)
6
6
  [![Code Climate](https://codeclimate.com/github/openstax/exchange-ruby.png)](https://codeclimate.com/github/openstax/exchange-ruby)
7
7
 
@@ -10,35 +10,73 @@ A ruby client for interfacing with the OpenStax Exchange API.
10
10
  Usage
11
11
  -----
12
12
 
13
- Add the engine to your Gemfile and then run `bundle install`.
13
+ Include the gem in your project:
14
+ ```rb
15
+ gem 'openstax_exchange'
16
+ ```
17
+
18
+ Include the following in your script:
19
+
20
+ ```rb
21
+ require 'openstax_exchange'
22
+ ```
14
23
 
15
- Create an `openstax_exchange.rb` initializer in `config/initializers`,
16
- with at least the following contents:
24
+ Configure the client's knowledge of the Exchange server:
17
25
 
18
26
  ```rb
19
27
  OpenStax::Exchange.configure do |config|
20
- config.openstax_exchange_platform_id = 'value_from_openstax_exchange_here'
21
- config.openstax_exchange_platform_secret = 'value_from_openstax_exchange_here'
28
+ config.client_platform_id = '123'
29
+ config.client_platform_secret = 'abc' ## do not check real secrets into version control!
30
+ config.client_server_url = 'http://www.example.com:3000/base/path'
31
+ config.client_api_version = 'v1'
32
+ end
33
+ ```
34
+
35
+ By default the real Exchange client will be used. However, the choice can be made explicitly by using the following:
36
+
37
+ ```rb
38
+ OpenStax::Exchange.use_real_client
39
+ OpenStax::Exchange.use_fake_client
40
+ ```
41
+
42
+ If using the fake client, configure the faked server settings:
43
+
44
+ ```rb
45
+ OpenStax::Exchange::FakeClient.configure do |config|
46
+ config.registered_platforms = {'123' => 'abc'}
47
+ config.server_url = 'http://www.example.com:3000/base/path'
48
+ config.supported_api_versions = ['v1']
22
49
  end
23
50
  ```
24
51
 
25
- If you're running the OpenStax Exchange server in a dev instance on your
26
- machine, you can specify that instance's local URL with:
52
+ After changing the client configuration, use:
53
+ ```rb
54
+ OpenStax::Exchange.reset!
55
+ ```
56
+ to ensure that the changes take effect.
27
57
 
58
+ The following Exchange API methods are currently supported:
28
59
  ```rb
29
- config.openstax_exchange_url = 'http://localhost:3003/'
60
+ identifier = OpenStax::Exchange.create_identifier
30
61
  ```
62
+ ```rb
63
+ response = OpenStax::Exchange.record_multiple_choice_answer(identifier, resource_uri, trial, answer)
64
+ ```
65
+
66
+ Running the Specs
67
+ -----------------
31
68
 
32
- Exchange API
33
- ------------
69
+ Create a local clone of the repo, and run the following commands:
70
+ ```rb
71
+ bundle install
72
+ bundle exec rake
73
+ ```
74
+ The result should be a set of passing specs.
34
75
 
35
- Exchange-ruby provides convenience methods for accessing
36
- the OpenStax Exchange API.
76
+ ## Contributing
37
77
 
38
- `OpenStax::Exchange.api_call(http_method, url, options = {})` provides a
39
- convenience method capable of making API calls to Exchange. `http_method` can
40
- be any valid HTTP method, and `url` is the desired API URL, without the 'api/'
41
- prefix. Options is a hash that can contain any option that
42
- OAuth2 requests accept, such as :headers, :params, :body, etc,
43
- plus the optional values :api_version (to specify an API version) and
44
- :access_token (to specify an OAuth access token).
78
+ 1. Fork it ( https://github.com/[my-github-username]/exchange-ruby/fork )
79
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
80
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
81
+ 4. Push to the branch (`git push origin my-new-feature`)
82
+ 5. Create a new Pull Request
data/Rakefile CHANGED
@@ -1,22 +1,14 @@
1
- #!/usr/bin/env rake
2
-
3
1
  begin
4
2
  require 'bundler/setup'
5
3
  rescue LoadError
6
4
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
5
  end
8
6
 
9
- APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
10
- load 'rails/tasks/engine.rake'
11
-
12
- Bundler::GemHelper.install_tasks
13
-
14
- Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
7
+ require "bundler/gem_tasks"
8
+ require "rspec/core/rake_task"
15
9
 
16
- require 'rspec/core'
17
- require 'rspec/core/rake_task'
18
-
19
- desc 'Run all specs in spec directory (excluding plugin specs)'
20
- RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
10
+ RSpec::Core::RakeTask.new(:spec) do |task|
11
+ task.rspec_opts = ['--color', '--format', 'documentation']
12
+ end
21
13
 
22
14
  task :default => :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'openstax/exchange/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "openstax_exchange"
8
+ spec.version = OpenStax::Exchange::VERSION
9
+ spec.authors = ["Kevin Burleigh"]
10
+ spec.email = ["klb@kindlinglabs"]
11
+ spec.summary = %q{Ruby client for OpenStax Exchange}
12
+ spec.description = %q{}
13
+ spec.homepage = "http://github.com/openstax/exchange-ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "oauth2", ">= 0.5.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "simplecov"
28
+ spec.add_development_dependency "vcr"
29
+ spec.add_development_dependency "webmock"
30
+ end
@@ -0,0 +1,23 @@
1
+ module OpenStax
2
+ module Exchange
3
+ module ClientInstance
4
+
5
+ def is_real_client?
6
+ raise NotImplementedError
7
+ end
8
+
9
+ def token
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def create_identifier
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def record_multiple_choice_answer(identifier, resource, trial, answer)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module OpenStax
2
+ module Exchange
3
+
4
+ class Configuration
5
+ attr_accessor :client_platform_id
6
+ attr_accessor :client_platform_secret
7
+ attr_accessor :client_server_url
8
+ attr_accessor :client_api_version
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module OpenStax
2
+ module Exchange
3
+
4
+ class ClientError < StandardError
5
+ def initialize(msg, original=$!)
6
+ super(msg)
7
+ @original = original
8
+ set_backtrace(original.backtrace)
9
+ end
10
+
11
+ def inspect
12
+ [super, @original.inspect].join(" ")
13
+ end
14
+
15
+ def message
16
+ [super, "(#{@original.message})"].join(" ")
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,65 @@
1
+ module OpenStax
2
+ module Exchange
3
+
4
+ @use_real_client = true
5
+
6
+ def self.configure
7
+ yield configuration
8
+ end
9
+
10
+ def self.configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def self.reset!
15
+ @client = nil
16
+ end
17
+
18
+ def self.use_real_client
19
+ @use_real_client = true
20
+ end
21
+
22
+ def self.use_fake_client
23
+ @use_real_client = false
24
+ end
25
+
26
+ def self.use_real_client?
27
+ !!@use_real_client
28
+ end
29
+
30
+ def self.create_identifier
31
+ begin
32
+ client.create_identifier
33
+ rescue StandardError => error
34
+ raise ClientError.new("create_identifier failure", error)
35
+ end
36
+ end
37
+
38
+ def self.record_multiple_choice_answer(*args)
39
+ begin
40
+ client.record_multiple_choice_answer(*args)
41
+ rescue StandardError => error
42
+ raise ClientError.new("record_multiple_choice_answer failure", error)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def self.client
49
+ begin
50
+ @client ||= create_client
51
+ rescue StandardError => error
52
+ raise ClientError.new("initialization failure", error)
53
+ end
54
+ end
55
+
56
+ def self.create_client
57
+ if use_real_client?
58
+ RealClient.new configuration
59
+ else
60
+ FakeClient.new configuration
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module OpenStax
2
+ module Exchange
3
+
4
+ class FakeClient
5
+ class Configuration
6
+ attr_accessor :registered_platforms
7
+ attr_accessor :server_url
8
+ attr_accessor :supported_api_versions
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,71 @@
1
+ require 'securerandom'
2
+ require 'uri'
3
+
4
+ module OpenStax
5
+ module Exchange
6
+
7
+ class FakeClient
8
+ include ClientInstance
9
+
10
+ def self.configure
11
+ yield configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def initialize(exchange_configuration)
19
+ @client_config_platform_id = exchange_configuration.client_platform_id
20
+ @client_config_platform_secret = exchange_configuration.client_platform_secret
21
+ @client_config_server_url = exchange_configuration.client_server_url
22
+ @client_config_api_version = exchange_configuration.client_api_version
23
+
24
+ @server_registered_platforms = self.class.configuration.registered_platforms
25
+ @server_server_url = self.class.configuration.server_url
26
+
27
+ raise "invalid server url" \
28
+ unless @client_config_server_url == @server_server_url
29
+
30
+ raise "invalid platform credentials" \
31
+ unless @server_registered_platforms.fetch(@client_config_platform_id) == @client_config_platform_secret
32
+
33
+ @token = SecureRandom.hex(64)
34
+ @multiple_choice_responses = {}
35
+ end
36
+
37
+ def is_real_client?
38
+ false
39
+ end
40
+
41
+ def token
42
+ @token
43
+ end
44
+
45
+ def create_identifier
46
+ SecureRandom.hex(64)
47
+ end
48
+
49
+ def record_multiple_choice_answer(identifier, resource, trial, answer)
50
+ @multiple_choice_responses[identifier] ||= {}
51
+ @multiple_choice_responses[identifier][resource] ||= {}
52
+
53
+ raise "invalid resource" \
54
+ unless URI(resource).host == "exercises.openstax.org"
55
+
56
+ raise "duplicate response for (identifier,resource,trial) triplet" \
57
+ if @multiple_choice_responses[identifier][resource][trial]
58
+
59
+ @multiple_choice_responses[identifier][resource][trial] = answer
60
+
61
+ return {
62
+ 'identifier' => identifier,
63
+ 'resource' => resource,
64
+ 'trial' => trial,
65
+ 'answer' => answer
66
+ }
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,76 @@
1
+ require 'oauth2'
2
+
3
+ module OpenStax
4
+ module Exchange
5
+
6
+ class RealClient
7
+ include ClientInstance
8
+
9
+ def initialize(exchange_configuration)
10
+ @platform_id = exchange_configuration.client_platform_id
11
+ @platform_secret = exchange_configuration.client_platform_secret
12
+ @server_url = exchange_configuration.client_server_url
13
+ @api_version = exchange_configuration.client_api_version
14
+
15
+ @oauth_client = OAuth2::Client.new(
16
+ @platform_id,
17
+ @platform_secret,
18
+ site: @server_url)
19
+
20
+ @oauth_token = @oauth_client.client_credentials.get_token
21
+ end
22
+
23
+ def is_real_client?
24
+ true
25
+ end
26
+
27
+ def token
28
+ @oauth_token.token
29
+ end
30
+
31
+ def create_identifier
32
+ options = {}
33
+ add_accept_header! options
34
+
35
+ response = @oauth_token.request(
36
+ :post,
37
+ "#{@server_url}/api/identifiers",
38
+ options)
39
+
40
+ return JSON.parse(response.body)['identifier']
41
+ end
42
+
43
+ def record_multiple_choice_answer(identifier, resource, trial, answer)
44
+ options = {}
45
+ add_accept_header! options
46
+ add_authorization_header! options
47
+
48
+ options[:body] = { identifier: identifier, resource: resource, trial: trial, answer: answer }.to_json
49
+
50
+ response = @oauth_token.request(
51
+ :post,
52
+ "#{@server_url}/api/events/platforms/multiple_choices",
53
+ options)
54
+
55
+ return JSON.parse(response.body)
56
+ end
57
+
58
+ private
59
+
60
+ def add_header_hash!(options)
61
+ options[:headers] = {} unless options.has_key? :headers
62
+ end
63
+
64
+ def add_accept_header!(options)
65
+ add_header_hash! options
66
+ options[:headers].merge!({ 'Accept' => "application/vnd.exchange.openstax.#{@api_version}" })
67
+ end
68
+
69
+ def add_authorization_header!(options)
70
+ add_header_hash! options
71
+ options[:headers].merge!({ 'Authorization' => "Bearer #{token}" })
72
+ end
73
+ end
74
+
75
+ end
76
+ end