pmp 0.1.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.
@@ -0,0 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'faraday_middleware'
4
+ require 'active_support'
5
+
6
+ module PMP
7
+ module Connection
8
+
9
+ ALLOWED_CONNECTION_OPTIONS = [
10
+ :headers,
11
+ :url,
12
+ :params,
13
+ :request,
14
+ :adapter,
15
+ :ssl,
16
+ :oauth_token,
17
+ :debug
18
+ ].freeze
19
+
20
+ def connection(options={})
21
+ opts = process_options(options)
22
+ Faraday::Connection.new(opts) do |faraday|
23
+ faraday.request :authorization, 'Bearer', opts[:oauth_token] unless opts[:oauth_token].nil?
24
+ faraday.request :url_encoded
25
+ faraday.request :multipart
26
+
27
+ faraday.response :mashify
28
+ faraday.response :logger if opts[:debug]
29
+ faraday.response :json
30
+
31
+ faraday.adapter opts[:adapter]
32
+ end
33
+ end
34
+
35
+ def process_options(opts={})
36
+ headers = opts.delete(:headers) || {}
37
+ options = {
38
+ :headers => {
39
+ # generic http headers
40
+ 'User-Agent' => opts[:user_agent],
41
+ 'Accept' => "application/vnd.pmp.collection.doc+json",
42
+ 'Content-Type' => "application/vnd.pmp.collection.doc+json"
43
+ },
44
+ :ssl => {:verify => false},
45
+ :url => opts[:endpoint]
46
+ }.merge(opts)
47
+ options[:headers] = options[:headers].merge(headers)
48
+
49
+ # clean out any that don't belong
50
+ options.select{|k,v| ALLOWED_CONNECTION_OPTIONS.include?(k.to_sym)}
51
+ end
52
+
53
+ end
54
+ end
data/lib/pmp/link.rb ADDED
@@ -0,0 +1,64 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'uri_template'
4
+ require 'ostruct'
5
+
6
+ # # it's a struct, we don't have to set these, up, but have a list of what we define particular logic for
7
+ # :href, # "https://api-sandbox.pmp.io/docs/af676335-21df-4486-ab43-e88c1b48f026"
8
+ # :href_template, # "https://api-sandbox.pmp.io/users{?limit,offset,tag,collection,text,searchsort,has}"
9
+ # :href_vars, # { "collection": "https://github.com/publicmediaplatform/pmpdocs/wiki/Content-Retrieval" }
10
+ # :hreflang, # Language of the linked document
11
+ # :hints, # Hints about interacting with the link, such as HTTP methods, e.g. "hints": { "allow": ["GET", "PUT", "DELETE"] }
12
+ # :rels, # [ "urn:pmp:query:users" ]
13
+ # :method, # http method - get, post, put, etc.
14
+ # :type, # 'image/png' - mime type of linked resource
15
+ # :title, # name/title of thing linked in
16
+ # :operation, # used by permissions link - read, write
17
+ # :blacklist, # used by permissions link
18
+
19
+ module PMP
20
+ class Link < OpenStruct
21
+
22
+ include Parser
23
+
24
+ attr_accessor :parent
25
+
26
+ attr_accessor :params
27
+
28
+ def initialize(parent, link)
29
+ super()
30
+ self.parent = parent || PMP::CollectionDocument.new
31
+ self.params = link.delete('params') || {}
32
+ parse_attributes(link)
33
+ end
34
+
35
+ def attributes
36
+ HashWithIndifferentAccess.new(marshal_dump)
37
+ end
38
+
39
+ def where(params={})
40
+ self.class.new(parent, attributes.merge({'params'=>params}))
41
+ end
42
+
43
+ def as_json
44
+ extract_attributes
45
+ end
46
+
47
+ def url
48
+ URITemplate.new(href_template || href).expand(params)
49
+ end
50
+
51
+ def retrieve
52
+ @_retrieved ||= parent.request((method || 'get').to_sym, url)
53
+ end
54
+
55
+ def method_missing(method, *args)
56
+ begin
57
+ super
58
+ rescue NoMethodError => err
59
+ self.retrieve.send(method, *args)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
data/lib/pmp/links.rb ADDED
@@ -0,0 +1,21 @@
1
+ # class to handle/manage and keep track of list of links in this doc
2
+
3
+ module PMP
4
+ class Links < HashWithIndifferentAccess
5
+
6
+ include Utils
7
+
8
+ attr_accessor :parent
9
+
10
+ def initialize(parent)
11
+ super()
12
+ self.parent = parent
13
+ end
14
+
15
+ def []=(k, link)
16
+ super
17
+ parent.send("#{to_ruby_safe_name(k)}=", link)
18
+ end
19
+
20
+ end
21
+ end
data/lib/pmp/parser.rb ADDED
@@ -0,0 +1,102 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module PMP
4
+ module Parser
5
+
6
+ include Utils
7
+
8
+ def as_json(options={})
9
+ result = {}
10
+ result['version'] = self.version
11
+ result['links'] = extract_links
12
+ result['attributes'] = extract_attributes
13
+
14
+ # more elegant?
15
+ result['attributes'].delete('items')
16
+
17
+ result
18
+ end
19
+
20
+ def extract_attributes(obj=self)
21
+ obj.attributes.inject({}) do |result, pair|
22
+ value = pair.last
23
+ name = to_json_key_name(pair.first)
24
+ result[name] = value
25
+ result
26
+ end
27
+ end
28
+
29
+ def extract_links(obj=self)
30
+ obj.links.inject({}) do |result, pair|
31
+ value = pair.last
32
+ name = to_json_key_name(pair.first)
33
+
34
+ links = if value.is_a?(PMP::Link)
35
+ [extract_attributes(value)]
36
+ elsif value.is_a?(Array)
37
+ value.map{|v| extract_attributes(v)}
38
+ elsif value.is_a?(Hash)
39
+ value.values.map{|v| extract_attributes(v)}
40
+ end
41
+
42
+ result[name] = links
43
+
44
+ result
45
+ end
46
+ end
47
+
48
+ def parse(doc)
49
+ return if (!doc)
50
+ parse_version(doc['version'])
51
+ parse_attributes(doc['attributes'])
52
+ parse_links(doc['links'])
53
+ parse_items(doc['items'])
54
+ # parse_error(doc)
55
+ end
56
+
57
+ def parse_version(document)
58
+ self.version = document || '1.0'
59
+ end
60
+
61
+ def parse_attributes(document)
62
+ Array(document).each do |k,v|
63
+ self.send("#{to_ruby_safe_name(k)}=", v)
64
+ end
65
+ end
66
+
67
+ def parse_links(document)
68
+ Array(document).each do |k,v|
69
+ link = parse_link(k,v)
70
+ if link
71
+ self.links[k] = link
72
+ end
73
+ end
74
+ end
75
+
76
+ def parse_link(name, info)
77
+
78
+ return parse_query_links(info) if (name.to_s == 'query')
79
+
80
+ if !info.is_a?(Array)
81
+ Link.new(self, info)
82
+ elsif info.size == 1
83
+ Link.new(self, info.first)
84
+ elsif info.size > 0
85
+ v.map{|l| Link.new(self, l)}
86
+ end
87
+ end
88
+
89
+ def parse_query_links(links)
90
+ links.inject({}) do |results, query|
91
+ rel = query['rels'].first
92
+ results[rel] = Link.new(self, query)
93
+ results
94
+ end
95
+ end
96
+
97
+ def parse_items(document)
98
+ self.items = Array(document).collect{|i| PMP::CollectionDocument.new(document:i)}
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module PMP
4
+ class Response
5
+ attr_accessor :raw, :request
6
+
7
+ def initialize(raw, request)
8
+ @raw = raw
9
+ @request = request
10
+
11
+ check_for_error(raw)
12
+ end
13
+
14
+ def check_for_error(response)
15
+ status_code_type = response.status.to_s[0]
16
+ case status_code_type
17
+ when "2"
18
+ # puts "all is well, status: #{response.status}"
19
+ when "4", "5"
20
+ raise "Whoops, error back from PMP: #{response.status}"
21
+ else
22
+ raise "Unrecongized status code: #{response.status}"
23
+ end
24
+ end
25
+
26
+ def body
27
+ self.raw.body
28
+ end
29
+
30
+ end
31
+ end
data/lib/pmp/token.rb ADDED
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'oauth2'
4
+
5
+ module PMP
6
+ class Token
7
+
8
+ include Configuration
9
+
10
+ def initialize(options={}, &block)
11
+ apply_configuration(options)
12
+
13
+ yield(self) if block_given?
14
+ end
15
+
16
+ def token_url
17
+ options['token_url'] || '/auth/access_token'
18
+ end
19
+
20
+ def get_token
21
+ oauth_options = {
22
+ site: endpoint,
23
+ token_url: token_url,
24
+ connection_opts: connection_options(options)
25
+ }
26
+
27
+ client = OAuth2::Client.new(client_id, client_secret, oauth_options) do |faraday|
28
+ faraday.request :url_encoded
29
+ faraday.response :logger if debug
30
+ faraday.adapter adapter
31
+ end
32
+
33
+ client.client_credentials.get_token
34
+ end
35
+
36
+ def connection_options(opts={})
37
+ headers = opts.delete(:headers) || {}
38
+ options = {
39
+ :headers => {
40
+ 'User-Agent' => opts[:user_agent],
41
+ 'Accept' => 'application/json',
42
+ 'Content-Type' => 'application/x-www-form-urlencoded'
43
+ },
44
+ :ssl => {:verify => false},
45
+ :url => opts[:endpoint]
46
+ }.merge(opts)
47
+ options[:headers] = options[:headers].merge(headers)
48
+
49
+ options
50
+ end
51
+
52
+ end
53
+ end
data/lib/pmp/utils.rb ADDED
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module PMP
4
+ module Utils
5
+
6
+ def to_ruby_safe_name(name)
7
+ safe = name.to_s.strip
8
+ safe = safe.gsub(/[^\w_!?=]+/, '_').sub(/^[0-9!?=]/, '')
9
+ safe[0..-2].gsub(/[!?=]+/, '_') + safe[-1]
10
+ end
11
+
12
+ def to_json_key_name(name)
13
+ name.to_s.gsub(/_/, '-')
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module PMP
4
+ VERSION = "0.1.0"
5
+ end
data/pmp.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pmp/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'pmp'
8
+ gem.version = PMP::VERSION
9
+ gem.authors = ['Andrew Kuklewicz']
10
+ gem.email = ['andrew@prx.org']
11
+ gem.description = %q{Public Media Platform Ruby Gem}
12
+ gem.summary = %q{Public Media Platform Ruby Gem}
13
+ gem.homepage = 'https://github.com/PRX/pmp'
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ['lib']
20
+
21
+ gem.add_development_dependency('bundler', '~> 1.3')
22
+ gem.add_development_dependency('rake')
23
+ gem.add_development_dependency('rake')
24
+ gem.add_development_dependency('minitest')
25
+ gem.add_development_dependency('webmock')
26
+ gem.add_development_dependency('pry')
27
+ gem.add_development_dependency('guard')
28
+ gem.add_development_dependency('guard-bundler')
29
+ gem.add_development_dependency('guard-minitest')
30
+ gem.add_development_dependency('simplecov')
31
+ gem.add_development_dependency('simplecov-gem-adapter')
32
+
33
+ gem.add_runtime_dependency('faraday')
34
+ gem.add_runtime_dependency('faraday_middleware')
35
+ gem.add_runtime_dependency('oauth2')
36
+ gem.add_runtime_dependency('multi_json')
37
+ gem.add_runtime_dependency('excon')
38
+ gem.add_runtime_dependency('hashie')
39
+ gem.add_runtime_dependency('activesupport')
40
+ gem.add_runtime_dependency('uri_template')
41
+
42
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ describe PMP::Client do
6
+
7
+ before(:each) {
8
+ @pmp = PMP::Client.new
9
+ }
10
+
11
+ it "returns a root api object" do
12
+ @pmp = PMP::Client.new
13
+ @pmp.root.wont_be_nil
14
+ @pmp.root.wont_be :loaded?
15
+ @pmp.root.href.must_equal "https://api.pmp.io/"
16
+ end
17
+
18
+ it "root doc can be loaded" do
19
+
20
+ root_doc = json_file(:collection_root)
21
+
22
+ stub_request(:get, "https://api.pmp.io/").
23
+ with(:headers => {'Accept'=>'application/vnd.pmp.collection.doc+json', 'Content-Type'=>'application/vnd.pmp.collection.doc+json', 'Host'=>'api.pmp.io:443'}).
24
+ to_return(:status => 200, :body => root_doc, :headers => {})
25
+
26
+ @root = @pmp.root
27
+ @root.creator.must_be_instance_of PMP::Link
28
+ end
29
+
30
+ end
@@ -0,0 +1,163 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ require 'pmp/collection_document'
6
+ require 'webmock/minitest'
7
+
8
+ describe PMP::CollectionDocument do
9
+
10
+ describe 'make' do
11
+
12
+ before(:each) {
13
+ @doc = PMP::CollectionDocument.new
14
+ }
15
+
16
+ it "can create a new, blank collection doc" do
17
+ @doc.wont_be_nil
18
+ @doc.must_be_instance_of PMP::CollectionDocument
19
+ end
20
+
21
+ it "can get options from configuration" do
22
+ @doc.options.wont_be_nil
23
+ @doc.options[:adapter].must_equal :excon
24
+ end
25
+
26
+ it "should default href to endpoint" do
27
+ @doc.href.must_equal "https://api.pmp.io/"
28
+ end
29
+ end
30
+
31
+ describe 'make from response' do
32
+
33
+ it "can create from document" do
34
+ response = mashify({
35
+ version: '1.0'
36
+ })
37
+
38
+ doc = PMP::CollectionDocument.new(document: response)
39
+ doc.version.must_equal '1.0'
40
+ end
41
+
42
+ it "can create from remote result" do
43
+ raw = Minitest::Mock.new
44
+ raw.expect(:status, 200)
45
+ raw.expect(:body, mashify({ version: '1.0' }))
46
+ response = PMP::Response.new(raw, {})
47
+
48
+ doc = PMP::CollectionDocument.new(response: response)
49
+ doc.version.must_equal '1.0'
50
+ doc.must_be :loaded
51
+ end
52
+
53
+ end
54
+
55
+ describe "parse from doc" do
56
+
57
+ before(:each) {
58
+ @doc = PMP::CollectionDocument.new(document: json_fixture(:collection_basic))
59
+ }
60
+
61
+ it "should assign attributes" do
62
+ @doc.guid.must_equal "f84e9018-5c21-4b32-93f8-d519308620f0"
63
+ @doc.valid.from.must_equal "2013-05-10T15:17:00.598Z"
64
+ end
65
+
66
+ it "should set-up links" do
67
+ @doc.profile.must_be_instance_of PMP::Link
68
+ @doc.profile.href.must_equal "http://api-sandbox.pmp.io/profiles/story"
69
+ @doc.self.href.must_equal "http://api-sandbox.pmp.io/docs/f84e9018-5c21-4b32-93f8-d519308620f0"
70
+ @doc.collection.href.must_equal "http://api-sandbox.pmp.io/docs/"
71
+ end
72
+
73
+ it "should serialize to collection.doc+json" do
74
+ @doc = PMP::CollectionDocument.new(document: json_fixture(:collection_basic))
75
+ @doc.to_json.must_equal '{"version":"1.0","links":{"profile":[{"href":"http://api-sandbox.pmp.io/profiles/story"}],"self":[{"href":"http://api-sandbox.pmp.io/docs/f84e9018-5c21-4b32-93f8-d519308620f0"}],"collection":[{"href":"http://api-sandbox.pmp.io/docs/"}]},"attributes":{"guid":"f84e9018-5c21-4b32-93f8-d519308620f0","title":"Peers Find Less Pressure Borrowing From Each Other","published":"2013-05-10T15:17:00.598Z","valid":{"from":"2013-05-10T15:17:00.598Z","to":"2213-05-10T15:17:00.598Z"},"byline":"by SOME PERSON","hreflang":"en","description":"","contentencoded":"...","contenttemplated":"..."}}'
76
+ end
77
+
78
+ it "provides list of attributes (not links)" do
79
+ @doc.attributes.keys.must_be :include?, 'title'
80
+ @doc.attributes.keys.wont_be :include?, 'self'
81
+ end
82
+
83
+ end
84
+
85
+ describe "loading" do
86
+
87
+ before(:each) {
88
+ root_doc = json_file(:collection_root)
89
+
90
+ stub_request(:get, "https://api.pmp.io/").
91
+ with(:headers => {'Accept'=>'application/vnd.pmp.collection.doc+json', 'Authorization'=>'Bearer thisisatesttoken', 'Content-Type'=>'application/vnd.pmp.collection.doc+json', 'Host'=>'api.pmp.io:443'}).
92
+ to_return(:status => 200, :body => root_doc, :headers => {})
93
+
94
+ @doc = PMP::CollectionDocument.new(oauth_token: 'thisisatesttoken')
95
+ }
96
+
97
+ it "should use oauth token" do
98
+ @doc.oauth_token.must_equal 'thisisatesttoken'
99
+ end
100
+
101
+ it "should load" do
102
+ @doc.wont_be :loaded
103
+ @doc.load
104
+ @doc.must_be :loaded
105
+ end
106
+
107
+ it "should not load lazily for assignment" do
108
+ @doc.foo = 'bar'
109
+ @doc.wont_be :loaded
110
+ end
111
+
112
+ it "should load lazily when attr not found" do
113
+ @doc.wont_be :loaded
114
+ creator = @doc.creator
115
+ @doc.must_be :loaded
116
+ creator.must_be_instance_of PMP::Link
117
+ end
118
+
119
+ it "should provide a list of links" do
120
+ @doc.load
121
+ @doc.links.keys.sort.must_equal ["creator", "edit", "navigation", "query"]
122
+ @doc.links['creator'].must_be_instance_of PMP::Link
123
+ end
124
+
125
+ end
126
+
127
+ describe "queries" do
128
+
129
+ before(:each) {
130
+ root_doc = json_file(:collection_root)
131
+
132
+ stub_request(:get, "https://api.pmp.io/").
133
+ with(:headers => {'Accept'=>'application/vnd.pmp.collection.doc+json', 'Authorization'=>'Bearer thisisatesttoken', 'Content-Type'=>'application/vnd.pmp.collection.doc+json', 'Host'=>'api.pmp.io:443'}).
134
+ to_return(:status => 200, :body => root_doc, :headers => {})
135
+
136
+ @doc = PMP::CollectionDocument.new(oauth_token: 'thisisatesttoken')
137
+ }
138
+
139
+ it "should get the list of query links" do
140
+ queries = @doc.query
141
+ queries.must_be_instance_of Hash
142
+ end
143
+
144
+ it "should get a query by rels" do
145
+ queries = @doc.query
146
+ queries["urn:pmp:query:docs"].must_be_instance_of PMP::Link
147
+ end
148
+
149
+ end
150
+
151
+ describe 'persistence' do
152
+
153
+ it "can set missing guid" do
154
+
155
+ end
156
+
157
+ it "can save a new record" do
158
+
159
+ end
160
+
161
+ end
162
+
163
+ end