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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p247
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pmp.gemspec
4
+ gemspec
5
+
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
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push 'lib'
6
+ t.libs.push 'test'
7
+ t.test_files = FileList['spec/**/*_spec.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :test
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,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module PMP
4
+ class Client
5
+
6
+ include Configuration
7
+
8
+ def token(opts={})
9
+ PMP::Token.new(opts).get_token
10
+ end
11
+
12
+ def root(opts={}, &block)
13
+ PMP::CollectionDocument.new(opts, &block)
14
+ end
15
+
16
+ end
17
+ end
@@ -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