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