psc 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 (47) hide show
  1. data/.gitignore +13 -0
  2. data/.rvmrc +2 -0
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +20 -0
  6. data/LICENSE +20 -0
  7. data/README.md +163 -0
  8. data/Rakefile +69 -0
  9. data/cucumber.yml +13 -0
  10. data/features/step_definitions/eval_steps.rb +14 -0
  11. data/features/step_definitions/setup_steps.rb +7 -0
  12. data/features/studies.feature +21 -0
  13. data/features/support/env.rb +33 -0
  14. data/features/support/int_psc.rb +165 -0
  15. data/int-psc/README-int-psc.md +57 -0
  16. data/int-psc/hsqldb/baseline.log +197 -0
  17. data/int-psc/hsqldb/baseline.properties +17 -0
  18. data/int-psc/hsqldb/baseline.script +295 -0
  19. data/int-psc/hsqldb/datasource.properties +18 -0
  20. data/int-psc/hsqldb/datasource.script +2205 -0
  21. data/int-psc/jetty/jetty-runner-7.4.0.v20110414.jar +0 -0
  22. data/int-psc/state/ABC 1200.xml +115 -0
  23. data/int-psc/state/int-psc-state.xml +64 -0
  24. data/lib/psc.rb +47 -0
  25. data/lib/psc/client.rb +35 -0
  26. data/lib/psc/connection.rb +96 -0
  27. data/lib/psc/faraday.rb +11 -0
  28. data/lib/psc/faraday/http_basic.rb +35 -0
  29. data/lib/psc/faraday/psc_token.rb +42 -0
  30. data/lib/psc/faraday/string_is_xml.rb +25 -0
  31. data/lib/psc/version.rb +3 -0
  32. data/meta.rakefile +30 -0
  33. data/psc.gemspec +33 -0
  34. data/spec/fixtures/studies-json.http +37 -0
  35. data/spec/middleware_helper.rb +18 -0
  36. data/spec/psc/client_spec.rb +39 -0
  37. data/spec/psc/connection_spec.rb +156 -0
  38. data/spec/psc/faraday/http_basic_spec.rb +15 -0
  39. data/spec/psc/faraday/psc_token_spec.rb +38 -0
  40. data/spec/psc/faraday/string_is_xml_spec.rb +49 -0
  41. data/spec/psc/version_spec.rb +11 -0
  42. data/spec/psc_spec.rb +16 -0
  43. data/spec/spec_helper.rb +15 -0
  44. data/tasks/int-psc.rake +84 -0
  45. data/tasks/psc/TODO +3 -0
  46. data/tasks/psc/state.rb +393 -0
  47. metadata +311 -0
@@ -0,0 +1,25 @@
1
+ require 'psc/faraday'
2
+
3
+ module Psc
4
+ module Faraday
5
+ ##
6
+ # Middleware which sets the request content type to `text/xml` if
7
+ # the provided body is a `String` and the content type isn't
8
+ # already set.
9
+ class StringIsXml
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ ##
15
+ # Sets the content type request header if appropriate
16
+ def call(env)
17
+ if String === env[:body]
18
+ env[:request_headers]['Content-Type'] ||= 'text/xml'
19
+ end
20
+
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Psc
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,30 @@
1
+ PLATFORMS = {
2
+ :ruby18 => 'ruby-1.8.7-p334',
3
+ :jruby => 'jruby-1.6.1',
4
+ :ruby19 => 'ruby-1.9.2-p180'
5
+ }
6
+
7
+ BUNDLER_VERSION = '1.0.13'
8
+
9
+ def ci_env
10
+ (ENV["PSC_RB_ENV"] || :ruby18).to_sym
11
+ end
12
+
13
+ namespace :ci do
14
+ task :rvmrc do
15
+ puts "#{PLATFORMS[ci_env]}@psc-rb"
16
+ end
17
+
18
+ task :ensure_bundler_available do
19
+ unless Gem.available?('bundler', BUNDLER_VERSION)
20
+ puts "Installing bundler"
21
+ sh "gem install bundler -v '#{BUNDLER_VERSION}'"
22
+ end
23
+ end
24
+
25
+ task :setup => [:ensure_bundler_available] do
26
+ sh "bundle _#{BUNDLER_VERSION}_ update"
27
+ end
28
+ end
29
+
30
+ task :default => 'ci:setup'
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "psc/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "psc"
7
+ s.version = Psc::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Rhett Sutphin"]
10
+ s.email = ["r-sutphin@northwestern.edu"]
11
+ s.homepage = "https://github.com/NUBIC/psc.rb"
12
+ s.summary = "A lightweight ruby client for Patient Study Calendar's RESTful HTTP API"
13
+ s.description = "A lightweight ruby client for Patient Study Calendar's RESTful HTTP API"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'faraday', '~> 0.7.0'
21
+ s.add_dependency 'builder', '>= 2.1.2'
22
+ s.add_dependency 'faraday-stack', '~> 0.1.1'
23
+ s.add_dependency 'nokogiri', '~> 1.4'
24
+ s.add_dependency 'activesupport', '>= 2.3' # for the JSON adapter
25
+
26
+ s.add_development_dependency 'rspec', '~> 2.6'
27
+ s.add_development_dependency 'ci_reporter', '~> 1.6'
28
+ s.add_development_dependency 'cucumber', '~> 0.10.2'
29
+ s.add_development_dependency 'childprocess', '~> 0.1'
30
+ s.add_development_dependency 'highline'
31
+ s.add_development_dependency 'webmock', '~> 1.6'
32
+ s.add_development_dependency 'yard', '~> 0.6.8'
33
+ end
@@ -0,0 +1,37 @@
1
+ HTTP/1.1 200 OK
2
+ Date: Mon, 16 May 2011 21:47:14 GMT
3
+ Server: Restlet-Framework/2.0.3
4
+ Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
5
+ Pragma: no-cache
6
+ Accept-Ranges: bytes
7
+ Vary: Accept-Charset,Accept-Encoding,Accept-Language,Accept
8
+ Content-Type: application/json
9
+ Connection: close
10
+ Transfer-Encoding: chunked
11
+
12
+ {
13
+ "studies": [
14
+ {
15
+ "privileges": [
16
+ "develop",
17
+ "see-development",
18
+ "set-managing-sites",
19
+ "release",
20
+ "assign-identifiers",
21
+ "purge"
22
+ ],
23
+ "assigned_identifier": "NU 1404"
24
+ },
25
+ {
26
+ "privileges": [
27
+ "develop",
28
+ "see-development",
29
+ "set-managing-sites",
30
+ "release",
31
+ "assign-identifiers",
32
+ "purge"
33
+ ],
34
+ "assigned_identifier": "ECOG 5607"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,18 @@
1
+ shared_context 'middleware' do
2
+ let(:app) { mock('app') }
3
+ let(:env) { { :request_headers => ::Faraday::Utils::Headers.new } }
4
+ let(:headers) { env[:request_headers] }
5
+
6
+ before { app.stub!(:call) }
7
+
8
+ def do_call
9
+ subject.call(env)
10
+ end
11
+ end
12
+
13
+ shared_examples 'unconditional middleware' do
14
+ it 'continues the chain' do
15
+ app.should_receive(:call)
16
+ subject.call(env)
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ module Psc
4
+ describe Client do
5
+ let(:options) { { :authenticator => { :basic => %w(superuser superuser) } } }
6
+ let(:url) { 'http://psc.example.org/' }
7
+ let(:client) { Psc::Client.new(url, options) }
8
+
9
+ def mockable_uri(path)
10
+ URI.join(url.sub(%r{//}, '//superuser:superuser@'), "api/v1/#{path}").to_s
11
+ end
12
+
13
+ describe '.new' do
14
+ it 'creates a connection from a string and options' do
15
+ client.connection.should be_a(Psc::Connection)
16
+ end
17
+
18
+ it 'yields a connection builder if given a block' do
19
+ client = Psc::Client.new(url, options) do |builder|
20
+ builder.response :logger
21
+ end
22
+ client.connection.builder[-2].should == ::Faraday::Response::Logger
23
+ end
24
+ end
25
+
26
+ describe '#studies' do
27
+ before do
28
+ stub_request(:get, mockable_uri('studies.json')).
29
+ to_return(http_fixture('studies-json'))
30
+ end
31
+
32
+ let(:actual) { client.studies }
33
+
34
+ it "has the data from the response" do
35
+ actual.first['assigned_identifier'].should == 'NU 1404'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,156 @@
1
+ require File.expand_path("../../spec_helper.rb", __FILE__)
2
+
3
+ module Psc
4
+ describe Connection do
5
+ let(:conn) {
6
+ Psc::Connection.new('http://psc.example.org/', options)
7
+ }
8
+
9
+ let(:options) {
10
+ { :authenticator => { :basic => ['foo', 'bar'] } }
11
+ }
12
+
13
+ describe 'appending the API path' do
14
+ def actual_path_prefix(url)
15
+ Psc::Connection.new(url, options).path_prefix
16
+ end
17
+
18
+ it 'is appended correctly if the URL ends with a slash' do
19
+ actual_path_prefix('http://psc.example.org/').should == '/api/v1'
20
+ end
21
+
22
+ it 'is appended correctly if the URL does not end with a slash' do
23
+ actual_path_prefix('http://psc.example.org').should == '/api/v1'
24
+ end
25
+
26
+ it 'is appended correctly if the URL has path info that ends with a slash' do
27
+ actual_path_prefix('http://www.example.net/psc/').should == '/psc/api/v1'
28
+ end
29
+
30
+ it 'is appended correctly if the URL has path info that does not end with a slash' do
31
+ actual_path_prefix('http://www.example.net/psc').should == '/psc/api/v1'
32
+ end
33
+
34
+ it 'is not appended if it is already there and it ends with a slash' do
35
+ actual_path_prefix('http://www.example.net/api/v1').should == '/api/v1'
36
+ end
37
+
38
+ it 'is not appended if it is already there and it does not end with a slash' do
39
+ actual_path_prefix('http://www.example.net/api/v1/').should == '/api/v1'
40
+ end
41
+ end
42
+
43
+ DEFAULT_MIDDLEWARE_COUNT = 6
44
+
45
+ describe 'the default middleware' do
46
+ it 'has the authentication middleware first' do
47
+ conn.builder[0].should == Psc::Faraday::HttpBasic
48
+ end
49
+
50
+ it 'has the string-is-xml middleware next' do
51
+ conn.builder[1].should == Psc::Faraday::StringIsXml
52
+ end
53
+
54
+ it 'has the Faraday JSON request middleware next' do
55
+ conn.builder[2].should == ::Faraday::Request::JSON
56
+ end
57
+
58
+ it 'has the Faraday URL-encoded request middleware next' do
59
+ conn.builder[3].should == ::Faraday::Request::UrlEncoded
60
+ end
61
+
62
+ it 'has the FaradayStack XML parser middleware next' do
63
+ conn.builder[4].should == ::FaradayStack::ResponseXML
64
+ end
65
+
66
+ it 'has the FaradayStack JSON parser middleware next' do
67
+ conn.builder[5].should == ::FaradayStack::ResponseJSON
68
+ end
69
+ end
70
+
71
+ describe 'user-specified additional middleware' do
72
+ let(:custom_conn) {
73
+ Psc::Connection.new('http://psc.example.org/', options) do |builder|
74
+ builder.use ::Faraday::Response::Logger
75
+ end
76
+ }
77
+
78
+ it 'comes after the default middleware' do
79
+ custom_conn.builder[DEFAULT_MIDDLEWARE_COUNT].should == ::Faraday::Response::Logger
80
+ end
81
+
82
+ it 'comes before the adapter' do
83
+ custom_conn.builder.handlers.last.should == ::Faraday::Adapter::NetHttp
84
+ end
85
+ end
86
+
87
+ describe 'selecting authentication middleware' do
88
+ let(:auth_middleware) { conn.builder[0] } # this is a Faraday::Builder::Handler
89
+ let(:auth_mw_args) { conn.builder[0].instance_eval { @args } }
90
+
91
+ context 'when :basic' do
92
+ before do
93
+ options[:authenticator] = { :basic => %w(foo bar) }
94
+ end
95
+
96
+ it 'uses the HttpBasic middleware' do
97
+ auth_middleware.klass.should == Psc::Faraday::HttpBasic
98
+ end
99
+
100
+ it 'provides the username and password to the middleware' do
101
+ auth_mw_args.should == %w(foo bar)
102
+ end
103
+ end
104
+
105
+ context 'when :token' do
106
+ before do
107
+ options[:authenticator] = { :token => lambda { 'foo' } }
108
+ end
109
+
110
+ it 'uses the PscToken middleware' do
111
+ auth_middleware.klass.should == Psc::Faraday::PscToken
112
+ end
113
+
114
+ it 'provides the token (or creator) to the middleware' do
115
+ auth_mw_args.first.call.should == 'foo'
116
+ end
117
+ end
118
+
119
+ context 'when an unknown kind' do
120
+ before do
121
+ options[:authenticator] = { :quuxor => 7 }
122
+ end
123
+
124
+ it 'raises an error' do
125
+ lambda { conn }.should raise_error('Unsupported authentication method :quuxor.')
126
+ end
127
+ end
128
+
129
+ context 'when :authenticator not specified' do
130
+ before do
131
+ options.delete :authenticator
132
+ end
133
+
134
+ it 'raises an error' do
135
+ lambda { conn }.should raise_error(
136
+ 'No authentication method specified. Please include the :authenticator option.')
137
+ end
138
+ end
139
+ end
140
+
141
+ describe 'selecting an adapter' do
142
+ let(:adapter) { conn.builder.handlers.last }
143
+
144
+ it 'defaults to net/http' do
145
+ adapter.klass.should == ::Faraday::Adapter::NetHttp
146
+ end
147
+
148
+ it 'uses the one the user provides if any' do
149
+ custom_conn = Connection.new('dc', options) do |builder|
150
+ builder.adapter :excon
151
+ end
152
+ custom_conn.builder.handlers.last.should == ::Faraday::Adapter::Excon
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe HttpBasic do
5
+ include_context "middleware"
6
+ it_behaves_like "unconditional middleware"
7
+
8
+ subject { HttpBasic.new(app, 'jo', 'basil') }
9
+
10
+ it 'adds the appropriate Authorization header' do
11
+ do_call
12
+ headers['Authorization'].should == 'Basic am86YmFzaWw='
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe PscToken do
5
+ include_context "middleware"
6
+
7
+ describe "with a static token" do
8
+ it_behaves_like "unconditional middleware"
9
+
10
+ subject { PscToken.new(app, 'jo-9') }
11
+
12
+ it 'adds the appropriate Authorization header' do
13
+ do_call
14
+ headers['Authorization'].should == 'psc_token jo-9'
15
+ end
16
+ end
17
+
18
+ describe "with a dynamic token" do
19
+ it_behaves_like "unconditional middleware"
20
+
21
+ subject do
22
+ i = 7
23
+ PscToken.new(app, lambda { i += 1; i ** 2 })
24
+ end
25
+
26
+ it 'adds the appropriate Authorization header' do
27
+ do_call
28
+ headers['Authorization'].should == 'psc_token 64'
29
+ end
30
+
31
+ it 'invokes the lambda for each call' do
32
+ do_call
33
+ do_call
34
+ headers['Authorization'].should == 'psc_token 81'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path('../../../spec_helper', __FILE__)
2
+
3
+ module Psc::Faraday
4
+ describe StringIsXml do
5
+ include_context 'middleware'
6
+
7
+ subject { StringIsXml.new(app) }
8
+
9
+ before do
10
+ env[:body] = '<foo/>'
11
+ end
12
+
13
+ it 'treats a string body as text/xml' do
14
+ do_call
15
+ headers['Content-Type'].should == 'text/xml'
16
+ end
17
+
18
+ it 'leaves a Hash body alone' do
19
+ env[:body] = { 'foo' => 'bar' }
20
+ do_call
21
+ headers['Content-Type'].should be_nil
22
+ end
23
+
24
+ it 'does nothing if the content type is already set' do
25
+ headers['Content-Type'] = 'application/vnd.sun.wadl+xml'
26
+ do_call
27
+ headers['Content-Type'].should == 'application/vnd.sun.wadl+xml'
28
+ end
29
+
30
+ context 'in the same stack as Faraday::Request::JSON' do
31
+ let(:conn) do
32
+ ::Faraday::Connection.new('http://example.org/') do |builder|
33
+ builder.use StringIsXml
34
+ builder.use ::Faraday::Request::JSON
35
+ end
36
+ end
37
+
38
+ it 'sets the content type for a string body' do
39
+ response = conn.put('dc', '<foo/>')
40
+ response.env[:request_headers]['Content-Type'].should == 'text/xml'
41
+ end
42
+
43
+ it 'does not interfere with the JSON middleware for a Hash body' do
44
+ response = conn.put('dc', { 'bar' => 'baz' })
45
+ response.env[:request_headers]['Content-Type'].should == 'application/json'
46
+ end
47
+ end
48
+ end
49
+ end