couchproxy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/LICENSE +19 -0
  2. data/README +36 -0
  3. data/Rakefile +42 -0
  4. data/bin/couchproxy +88 -0
  5. data/conf/couchproxy.yml +21 -0
  6. data/lib/couchproxy/cluster.rb +43 -0
  7. data/lib/couchproxy/collator.rb +60 -0
  8. data/lib/couchproxy/deferrable_body.rb +15 -0
  9. data/lib/couchproxy/node.rb +25 -0
  10. data/lib/couchproxy/partition.rb +15 -0
  11. data/lib/couchproxy/rack/active_tasks.rb +9 -0
  12. data/lib/couchproxy/rack/all_databases.rb +23 -0
  13. data/lib/couchproxy/rack/all_docs.rb +9 -0
  14. data/lib/couchproxy/rack/base.rb +197 -0
  15. data/lib/couchproxy/rack/bulk_docs.rb +68 -0
  16. data/lib/couchproxy/rack/changes.rb +9 -0
  17. data/lib/couchproxy/rack/compact.rb +16 -0
  18. data/lib/couchproxy/rack/config.rb +16 -0
  19. data/lib/couchproxy/rack/database.rb +83 -0
  20. data/lib/couchproxy/rack/design_doc.rb +227 -0
  21. data/lib/couchproxy/rack/doc.rb +15 -0
  22. data/lib/couchproxy/rack/ensure_full_commit.rb +16 -0
  23. data/lib/couchproxy/rack/not_found.rb +13 -0
  24. data/lib/couchproxy/rack/replicate.rb +9 -0
  25. data/lib/couchproxy/rack/revs_limit.rb +18 -0
  26. data/lib/couchproxy/rack/root.rb +10 -0
  27. data/lib/couchproxy/rack/stats.rb +53 -0
  28. data/lib/couchproxy/rack/temp_view.rb +9 -0
  29. data/lib/couchproxy/rack/update.rb +11 -0
  30. data/lib/couchproxy/rack/users.rb +9 -0
  31. data/lib/couchproxy/rack/uuids.rb +9 -0
  32. data/lib/couchproxy/rack/view_cleanup.rb +16 -0
  33. data/lib/couchproxy/reducer.rb +57 -0
  34. data/lib/couchproxy/request.rb +50 -0
  35. data/lib/couchproxy/router.rb +62 -0
  36. data/lib/couchproxy.rb +48 -0
  37. data/lib/couchproxy.ru +22 -0
  38. data/test/collator_test.rb +100 -0
  39. metadata +164 -0
@@ -0,0 +1,62 @@
1
+ # encoding: UTF-8
2
+
3
+ module CouchProxy
4
+ class Router
5
+ DB_NAME = '[a-z]([a-z0-9_$()+-]|%2[489bBfF])*'.freeze
6
+ ROOT = ''.freeze
7
+ UUIDS = '_uuids'.freeze
8
+ USERS = '_users'.freeze
9
+ STATS = '_stats'.freeze
10
+ REPLICATE = '_replicate'.freeze
11
+ ALL_DBS = '_all_dbs'.freeze
12
+ ACTIVE_TASKS = '_active_tasks'.freeze
13
+ CONFIG = /^(_config|_config\/.*)$/
14
+ BULK_DOCS = /^#{DB_NAME}\/_bulk_docs$/
15
+ ALL_DOCS = /^#{DB_NAME}\/_all_docs$/
16
+ DESIGN_DOC = /^#{DB_NAME}\/_design\/.+$/
17
+ COMPACT = /^#{DB_NAME}\/(_compact|_compact\/.*)$/
18
+ VIEW_CLEANUP = /^#{DB_NAME}\/_view_cleanup$/
19
+ TEMP_VIEW = /^#{DB_NAME}\/_temp_view$/
20
+ UPDATE = /^#{DB_NAME}\/_update$/
21
+ REVS_LIMIT = /^#{DB_NAME}\/_revs_limit$/
22
+ CHANGES = /^#{DB_NAME}\/_changes$/
23
+ FULL_COMMIT = /^#{DB_NAME}\/_ensure_full_commit$/
24
+ DATABASE = /^#{DB_NAME}$/
25
+ DOC = /^#{DB_NAME}\/.+$/
26
+
27
+ def initialize(cluster)
28
+ @cluster = cluster
29
+ end
30
+
31
+ def call(env)
32
+ request = ::Rack::Request.new(env)
33
+ app = case env['REQUEST_PATH'][1..-1].chomp('/')
34
+ when ROOT then :root
35
+ when UUIDS then :uuids
36
+ when CONFIG then :config
37
+ when USERS then :users
38
+ when STATS then :stats
39
+ when REPLICATE then :replicate
40
+ when ALL_DBS then :all_databases
41
+ when ACTIVE_TASKS then :active_tasks
42
+ when BULK_DOCS then :bulk_docs
43
+ when ALL_DOCS then :all_docs
44
+ when DESIGN_DOC then :design_doc
45
+ when COMPACT then :compact
46
+ when VIEW_CLEANUP then :view_cleanup
47
+ when TEMP_VIEW then :temp_view
48
+ when UPDATE then :update
49
+ when REVS_LIMIT then :revs_limit
50
+ when CHANGES then :changes
51
+ when FULL_COMMIT then :ensure_full_commit
52
+ when DATABASE then :database
53
+ when DOC then :doc
54
+ else :not_found
55
+ end
56
+ name = app.to_s.split('_').map {|n| n.capitalize }.join
57
+ app = CouchProxy::Rack.const_get(name).new(request, @cluster)
58
+ app.send(request.request_method.downcase)
59
+ throw :async
60
+ end
61
+ end
62
+ end
data/lib/couchproxy.rb ADDED
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+
3
+ $:.unshift File.dirname(__FILE__) unless
4
+ $:.include?(File.dirname(__FILE__))
5
+
6
+ %w[
7
+ em-http
8
+ json
9
+ json/stream
10
+ thin
11
+ time
12
+ uri
13
+ yaml
14
+ zlib
15
+
16
+ couchproxy/collator
17
+ couchproxy/cluster
18
+ couchproxy/node
19
+ couchproxy/partition
20
+ couchproxy/deferrable_body
21
+ couchproxy/reducer
22
+ couchproxy/request
23
+ couchproxy/router
24
+
25
+ couchproxy/rack/base
26
+ couchproxy/rack/all_databases
27
+ couchproxy/rack/bulk_docs
28
+ couchproxy/rack/changes
29
+ couchproxy/rack/compact
30
+ couchproxy/rack/config
31
+ couchproxy/rack/database
32
+ couchproxy/rack/design_doc
33
+ couchproxy/rack/doc
34
+ couchproxy/rack/ensure_full_commit
35
+ couchproxy/rack/not_found
36
+ couchproxy/rack/replicate
37
+ couchproxy/rack/revs_limit
38
+ couchproxy/rack/root
39
+ couchproxy/rack/stats
40
+ couchproxy/rack/update
41
+ couchproxy/rack/users
42
+ couchproxy/rack/uuids
43
+ couchproxy/rack/view_cleanup
44
+ ].each {|f| require f }
45
+
46
+ module CouchProxy
47
+ VERSION = '0.1.0'
48
+ end
data/lib/couchproxy.ru ADDED
@@ -0,0 +1,22 @@
1
+ $:.unshift File.dirname(__FILE__) unless
2
+ $:.include?(File.dirname(__FILE__))
3
+
4
+ require 'couchproxy'
5
+
6
+ def cluster
7
+ yaml = ENV['COUCH_PROXY_CONFIG'] || ''
8
+ unless File.exist?(yaml)
9
+ raise ArgumentError.new('COUCH_PROXY_CONFIG must point to a couchproxy.yml file')
10
+ end
11
+ config = YAML.load_file(yaml)
12
+ raise ArgumentError.new('must define node list') unless config['nodes']
13
+ nodes = config['nodes'].map {|n| CouchProxy::Node.new(n['host'], n['partitions']) }
14
+ reducers = config['reducers'] || 4
15
+ couchjs = config['couchjs'] or raise ArgumentError.new('must define couchjs')
16
+ raise ArgumentError.new("#{couchjs} must be executable") unless File.executable?(couchjs)
17
+ mainjs = File.expand_path('../../share/couchdb/server/main.js', couchjs)
18
+ raise ArgumentError.new("could not find #{mainjs}") unless File.exist?(mainjs)
19
+ CouchProxy::Cluster.new(nodes, couchjs, reducers)
20
+ end
21
+
22
+ run CouchProxy::Router.new(cluster)
@@ -0,0 +1,100 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'couchproxy'
4
+ require 'test/unit'
5
+
6
+ # Test that we properly sort JSON keys according to
7
+ # http://wiki.apache.org/couchdb/View_collation.
8
+ class CollatorTest < Test::Unit::TestCase
9
+ def setup
10
+ @collator = CouchProxy::Collator.new
11
+ end
12
+
13
+ def test_keyword
14
+ assert_equal(0, @collator.compare(nil, nil))
15
+ assert_equal(0, @collator.compare(true, true))
16
+ assert_equal(0, @collator.compare(false, false))
17
+
18
+ assert_equal(-1, @collator.compare(nil, false))
19
+ assert_equal(1, @collator.compare(false, nil))
20
+
21
+ assert_equal(-1, @collator.compare(nil, true))
22
+ assert_equal(1, @collator.compare(true, nil))
23
+
24
+ assert_equal(-1, @collator.compare(false, true))
25
+ assert_equal(1, @collator.compare(true, false))
26
+ end
27
+
28
+ def test_number
29
+ assert_equal(0, @collator.compare(0, 0))
30
+ assert_equal(0, @collator.compare(0, 0.0))
31
+ assert_equal(0, @collator.compare(1, 1.0))
32
+ assert_equal(-1, @collator.compare(0, 1.0))
33
+ assert_equal(1, @collator.compare(1.0, 0))
34
+ end
35
+
36
+ def test_string
37
+ assert_equal(0, @collator.compare('', ''))
38
+ assert_equal(0, @collator.compare('a', 'a'))
39
+ assert_equal(-1, @collator.compare('a', 'aa'))
40
+ assert_equal(-1, @collator.compare('a', 'b'))
41
+ assert_equal(1, @collator.compare('b', 'a'))
42
+ end
43
+
44
+ def test_type
45
+ assert_equal(-1, @collator.compare(nil, 0))
46
+ assert_equal(-1, @collator.compare(nil, ''))
47
+ assert_equal(-1, @collator.compare(nil, []))
48
+ assert_equal(-1, @collator.compare(nil, {}))
49
+
50
+ assert_equal(-1, @collator.compare(true, 0))
51
+ assert_equal(-1, @collator.compare(true, ''))
52
+ assert_equal(-1, @collator.compare(true, []))
53
+ assert_equal(-1, @collator.compare(true, {}))
54
+
55
+ assert_equal(-1, @collator.compare(false, 0))
56
+ assert_equal(-1, @collator.compare(false, ''))
57
+ assert_equal(-1, @collator.compare(false, []))
58
+ assert_equal(-1, @collator.compare(false, {}))
59
+
60
+ assert_equal(-1, @collator.compare(0, ''))
61
+ assert_equal(-1, @collator.compare(0, []))
62
+ assert_equal(-1, @collator.compare(0, {}))
63
+
64
+ assert_equal(-1, @collator.compare('', []))
65
+ assert_equal(-1, @collator.compare('', {}))
66
+
67
+ assert_equal(-1, @collator.compare([], {}))
68
+ end
69
+
70
+ def test_array
71
+ assert_equal(0, @collator.compare([], []))
72
+ assert_equal(0, @collator.compare([0], [0]))
73
+ assert_equal(0, @collator.compare([0], [0.0]))
74
+
75
+ assert_equal(-1, @collator.compare([], [0]))
76
+ assert_equal(-1, @collator.compare([0], [0, 1]))
77
+ assert_equal(1, @collator.compare([0], []))
78
+ assert_equal(1, @collator.compare([0, 1], [0]))
79
+
80
+ assert_equal(1, @collator.compare([0], [-1, 0]))
81
+ assert_equal(-1, @collator.compare([-1, 0], [0]))
82
+
83
+ assert_equal(-1, @collator.compare([nil], [false]))
84
+ assert_equal(-1, @collator.compare([nil], [nil, false]))
85
+
86
+ assert_equal(0, @collator.compare([[]], [[]]))
87
+ assert_equal(-1, @collator.compare([[0]], [[1]]))
88
+ assert_equal(-1, @collator.compare([0], [[]]))
89
+ end
90
+
91
+ def test_hash
92
+ assert_equal(0, @collator.compare({}, {}))
93
+ assert_equal(0, @collator.compare({nil => nil}, {nil => nil}))
94
+ assert_equal(-1, @collator.compare({nil => nil}, {nil => true}))
95
+ assert_equal(1, @collator.compare({nil => true}, {nil => nil}))
96
+ assert_equal(-1, @collator.compare({'a' => 1}, {'a' => 2}))
97
+ assert_equal(-1, @collator.compare({'b' => 2}, {'b' => 2, 'a' => 1}))
98
+ assert_equal(-1, @collator.compare({'b' => 2, 'a' => 1}, {'b' => 2, 'c' => 2}))
99
+ end
100
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: couchproxy
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - David Graham
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-06 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: em-http-request
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 2
31
+ version: "0.2"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: json
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 1
44
+ - 4
45
+ version: "1.4"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: json-stream
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ - 1
59
+ version: "0.1"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: thin
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 1
72
+ - 2
73
+ version: "1.2"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ description: |-
77
+ CouchProxy is a simple proxy server that distributes reads and writes to a
78
+ cluster of Apache CouchDB servers so they appear to be a single huge database.
79
+ Documents are stored and retrieved from a particular CouchDB instance, using
80
+ consistent hashing of the document id. Map/reduce views are processed
81
+ concurrently on each CouchDB instance and merged together by the proxy before
82
+ returning the results to the client.
83
+ email: david.malcom.graham@gmail.com
84
+ executables:
85
+ - couchproxy
86
+ extensions: []
87
+
88
+ extra_rdoc_files: []
89
+
90
+ files:
91
+ - LICENSE
92
+ - Rakefile
93
+ - README
94
+ - bin/couchproxy
95
+ - lib/couchproxy/cluster.rb
96
+ - lib/couchproxy/collator.rb
97
+ - lib/couchproxy/deferrable_body.rb
98
+ - lib/couchproxy/node.rb
99
+ - lib/couchproxy/partition.rb
100
+ - lib/couchproxy/rack/active_tasks.rb
101
+ - lib/couchproxy/rack/all_databases.rb
102
+ - lib/couchproxy/rack/all_docs.rb
103
+ - lib/couchproxy/rack/base.rb
104
+ - lib/couchproxy/rack/bulk_docs.rb
105
+ - lib/couchproxy/rack/changes.rb
106
+ - lib/couchproxy/rack/compact.rb
107
+ - lib/couchproxy/rack/config.rb
108
+ - lib/couchproxy/rack/database.rb
109
+ - lib/couchproxy/rack/design_doc.rb
110
+ - lib/couchproxy/rack/doc.rb
111
+ - lib/couchproxy/rack/ensure_full_commit.rb
112
+ - lib/couchproxy/rack/not_found.rb
113
+ - lib/couchproxy/rack/replicate.rb
114
+ - lib/couchproxy/rack/revs_limit.rb
115
+ - lib/couchproxy/rack/root.rb
116
+ - lib/couchproxy/rack/stats.rb
117
+ - lib/couchproxy/rack/temp_view.rb
118
+ - lib/couchproxy/rack/update.rb
119
+ - lib/couchproxy/rack/users.rb
120
+ - lib/couchproxy/rack/uuids.rb
121
+ - lib/couchproxy/rack/view_cleanup.rb
122
+ - lib/couchproxy/reducer.rb
123
+ - lib/couchproxy/request.rb
124
+ - lib/couchproxy/router.rb
125
+ - lib/couchproxy.rb
126
+ - lib/couchproxy.ru
127
+ - conf/couchproxy.yml
128
+ - test/collator_test.rb
129
+ has_rdoc: true
130
+ homepage: http://github.com/dgraham/couchproxy
131
+ licenses: []
132
+
133
+ post_install_message:
134
+ rdoc_options: []
135
+
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ segments:
144
+ - 1
145
+ - 9
146
+ - 1
147
+ version: 1.9.1
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ segments:
154
+ - 0
155
+ version: "0"
156
+ requirements: []
157
+
158
+ rubyforge_project:
159
+ rubygems_version: 1.3.7
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: A proxy server for Apache CouchDB clusters.
163
+ test_files:
164
+ - test/collator_test.rb