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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/Guardfile +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +136 -0
- data/Rakefile +11 -0
- data/lib/pmp.rb +22 -0
- data/lib/pmp/client.rb +17 -0
- data/lib/pmp/collection_document.rb +126 -0
- data/lib/pmp/configuration.rb +90 -0
- data/lib/pmp/connection.rb +54 -0
- data/lib/pmp/link.rb +64 -0
- data/lib/pmp/links.rb +21 -0
- data/lib/pmp/parser.rb +102 -0
- data/lib/pmp/response.rb +31 -0
- data/lib/pmp/token.rb +53 -0
- data/lib/pmp/utils.rb +17 -0
- data/lib/pmp/version.rb +5 -0
- data/pmp.gemspec +42 -0
- data/spec/client_spec.rb +30 -0
- data/spec/collection_document_spec.rb +163 -0
- data/spec/configuration_spec.rb +53 -0
- data/spec/connection_spec.rb +35 -0
- data/spec/fixtures/collection_basic.json +38 -0
- data/spec/fixtures/collection_query.json +640 -0
- data/spec/fixtures/collection_root.json +178 -0
- data/spec/link_spec.rb +74 -0
- data/spec/links_spec.rb +31 -0
- data/spec/parser_spec.rb +49 -0
- data/spec/response_spec.rb +33 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/token_spec.rb +40 -0
- data/spec/utils_spec.rb +35 -0
- metadata +358 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 20ab2639c7b9fadf21969f73537adca2253ff409
|
4
|
+
data.tar.gz: 2bd71ffb8dcfc812eab677e8b951070475dfcfb3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6ade468f61ff12b0a71bb8479c4145a750f6680fec62dac610f64e763923afd2d81417a60e0b5e274c127113b4da0c1e786cc86c1fac9ec153ba79368229d472
|
7
|
+
data.tar.gz: 5be84ad2ec152d24da4a8d9968752a43b59380faf8e0392beb0cf747d6a09328ec5c8b27f8062fcbb3f3825af697c264f1789c1005fba12f829856a3978e16bc
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
7
|
+
watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
guard :minitest do
|
11
|
+
# with Minitest::Unit
|
12
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb})
|
13
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
14
|
+
watch(%r{^test/spec_helper\.rb}) { 'test' }
|
15
|
+
|
16
|
+
# with Minitest::Spec
|
17
|
+
watch(%r{^spec/(.*)_spec\.rb})
|
18
|
+
watch(%r{^lib/pmp/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
|
19
|
+
watch(%r{^spec/spec_helper\.rb}) { 'spec' }
|
20
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 PRX, Andrew Kuklewicz
|
2
|
+
|
3
|
+
MIT License
|
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
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# PMP gem
|
2
|
+
|
3
|
+
Gem to make it easier to use the PMP API, which is a hypermedia API using the collection.doc+json format.
|
4
|
+
|
5
|
+
https://github.com/publicmediaplatform/pmpdocs/wiki
|
6
|
+
|
7
|
+
Very big hat tip to the hyperresource gem: https://github.com/gamache/hyperresource
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'pmp'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install pmp
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
You can go through the `PMP::Client` as a convenience or start with a `PMP::CollectionDocument`
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
|
30
|
+
# so you need a few things, like the endpoint, default is "https://api.pmp.io"
|
31
|
+
endpoint = "https://api-sandbox.pmp.io"
|
32
|
+
|
33
|
+
# and you need credentials, the gem doesn't help you create these (yet)
|
34
|
+
client_id = "thisisnota-real-client-id-soverysorry"
|
35
|
+
client_secret = "thisisnotarealsecreteither"
|
36
|
+
|
37
|
+
# construct the client, setting some config in there
|
38
|
+
# this will automatically get a token as there is not one set
|
39
|
+
pmp = PMP::Client.new(client_id: client_id, client_secret: client_secret, endpoint: endpoint)
|
40
|
+
|
41
|
+
# or if you have a token already
|
42
|
+
token = 'thisisnotatoken'
|
43
|
+
pmp = PMP::Client.new(oauth_token: token, endpoint: endpoint)
|
44
|
+
|
45
|
+
# or if you want to get a token
|
46
|
+
pmp = PMP::Client.new(client_id: client_id, client_secret: client_secret, endpoint: endpoint)
|
47
|
+
oauth_token = pmp.token
|
48
|
+
|
49
|
+
# get the token string out of the token response
|
50
|
+
puts oauth_token.token
|
51
|
+
> 'thisisnotatoken'
|
52
|
+
|
53
|
+
# so let's get the root doc - an PMP::CollectionDocument instance
|
54
|
+
root = pmp.root
|
55
|
+
|
56
|
+
# or we can get it without the client, since PMP::CollectionDocument defaults to root
|
57
|
+
root = PMP::CollectionDocument.new(oauth_token: token, endpoint: endpoint)
|
58
|
+
|
59
|
+
# wanna get an attribute, act like it is a ruby attribute
|
60
|
+
puts root.guid
|
61
|
+
> '04224975-e93c-4b17-9df9-96db37d318f3'
|
62
|
+
|
63
|
+
# want to get the links, you can get a list of them by the rels
|
64
|
+
puts root.links.keys.sort
|
65
|
+
> ["creator", "edit", "navigation", "query"]
|
66
|
+
|
67
|
+
# want to get the creator link?
|
68
|
+
puts root.links["creator"]
|
69
|
+
> #<PMP::Link href="https://api-sandbox.pmp.io/docs/af676335-21df-4486-ab43-e88c1b48f026">
|
70
|
+
|
71
|
+
# get the same thing as a method
|
72
|
+
puts root.creator
|
73
|
+
> #<PMP::Link href="https://api-sandbox.pmp.io/docs/af676335-21df-4486-ab43-e88c1b48f026">
|
74
|
+
|
75
|
+
# like the root doc itself, this is lazy loaded
|
76
|
+
# but ask for an attribute on there, and you'll get the doc loaded up
|
77
|
+
puts root.creator.guid
|
78
|
+
|
79
|
+
#### http get request to link href occurs, loads info about the creator
|
80
|
+
> 'af676335-21df-4486-ab43-e88c1b48f026'
|
81
|
+
|
82
|
+
```
|
83
|
+
|
84
|
+
## Saving and Deleting
|
85
|
+
|
86
|
+
Once you have a doc, you can save or delete it like so:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
|
90
|
+
# create a new blank doc, will generatr a guid automatically if not present
|
91
|
+
doc = PMP::CollectionDocument.new()
|
92
|
+
doc.title = "this is an example, ok?"
|
93
|
+
doc.save
|
94
|
+
|
95
|
+
# get that guid!
|
96
|
+
guid = doc.guid
|
97
|
+
|
98
|
+
# how about the link to self
|
99
|
+
self_href = doc.self.href
|
100
|
+
|
101
|
+
# get a new doc using self, could just have used the link, this is a bad example perhaps
|
102
|
+
doc = PMP::CollectionDocument.new(href: self_href)
|
103
|
+
|
104
|
+
# update an existing attribute
|
105
|
+
doc.title = "this is another awesome example, cool?"
|
106
|
+
|
107
|
+
# can add an attribute (doesn't check schema yet)
|
108
|
+
doc.adding_an_attribute = "this will get saved as a new attribute adding-an-attribute"
|
109
|
+
|
110
|
+
# can add links (doesn't check schema yet)
|
111
|
+
doc.links['some-new-link'] = PMP::Link.new(doc, {href:'http://somenewlink.io'})
|
112
|
+
new_link = doc.some_new_link
|
113
|
+
|
114
|
+
# save changes
|
115
|
+
doc.save
|
116
|
+
|
117
|
+
# never mind, delete it
|
118
|
+
doc.delete
|
119
|
+
|
120
|
+
|
121
|
+
```
|
122
|
+
|
123
|
+
## To Do
|
124
|
+
|
125
|
+
Think about integrating this lovely json schema parsing project: https://github.com/google/autoparse
|
126
|
+
|
127
|
+
or this one: https://github.com/hoxworth/json-schema
|
128
|
+
|
129
|
+
|
130
|
+
## Contributing
|
131
|
+
|
132
|
+
1. Fork it
|
133
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
134
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
135
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
136
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/pmp.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support/all'
|
5
|
+
|
6
|
+
require 'pmp/version'
|
7
|
+
|
8
|
+
require 'pmp/utils'
|
9
|
+
require 'pmp/configuration'
|
10
|
+
require 'pmp/connection'
|
11
|
+
require 'pmp/response'
|
12
|
+
require 'pmp/parser'
|
13
|
+
|
14
|
+
require 'pmp/links'
|
15
|
+
require 'pmp/link'
|
16
|
+
require 'pmp/collection_document'
|
17
|
+
|
18
|
+
require 'pmp/token'
|
19
|
+
require 'pmp/client'
|
20
|
+
|
21
|
+
module PMP
|
22
|
+
end
|
data/lib/pmp/client.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module PMP
|
4
|
+
|
5
|
+
# Using OpenStruct for now - perhaps use ActiveModel? hmm...
|
6
|
+
class CollectionDocument < OpenStruct
|
7
|
+
|
8
|
+
include Configuration
|
9
|
+
include Connection
|
10
|
+
include Parser
|
11
|
+
|
12
|
+
# the href/url string to retrieve info for this resource
|
13
|
+
attr_accessor :href
|
14
|
+
|
15
|
+
# keep a ref to response obj if this resulted from one
|
16
|
+
# should this be private?
|
17
|
+
attr_accessor :response
|
18
|
+
|
19
|
+
# keep a ref to original doc from which this obj was created
|
20
|
+
# should this be private?
|
21
|
+
attr_accessor :original
|
22
|
+
|
23
|
+
# all collection docs have a version
|
24
|
+
# default is '1.0'
|
25
|
+
attr_accessor :version
|
26
|
+
|
27
|
+
# has this resource actually been loaded from remote url or json document?
|
28
|
+
attr_accessor :loaded
|
29
|
+
|
30
|
+
# private var to save links obj, to handle link additions
|
31
|
+
attr_accessor :links
|
32
|
+
|
33
|
+
# document is the original json derived doc used to create this resource
|
34
|
+
# assumption is that doc is a parsed json doc confirming to collection.doc+json
|
35
|
+
# TODO: check if this is a json string or hash, for now assume it has been mashified
|
36
|
+
def initialize(options={}, &block)
|
37
|
+
super()
|
38
|
+
|
39
|
+
self.links = PMP::Links.new(self)
|
40
|
+
|
41
|
+
self.href = options.delete(:href)
|
42
|
+
|
43
|
+
# if there is a doc to be had, pull it out
|
44
|
+
self.response = options.delete(:response)
|
45
|
+
self.original = options.delete(:document)
|
46
|
+
|
47
|
+
apply_configuration(options)
|
48
|
+
|
49
|
+
if !loaded? && !href
|
50
|
+
self.href = endpoint
|
51
|
+
end
|
52
|
+
|
53
|
+
yield(self) if block_given?
|
54
|
+
end
|
55
|
+
|
56
|
+
def attributes
|
57
|
+
HashWithIndifferentAccess.new(marshal_dump.delete_if{|k,v| links.keys.include?(k.to_s)})
|
58
|
+
end
|
59
|
+
|
60
|
+
def response=(resp)
|
61
|
+
unless (!resp || loaded?)
|
62
|
+
@response = resp
|
63
|
+
self.original = resp.body
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def original=(doc)
|
68
|
+
unless (!doc || loaded?)
|
69
|
+
@original = doc
|
70
|
+
parse(@original)
|
71
|
+
self.loaded = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def load
|
76
|
+
if !loaded?
|
77
|
+
self.response = request(:get, self.href || self.self.url)
|
78
|
+
self.loaded = true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
alias_method :get, :load
|
82
|
+
|
83
|
+
def save
|
84
|
+
save_link = self.edit
|
85
|
+
raise 'Edit link does not specify saving via put' unless (save_link && save_link.hints.allow.include?('PUT'))
|
86
|
+
set_guid_if_blank
|
87
|
+
url = save_link.where(guid: self.guid).url
|
88
|
+
request(:put, url, self)
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete
|
92
|
+
delete_link = self.edit
|
93
|
+
raise 'Edit link does not specify deleting' unless (delete_link && delete_link.hints.allow.include?('DELETE'))
|
94
|
+
raise 'No guid specified to delete' if self.guid.blank?
|
95
|
+
|
96
|
+
url = delete_link.where(guid: self.guid).url
|
97
|
+
request(:put, url, self)
|
98
|
+
end
|
99
|
+
|
100
|
+
def loaded?
|
101
|
+
!!self.loaded
|
102
|
+
end
|
103
|
+
|
104
|
+
# url includes any params - full url
|
105
|
+
def request(method, url, body=nil) # :nodoc:
|
106
|
+
raw = connection(options.merge({url: url})).send(method) do |request|
|
107
|
+
if [:post, :put].include?(method.to_sym) && !body.blank?
|
108
|
+
request.body = body.is_a?(String) ? body : body.to_json
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# may not need this, but remember how we made this response
|
113
|
+
PMP::Response.new(raw, {method: method, url: url, body: body})
|
114
|
+
end
|
115
|
+
|
116
|
+
def set_guid_if_blank
|
117
|
+
self.guid = SecureRandom.uuid if guid.blank?
|
118
|
+
end
|
119
|
+
|
120
|
+
def method_missing(method, *args)
|
121
|
+
load if (method.to_s.last != '=') && !loaded?
|
122
|
+
super
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module PMP
|
6
|
+
module Configuration
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
VALID_OPTIONS_KEYS = [
|
11
|
+
:client_id,
|
12
|
+
:client_secret,
|
13
|
+
:oauth_token,
|
14
|
+
:adapter,
|
15
|
+
:endpoint,
|
16
|
+
:user_agent,
|
17
|
+
:debug
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
# this you need to get from pmp, not covered by this
|
21
|
+
DEFAULT_CLIENT_ID = nil
|
22
|
+
DEFAULT_CLIENT_SECRET = nil
|
23
|
+
|
24
|
+
# Adapters are whatever Faraday supports - I like excon alot, so I'm defaulting it
|
25
|
+
DEFAULT_ADAPTER = :excon
|
26
|
+
|
27
|
+
# The api endpoint for YQL
|
28
|
+
DEFAULT_ENDPOINT = 'https://api.pmp.io/'.freeze
|
29
|
+
|
30
|
+
# The value sent in the http header for 'User-Agent' if none is set
|
31
|
+
DEFAULT_USER_AGENT = "PMP Ruby Gem #{PMP::VERSION}".freeze
|
32
|
+
|
33
|
+
# debug is defaulted to the ENV['DEBUG'], see below
|
34
|
+
|
35
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
36
|
+
|
37
|
+
included do
|
38
|
+
|
39
|
+
attr_accessor :options
|
40
|
+
|
41
|
+
VALID_OPTIONS_KEYS.each do |key|
|
42
|
+
define_method "#{key}=" do |arg|
|
43
|
+
self.instance_variable_set("@#{key}", arg)
|
44
|
+
self.options.merge!({:"#{key}" => arg})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def apply_configuration(opts={})
|
51
|
+
reset! unless @options
|
52
|
+
self.options = options.merge(opts)
|
53
|
+
VALID_OPTIONS_KEYS.each do |key|
|
54
|
+
send("#{key}=", options[key])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Convenience method to allow for global setting of configuration options
|
59
|
+
def configure
|
60
|
+
yield self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Reset configuration options to their defaults
|
64
|
+
def reset!
|
65
|
+
@options = {}
|
66
|
+
self.client_id = DEFAULT_CLIENT_ID
|
67
|
+
self.client_secret = DEFAULT_CLIENT_SECRET
|
68
|
+
self.adapter = DEFAULT_ADAPTER
|
69
|
+
self.endpoint = DEFAULT_ENDPOINT
|
70
|
+
self.user_agent = DEFAULT_USER_AGENT
|
71
|
+
self.debug = ENV['DEBUG']
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def options
|
76
|
+
options = {}
|
77
|
+
VALID_OPTIONS_KEYS.each { |k| options[k] = send(k) }
|
78
|
+
options
|
79
|
+
end
|
80
|
+
|
81
|
+
module ClassMethods
|
82
|
+
|
83
|
+
def keys
|
84
|
+
VALID_OPTIONS_KEYS
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|