pupa 0.0.13 → 0.1.0

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