pupa 0.0.13 → 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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/README.md +17 -0
  4. data/lib/pupa.rb +5 -8
  5. data/lib/pupa/errors.rb +4 -0
  6. data/lib/pupa/models/model.rb +5 -5
  7. data/lib/pupa/models/organization.rb +10 -6
  8. data/lib/pupa/models/person.rb +10 -6
  9. data/lib/pupa/processor.rb +14 -15
  10. data/lib/pupa/processor/connection.rb +26 -0
  11. data/lib/pupa/processor/connection_adapters/mongodb_adapter.rb +92 -0
  12. data/lib/pupa/processor/connection_adapters/postgresql_adapter.rb +116 -0
  13. data/lib/pupa/processor/document_store.rb +3 -0
  14. data/lib/pupa/processor/middleware/raise_error.rb +1 -0
  15. data/lib/pupa/refinements/faraday_middleware.rb +1 -1
  16. data/lib/pupa/runner.rb +14 -21
  17. data/lib/pupa/version.rb +1 -1
  18. data/pupa.gemspec +3 -2
  19. data/schemas/popolo/contact_detail.json +10 -0
  20. data/schemas/popolo/membership.json +29 -9
  21. data/schemas/popolo/organization.json +9 -2
  22. data/schemas/popolo/other_name.json +24 -0
  23. data/schemas/popolo/person.json +6 -3
  24. data/schemas/popolo/post.json +16 -2
  25. data/spec/models/model_spec.rb +1 -1
  26. data/spec/processor/connection_adapters/mongodb_adapter_spec.rb +61 -0
  27. data/spec/processor/connection_adapters/postgresql_adapter_spec.rb +70 -0
  28. data/spec/processor/connection_spec.rb +15 -0
  29. data/spec/processor/middleware/parse_json_spec.rb +90 -0
  30. data/spec/processor_spec.rb +9 -10
  31. data/spec/spec_helper.rb +0 -10
  32. metadata +83 -75
  33. data/lib/pupa/processor/persistence.rb +0 -85
  34. data/spec/cassettes/31ac91ccad069eefc07d96cfbe66fa66c1b41fcf.yml +0 -56
  35. data/spec/cassettes/4ff54d737afb5d693653752d7bf234a405a80172.yml +0 -48
  36. data/spec/cassettes/898049a22e6ca51dfa2510d9e0e0207a5c396524.yml +0 -54
  37. data/spec/cassettes/ce69ff734ce852d2bfaa482bbf55d7ffb4762e87.yml +0 -26
  38. data/spec/cassettes/da629b01e0836deda8a5540a4e6a08783dd7aef9.yml +0 -46
  39. data/spec/cassettes/e398f35bea86b3d4c87a6934bae1eb7fca8744f9.yml +0 -26
  40. data/spec/cassettes/f861172f1df3bdb2052af5451f9922699d574b77.yml +0 -62
  41. 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