openstax_exchange 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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