pupa 0.0.13 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +17 -0
- data/lib/pupa.rb +5 -8
- data/lib/pupa/errors.rb +4 -0
- data/lib/pupa/models/model.rb +5 -5
- data/lib/pupa/models/organization.rb +10 -6
- data/lib/pupa/models/person.rb +10 -6
- data/lib/pupa/processor.rb +14 -15
- data/lib/pupa/processor/connection.rb +26 -0
- data/lib/pupa/processor/connection_adapters/mongodb_adapter.rb +92 -0
- data/lib/pupa/processor/connection_adapters/postgresql_adapter.rb +116 -0
- data/lib/pupa/processor/document_store.rb +3 -0
- data/lib/pupa/processor/middleware/raise_error.rb +1 -0
- data/lib/pupa/refinements/faraday_middleware.rb +1 -1
- data/lib/pupa/runner.rb +14 -21
- data/lib/pupa/version.rb +1 -1
- data/pupa.gemspec +3 -2
- data/schemas/popolo/contact_detail.json +10 -0
- data/schemas/popolo/membership.json +29 -9
- data/schemas/popolo/organization.json +9 -2
- data/schemas/popolo/other_name.json +24 -0
- data/schemas/popolo/person.json +6 -3
- data/schemas/popolo/post.json +16 -2
- data/spec/models/model_spec.rb +1 -1
- data/spec/processor/connection_adapters/mongodb_adapter_spec.rb +61 -0
- data/spec/processor/connection_adapters/postgresql_adapter_spec.rb +70 -0
- data/spec/processor/connection_spec.rb +15 -0
- data/spec/processor/middleware/parse_json_spec.rb +90 -0
- data/spec/processor_spec.rb +9 -10
- data/spec/spec_helper.rb +0 -10
- metadata +83 -75
- data/lib/pupa/processor/persistence.rb +0 -85
- data/spec/cassettes/31ac91ccad069eefc07d96cfbe66fa66c1b41fcf.yml +0 -56
- data/spec/cassettes/4ff54d737afb5d693653752d7bf234a405a80172.yml +0 -48
- data/spec/cassettes/898049a22e6ca51dfa2510d9e0e0207a5c396524.yml +0 -54
- data/spec/cassettes/ce69ff734ce852d2bfaa482bbf55d7ffb4762e87.yml +0 -26
- data/spec/cassettes/da629b01e0836deda8a5540a4e6a08783dd7aef9.yml +0 -46
- data/spec/cassettes/e398f35bea86b3d4c87a6934bae1eb7fca8744f9.yml +0 -26
- data/spec/cassettes/f861172f1df3bdb2052af5451f9922699d574b77.yml +0 -62
- data/spec/processor/persistence_spec.rb +0 -51
@@ -1,85 +0,0 @@
|
|
1
|
-
module Pupa
|
2
|
-
class Processor
|
3
|
-
# A proxy class to save plain old Ruby objects to MongoDB.
|
4
|
-
class Persistence
|
5
|
-
# @param [Object] object an object
|
6
|
-
def initialize(object)
|
7
|
-
@object = object
|
8
|
-
end
|
9
|
-
|
10
|
-
# Finds a document matching the selection criteria.
|
11
|
-
#
|
12
|
-
# The selection criteria *must* set a `_type` key in order to determine
|
13
|
-
# the collection to query.
|
14
|
-
#
|
15
|
-
# @param [Hash] selector the selection criteria
|
16
|
-
# @return [Hash,nil] the matched document, or nil
|
17
|
-
# @raises [Pupa::Errors::TooManyMatches] if multiple documents are found
|
18
|
-
def self.find(selector)
|
19
|
-
collection_name = collection_name_from_class_name(selector[:_type].camelize)
|
20
|
-
query = Pupa.session[collection_name].find(selector)
|
21
|
-
case query.count
|
22
|
-
when 0
|
23
|
-
nil
|
24
|
-
when 1
|
25
|
-
query.first
|
26
|
-
else
|
27
|
-
raise Errors::TooManyMatches, "selector matches multiple documents during find: #{collection_name} #{JSON.dump(selector)}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Inserts or replaces a document in MongoDB.
|
32
|
-
#
|
33
|
-
# @return [Array] whether the object was inserted and the object's database ID
|
34
|
-
# @raises [Pupa::Errors::TooManyMatches] if multiple documents would be updated
|
35
|
-
def save
|
36
|
-
selector = @object.fingerprint
|
37
|
-
query = collection.find(selector)
|
38
|
-
|
39
|
-
# Run query before callbacks to avoid e.g. timestamps in the selector.
|
40
|
-
case query.count
|
41
|
-
when 0
|
42
|
-
@object.run_callbacks(:save) do
|
43
|
-
@object.run_callbacks(:create) do
|
44
|
-
collection.insert(@object.to_h(persist: true))
|
45
|
-
[true, @object._id.to_s]
|
46
|
-
end
|
47
|
-
end
|
48
|
-
when 1
|
49
|
-
# Make the document available to the callbacks.
|
50
|
-
# @see https://github.com/opennorth/pupa-ruby/issues/17
|
51
|
-
@object.document = query.first
|
52
|
-
@object.run_callbacks(:save) do
|
53
|
-
query.update(@object.to_h(persist: true).except(:_id))
|
54
|
-
[false, @object.document['_id'].to_s]
|
55
|
-
end
|
56
|
-
else
|
57
|
-
raise Errors::TooManyMatches, "selector matches multiple documents during save: #{collection_name} #{JSON.dump(selector)} for #{@object._id}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
# Returns the name of the collection in which to save the object.
|
64
|
-
#
|
65
|
-
# @return [String] the name of the object's class
|
66
|
-
def self.collection_name_from_class_name(class_name)
|
67
|
-
class_name.demodulize.underscore.pluralize.to_sym
|
68
|
-
end
|
69
|
-
|
70
|
-
# Returns the name of the collection in which to save the object.
|
71
|
-
#
|
72
|
-
# @return [String] the name of the collection in which to save the object
|
73
|
-
def collection_name
|
74
|
-
self.class.collection_name_from_class_name(@object.class.to_s)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns the collection in which to save the object.
|
78
|
-
#
|
79
|
-
# @return [Moped::Collection] the collection in which to save the object
|
80
|
-
def collection
|
81
|
-
Pupa.session[collection_name]
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: post
|
5
|
-
uri: http://httpbin.org/post
|
6
|
-
body:
|
7
|
-
encoding: UTF-8
|
8
|
-
string: foo=bar
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
Content-Type:
|
13
|
-
- application/x-www-form-urlencoded
|
14
|
-
response:
|
15
|
-
status:
|
16
|
-
code: 200
|
17
|
-
message:
|
18
|
-
headers:
|
19
|
-
access-control-allow-origin:
|
20
|
-
- '*'
|
21
|
-
content-type:
|
22
|
-
- application/json
|
23
|
-
date:
|
24
|
-
- Sun, 15 Sep 2013 22:37:07 GMT
|
25
|
-
server:
|
26
|
-
- gunicorn/0.17.4
|
27
|
-
content-length:
|
28
|
-
- '439'
|
29
|
-
connection:
|
30
|
-
- Close
|
31
|
-
body:
|
32
|
-
encoding: UTF-8
|
33
|
-
string: |-
|
34
|
-
{
|
35
|
-
"args": {},
|
36
|
-
"data": "",
|
37
|
-
"json": null,
|
38
|
-
"origin": "70.49.74.123",
|
39
|
-
"files": {},
|
40
|
-
"form": {
|
41
|
-
"foo": "bar"
|
42
|
-
},
|
43
|
-
"headers": {
|
44
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
45
|
-
"User-Agent": "Faraday v0.8.8",
|
46
|
-
"Connection": "close",
|
47
|
-
"Host": "httpbin.org",
|
48
|
-
"Content-Length": "7",
|
49
|
-
"Accept": "*/*",
|
50
|
-
"Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
|
51
|
-
},
|
52
|
-
"url": "http://httpbin.org/post"
|
53
|
-
}
|
54
|
-
http_version:
|
55
|
-
recorded_at: Sun, 15 Sep 2013 22:37:07 GMT
|
56
|
-
recorded_with: VCR 2.5.0
|
@@ -1,48 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http://httpbin.org/get?foo=bar
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
response:
|
13
|
-
status:
|
14
|
-
code: 200
|
15
|
-
message:
|
16
|
-
headers:
|
17
|
-
access-control-allow-origin:
|
18
|
-
- '*'
|
19
|
-
content-type:
|
20
|
-
- application/json
|
21
|
-
date:
|
22
|
-
- Sun, 15 Sep 2013 22:37:07 GMT
|
23
|
-
server:
|
24
|
-
- gunicorn/0.17.4
|
25
|
-
content-length:
|
26
|
-
- '303'
|
27
|
-
connection:
|
28
|
-
- Close
|
29
|
-
body:
|
30
|
-
encoding: UTF-8
|
31
|
-
string: |-
|
32
|
-
{
|
33
|
-
"origin": "70.49.74.123",
|
34
|
-
"url": "http://httpbin.org/get?foo=bar",
|
35
|
-
"args": {
|
36
|
-
"foo": "bar"
|
37
|
-
},
|
38
|
-
"headers": {
|
39
|
-
"Accept": "*/*",
|
40
|
-
"Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
|
41
|
-
"Host": "httpbin.org",
|
42
|
-
"User-Agent": "Faraday v0.8.8",
|
43
|
-
"Connection": "close"
|
44
|
-
}
|
45
|
-
}
|
46
|
-
http_version:
|
47
|
-
recorded_at: Sun, 15 Sep 2013 22:37:07 GMT
|
48
|
-
recorded_with: VCR 2.5.0
|
@@ -1,54 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: post
|
5
|
-
uri: http://httpbin.org/post
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
Content-Type:
|
13
|
-
- application/x-www-form-urlencoded
|
14
|
-
response:
|
15
|
-
status:
|
16
|
-
code: 200
|
17
|
-
message:
|
18
|
-
headers:
|
19
|
-
access-control-allow-origin:
|
20
|
-
- '*'
|
21
|
-
content-type:
|
22
|
-
- application/json
|
23
|
-
date:
|
24
|
-
- Sun, 15 Sep 2013 22:37:07 GMT
|
25
|
-
server:
|
26
|
-
- gunicorn/0.17.4
|
27
|
-
content-length:
|
28
|
-
- '419'
|
29
|
-
connection:
|
30
|
-
- Close
|
31
|
-
body:
|
32
|
-
encoding: UTF-8
|
33
|
-
string: |-
|
34
|
-
{
|
35
|
-
"json": null,
|
36
|
-
"form": {},
|
37
|
-
"data": "",
|
38
|
-
"files": {},
|
39
|
-
"headers": {
|
40
|
-
"Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
|
41
|
-
"Content-Length": "0",
|
42
|
-
"User-Agent": "Faraday v0.8.8",
|
43
|
-
"Accept": "*/*",
|
44
|
-
"Connection": "close",
|
45
|
-
"Host": "httpbin.org",
|
46
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
47
|
-
},
|
48
|
-
"url": "http://httpbin.org/post",
|
49
|
-
"args": {},
|
50
|
-
"origin": "70.49.74.123"
|
51
|
-
}
|
52
|
-
http_version:
|
53
|
-
recorded_at: Sun, 15 Sep 2013 22:37:07 GMT
|
54
|
-
recorded_with: VCR 2.5.0
|
@@ -1,26 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: post
|
5
|
-
uri: http:/hello
|
6
|
-
body:
|
7
|
-
encoding: UTF-8
|
8
|
-
string: foo=bar
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
Accept:
|
13
|
-
- text/html
|
14
|
-
response:
|
15
|
-
status:
|
16
|
-
code: 200
|
17
|
-
message:
|
18
|
-
headers:
|
19
|
-
Content-Type:
|
20
|
-
- text/html
|
21
|
-
body:
|
22
|
-
encoding: UTF-8
|
23
|
-
string: hello
|
24
|
-
http_version:
|
25
|
-
recorded_at: Sun, 15 Sep 2013 22:37:06 GMT
|
26
|
-
recorded_with: VCR 2.5.0
|
@@ -1,46 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http://httpbin.org/get
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
response:
|
13
|
-
status:
|
14
|
-
code: 200
|
15
|
-
message:
|
16
|
-
headers:
|
17
|
-
access-control-allow-origin:
|
18
|
-
- '*'
|
19
|
-
content-type:
|
20
|
-
- application/json
|
21
|
-
date:
|
22
|
-
- Sun, 15 Sep 2013 22:37:07 GMT
|
23
|
-
server:
|
24
|
-
- gunicorn/0.17.4
|
25
|
-
content-length:
|
26
|
-
- '275'
|
27
|
-
connection:
|
28
|
-
- Close
|
29
|
-
body:
|
30
|
-
encoding: UTF-8
|
31
|
-
string: |-
|
32
|
-
{
|
33
|
-
"url": "http://httpbin.org/get",
|
34
|
-
"args": {},
|
35
|
-
"origin": "70.49.74.123",
|
36
|
-
"headers": {
|
37
|
-
"Accept": "*/*",
|
38
|
-
"User-Agent": "Faraday v0.8.8",
|
39
|
-
"Connection": "close",
|
40
|
-
"Host": "httpbin.org",
|
41
|
-
"Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
|
42
|
-
}
|
43
|
-
}
|
44
|
-
http_version:
|
45
|
-
recorded_at: Sun, 15 Sep 2013 22:37:07 GMT
|
46
|
-
recorded_with: VCR 2.5.0
|
@@ -1,26 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http:/hello
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
Accept:
|
13
|
-
- text/html
|
14
|
-
response:
|
15
|
-
status:
|
16
|
-
code: 200
|
17
|
-
message:
|
18
|
-
headers:
|
19
|
-
Content-Type:
|
20
|
-
- text/html
|
21
|
-
body:
|
22
|
-
encoding: UTF-8
|
23
|
-
string: hello
|
24
|
-
http_version:
|
25
|
-
recorded_at: Sun, 15 Sep 2013 22:37:06 GMT
|
26
|
-
recorded_with: VCR 2.5.0
|
@@ -1,62 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http://example.com/
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
response:
|
13
|
-
status:
|
14
|
-
code: 200
|
15
|
-
message:
|
16
|
-
headers:
|
17
|
-
accept-ranges:
|
18
|
-
- bytes
|
19
|
-
cache-control:
|
20
|
-
- max-age=604800
|
21
|
-
content-type:
|
22
|
-
- text/html
|
23
|
-
date:
|
24
|
-
- Fri, 27 Sep 2013 00:31:23 GMT
|
25
|
-
etag:
|
26
|
-
- '"3012602696"'
|
27
|
-
expires:
|
28
|
-
- Fri, 04 Oct 2013 00:31:23 GMT
|
29
|
-
last-modified:
|
30
|
-
- Fri, 09 Aug 2013 23:54:35 GMT
|
31
|
-
server:
|
32
|
-
- ECS (mdw/13C6)
|
33
|
-
x-cache:
|
34
|
-
- HIT
|
35
|
-
x-ec-custom-error:
|
36
|
-
- '1'
|
37
|
-
content-length:
|
38
|
-
- '1270'
|
39
|
-
connection:
|
40
|
-
- close
|
41
|
-
body:
|
42
|
-
encoding: UTF-8
|
43
|
-
string: "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n
|
44
|
-
\ <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html;
|
45
|
-
charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,
|
46
|
-
initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color:
|
47
|
-
#f0f0f2;\n margin: 0;\n padding: 0;\n font-family: \"Open
|
48
|
-
Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n
|
49
|
-
\ div {\n width: 600px;\n margin: 5em auto;\n padding:
|
50
|
-
50px;\n background-color: #fff;\n border-radius: 1em;\n }\n
|
51
|
-
\ a:link, a:visited {\n color: #38488f;\n text-decoration:
|
52
|
-
none;\n }\n @media (max-width: 700px) {\n body {\n background-color:
|
53
|
-
#fff;\n }\n div {\n width: auto;\n margin:
|
54
|
-
0 auto;\n border-radius: 0;\n padding: 1em;\n }\n
|
55
|
-
\ }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n
|
56
|
-
\ <p>This domain is established to be used for illustrative examples in
|
57
|
-
documents. You may use this\n domain in examples without prior coordination
|
58
|
-
or asking for permission.</p>\n <p><a href=\"http://www.iana.org/domains/example\">More
|
59
|
-
information...</a></p>\n</div>\n</body>\n</html>\n"
|
60
|
-
http_version:
|
61
|
-
recorded_at: Fri, 27 Sep 2013 00:31:23 GMT
|
62
|
-
recorded_with: VCR 2.5.0
|
@@ -1,51 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe Pupa::Processor::Persistence do
|
4
|
-
def _type
|
5
|
-
if testing_python_compatibility?
|
6
|
-
'person'
|
7
|
-
else
|
8
|
-
'pupa/person'
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
before :all do
|
13
|
-
Pupa.session = Moped::Session.new(['localhost:27017'], database: 'pupa_test')
|
14
|
-
Pupa.session.collections.each(&:drop)
|
15
|
-
|
16
|
-
Pupa::Processor::Persistence.new(Pupa::Person.new(_id: 'existing', name: 'existing', email: 'existing@example.com')).save
|
17
|
-
|
18
|
-
Pupa.session[:people].insert(_type: 'pupa/person', name: 'non-unique')
|
19
|
-
Pupa.session[:people].insert(_type: 'pupa/person', name: 'non-unique')
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '.find' do
|
23
|
-
it 'should return nil if no matches' do
|
24
|
-
Pupa::Processor::Persistence.find(_type: _type, name: 'nonexistent').should == nil
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'should return a document if one match' do
|
28
|
-
Pupa::Processor::Persistence.find(_type: _type, name: 'existing').should be_a(Hash)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should raise an error if many matches' do
|
32
|
-
expect{Pupa::Processor::Persistence.find(_type: 'pupa/person', name: 'non-unique')}.to raise_error(Pupa::Errors::TooManyMatches)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe '#save' do
|
37
|
-
it 'should insert a document if no matches' do
|
38
|
-
Pupa::Processor::Persistence.new(Pupa::Person.new(_id: 'new', name: 'new', email: 'new@example.com')).save.should == [true, 'new']
|
39
|
-
Pupa::Processor::Persistence.find(_type: _type, name: 'new')['email'].should == 'new@example.com'
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'should update a document if one match' do
|
43
|
-
Pupa::Processor::Persistence.new(Pupa::Person.new(_id: 'changed', name: 'existing', email: 'changed@example.com')).save.should == [false, 'existing']
|
44
|
-
Pupa::Processor::Persistence.find(_type: _type, name: 'existing')['email'].should == 'changed@example.com'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'should raise an error if many matches' do
|
48
|
-
expect{Pupa::Processor::Persistence.new(Pupa::Person.new(name: 'non-unique')).save}.to raise_error(Pupa::Errors::TooManyMatches)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|