jchris-couchrest 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'spec/rake/spectask'
5
5
 
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = "couchrest"
8
- s.version = "0.9.5"
8
+ s.version = "0.9.7"
9
9
  s.date = "2008-09-11"
10
10
  s.summary = "Lean and RESTful interface to CouchDB."
11
11
  s.email = "jchris@grabb.it"
@@ -36,7 +36,6 @@ namespace :github do # thanks merb!
36
36
  next if skip_fields.include?(name) || value.nil? || value == "" || (value.respond_to?(:empty?) && value.empty?)
37
37
  if name == "dependencies"
38
38
  value.each do |d|
39
- puts d.to_s
40
39
  dep, *ver = d.to_s.split(" ")
41
40
  result << " s.add_dependency #{dep.inspect}, [#{ /\(([^\,]*)/ . match(ver.join(" "))[1].inspect}]\n"
42
41
  end
data/bin/couchapp ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require File.expand_path(File.dirname(__FILE__)) + '/../lib/couchrest'
5
+
6
+ options = {
7
+ :loud => true,
8
+ }
9
+
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: #$0 [options] (push|pull|generate)"
12
+ opts.on('-q', '--quiet', "Omit extra debug info") do
13
+ options[:loud] = false
14
+ end
15
+ opts.on_tail('-h', '--help', "Display detailed help and exit") do
16
+ puts opts
17
+ exit
18
+ end
19
+ end
20
+
21
+ opts.parse!(ARGV)
22
+
23
+ case ARGV.shift
24
+ when /generate/
25
+ appname = ARGV.shift
26
+ current = Dir.getwd
27
+ appdir = File.join(current, appname)
28
+ puts "generating couchapp in #{appdir}"
29
+ CouchRest::FileManager.generate_app(appdir)
30
+
31
+ when /push/
32
+ dirname = ARGV.shift
33
+ if ARGV.length == 2
34
+ appname = ARGV.shift
35
+ dbstring = ARGV.shift
36
+ elsif ARGV.length == 1
37
+ appname = dirname
38
+ dbstring = ARGV.shift
39
+ else
40
+ puts opts
41
+ puts "push dirname [appname] database"
42
+ exit(0)
43
+ end
44
+ dbspec = CouchRest.parse(dbstring)
45
+ fm = CouchRest::FileManager.new(dbspec[:database], dbspec[:host])
46
+ fm.push_app(dirname, appname)
47
+ when /pull/
48
+
49
+ else
50
+ puts opts
51
+ puts "please specify a command"
52
+ end
@@ -47,10 +47,12 @@ module CouchRest
47
47
  return
48
48
  end
49
49
 
50
+ doc["signatures"] ||= {}
51
+ doc["_attachments"] ||= {}
50
52
  # remove deleted docs
51
53
  to_be_removed = doc["signatures"].keys.select do |d|
52
54
  !pushfiles.collect{|p| p.keys.first}.include?(d)
53
- end
55
+ end
54
56
 
55
57
  to_be_removed.each do |p|
56
58
  say "deleting #{p}"
@@ -69,7 +71,6 @@ module CouchRest
69
71
  doc["_attachments"][path].delete("length")
70
72
  doc["_attachments"][path]["data"] = @attachments[path]["data"]
71
73
  doc["_attachments"][path].merge!({"data" => @attachments[path]["data"]} )
72
-
73
74
  end
74
75
  end
75
76
 
@@ -185,9 +186,70 @@ module CouchRest
185
186
 
186
187
  end
187
188
 
189
+ def push_app(appdir, appname)
190
+ libs = []
191
+ viewdir = File.join(appdir,"views")
192
+ attachdir = File.join(appdir,"attachments")
193
+ views, lang = read_design_views(viewdir)
194
+ # attachments = read_attachments(attachdir)
195
+ docid = "_design/#{appname}"
196
+ design = @db.get(docid) rescue {}
197
+ design['_id'] = docid
198
+ design['views'] = views
199
+ design['language'] = lang
200
+ @db.save(design)
201
+ push_directory(attachdir, docid)
202
+ # puts views.inspect
203
+ end
204
+
205
+ # Generate an application in the given directory.
206
+ # This is a class method because it doesn't depend on
207
+ # specifying a database.
208
+ def self.generate_app(app_dir)
209
+ FileUtils.mkdir_p(app_dir)
210
+ FileUtils.mkdir_p(File.join(app_dir,"attachments"))
211
+ FileUtils.mkdir_p(File.join(app_dir,"views"))
212
+
213
+ index_template = File.join(File.expand_path(File.dirname(__FILE__)), 'templates','index.html')
214
+ index_dest = File.join(app_dir,"attachments","index.html")
215
+ FileUtils.cp(index_template, index_dest)
216
+
217
+ map_template = File.join(File.expand_path(File.dirname(__FILE__)), 'templates','example-map.js')
218
+ map_dest = File.join(app_dir,"views","example-map.js")
219
+ FileUtils.cp(map_template, map_dest)
220
+
221
+ rereduce_template = File.join(File.expand_path(File.dirname(__FILE__)), 'templates','example-reduce.js')
222
+ rereduce_dest = File.join(app_dir,"views","example-reduce.js")
223
+ FileUtils.cp(rereduce_template, rereduce_dest)
224
+ end
188
225
 
189
226
  private
190
227
 
228
+ def read_design_views(viewdir)
229
+ libs = []
230
+ language = nil
231
+ views = {}
232
+ Dir["#{viewdir}/*.*"].each do |viewfile|
233
+ view_parts = viewfile.split('/')
234
+ viewfile_name = view_parts.last
235
+ # example-map.js
236
+ viewfile_name_parts = viewfile_name.split('.')
237
+ viewfile_ext = viewfile_name_parts.last
238
+ view_name_parts = viewfile_name_parts.first.split('-')
239
+ func_type = view_name_parts.pop
240
+ view_name = view_name_parts.join('-')
241
+ contents = File.open(viewfile).read
242
+ if /^lib\..*$/.match viewfile_name
243
+ libs.push(contents)
244
+ else
245
+ views[view_name] ||= {}
246
+ language = LANGS[viewfile_ext]
247
+ views[view_name][func_type] = contents.sub(/(\/\/|#)include-lib/,libs.join("\n"))
248
+ end
249
+ end
250
+ [views, language]
251
+ end
252
+
191
253
  def say words
192
254
  puts words if @loud
193
255
  end
@@ -206,7 +268,7 @@ module CouchRest
206
268
  existing = @db.get(id) rescue nil
207
269
 
208
270
  if existing
209
- updated = fields.merge({"_id" => id, "_rev" => existing["_rev"]})
271
+ updated = existing.merge(fields)
210
272
  if existing != updated
211
273
  say "replacing #{id}"
212
274
  db.save(updated)
@@ -0,0 +1,8 @@
1
+ // an example map function, emits the doc id
2
+ // and the list of keys it contains
3
+
4
+ function(doc) {
5
+ var k, keys = []
6
+ for (k in doc) keys.push(k);
7
+ emit(doc._id, keys);
8
+ };
@@ -0,0 +1,10 @@
1
+ // example reduce function to count the
2
+ // number of rows in a given key range.
3
+
4
+ function(keys, value, rereduce) {
5
+ if (rereduce) {
6
+ return sum(values);
7
+ } else {
8
+ return values.length;
9
+ }
10
+ };
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Generated CouchApp</title>
5
+ <link rel="stylesheet" href="screen.css" type="text/css">
6
+ </head>
7
+ <body>
8
+ <h1>Generated CouchApp</h1>
9
+ <ul id="view"></ul>
10
+ </body>
11
+ <script src="/_utils/script/json2.js"></script>
12
+ <script src="/_utils/script/jquery.js?1.2.6"></script>
13
+ <script src="/_utils/script/jquery.couch.js?0.8.0"></script>
14
+ <script type="text/javascript" charset="utf-8">
15
+ $(function() {
16
+ var dbname = document.location.href.split('/')[3];
17
+ var design = unescape(document.location.href.split('/')[4]).split('/')[1];
18
+ var DB = $.couch.db(dbname);
19
+ DB.view(design+"/example-map",{success: function(json) {
20
+ $("#view").html(json.rows.map(function(row) {
21
+ return '<li>'+row.key+'</li>';
22
+ }).join(''));
23
+ }});
24
+ });
25
+ </script>
26
+ </html>
data/lib/couchrest.rb CHANGED
@@ -41,6 +41,37 @@ module CouchRest
41
41
  Server.new(*opts)
42
42
  end
43
43
 
44
+ def parse url
45
+ case url
46
+ when /^http:\/\/(.*)\/(.*)\/(.*)/
47
+ host = $1
48
+ db = $2
49
+ docid = $3
50
+ when /^http:\/\/(.*)\/(.*)/
51
+ host = $1
52
+ db = $2
53
+ when /^http:\/\/(.*)/
54
+ host = $1
55
+ when /(.*)\/(.*)\/(.*)/
56
+ host = $1
57
+ db = $2
58
+ docid = $3
59
+ when /(.*)\/(.*)/
60
+ host = $1
61
+ db = $2
62
+ else
63
+ db = url
64
+ end
65
+
66
+ db = nil if db && db.empty?
67
+
68
+ {
69
+ :host => host || "localhost:5984",
70
+ :database => db,
71
+ :doc => docid
72
+ }
73
+ end
74
+
44
75
  # ensure that a database exists
45
76
  # creates it if it isn't already there
46
77
  # returns it after it's been created
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "couchapp" do
4
+ before(:all) do
5
+ @fixdir = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp-test'
6
+ @couchapp = File.expand_path(File.dirname(__FILE__)) + '/../bin/couchapp'
7
+ `rm -rf #{@fixdir}`
8
+ `mkdir -p #{@fixdir}`
9
+ @run = "cd #{@fixdir} && #{@couchapp}"
10
+
11
+ end
12
+
13
+ describe "--help" do
14
+ it "should output the opts" do
15
+ `#{@run} --help`.should match(/Usage/)
16
+ end
17
+ end
18
+
19
+ describe "generate my-app" do
20
+ it "should create an app directory" do
21
+ `#{@run} generate my-app`.should match(/generating/i)
22
+ Dir["#{@fixdir}/*"].select{|x|x =~ /my-app/}.length.should == 1
23
+ end
24
+ it "should create a views directory" do
25
+ `#{@run} generate my-app`.should match(/generating/i)
26
+ Dir["#{@fixdir}/my-app/*"].select{|x|x =~ /views/}.length.should == 1
27
+ end
28
+ end
29
+
30
+ describe "push my-app #{TESTDB}" do
31
+ before(:all) do
32
+ @cr = CouchRest.new(COUCHHOST)
33
+ @db = @cr.database(TESTDB)
34
+ @db.delete! rescue nil
35
+ @db = @cr.create_db(TESTDB) rescue nil
36
+ `#{@run} generate my-app`
37
+ end
38
+ it "should create the design document with the app name" do
39
+ `#{@run} push my-app #{TESTDB}`
40
+ lambda{@db.get("_design/my-app")}.should_not raise_error
41
+ end
42
+ it "should create the views" do
43
+ `#{@run} push my-app #{TESTDB}`
44
+ doc = @db.get("_design/my-app")
45
+ doc['views']['example']['map'].should match(/function/)
46
+ end
47
+ it "should create the index" do
48
+ `#{@run} push my-app #{TESTDB}`
49
+ doc = @db.get("_design/my-app")
50
+ doc['_attachments']['index.html']["content_type"].should == 'text/html'
51
+ end
52
+ end
53
+
54
+ describe "push my-app my-design #{TESTDB}" do
55
+ before(:all) do
56
+ @cr = CouchRest.new(COUCHHOST)
57
+ @db = @cr.database(TESTDB)
58
+ @db.delete! rescue nil
59
+ @db = @cr.create_db(TESTDB) rescue nil
60
+ `#{@run} generate my-app`
61
+ end
62
+ it "should create the design document" do
63
+ `#{@run} push my-app my-design #{TESTDB}`
64
+ lambda{@db.get("_design/my-design")}.should_not raise_error
65
+ end
66
+ end
67
+ end
68
+
@@ -42,6 +42,99 @@ describe CouchRest do
42
42
  end
43
43
  end
44
44
 
45
+ describe "parsing urls" do
46
+ it "should parse just a dbname" do
47
+ db = CouchRest.parse "my-db"
48
+ db[:database].should == "my-db"
49
+ db[:host].should == "localhost:5984"
50
+ end
51
+ it "should parse a host and db" do
52
+ db = CouchRest.parse "localhost/my-db"
53
+ db[:database].should == "my-db"
54
+ db[:host].should == "localhost"
55
+ end
56
+ it "should parse a host and db with http" do
57
+ db = CouchRest.parse "http://localhost/my-db"
58
+ db[:database].should == "my-db"
59
+ db[:host].should == "localhost"
60
+ end
61
+ it "should parse a host with a port and db" do
62
+ db = CouchRest.parse "localhost:5555/my-db"
63
+ db[:database].should == "my-db"
64
+ db[:host].should == "localhost:5555"
65
+ end
66
+ it "should parse a host with a port and db with http" do
67
+ db = CouchRest.parse "http://localhost:5555/my-db"
68
+ db[:database].should == "my-db"
69
+ db[:host].should == "localhost:5555"
70
+ end
71
+ it "should parse just a host" do
72
+ db = CouchRest.parse "http://localhost:5555/"
73
+ db[:database].should be_nil
74
+ db[:host].should == "localhost:5555"
75
+ end
76
+ it "should parse just a host no slash" do
77
+ db = CouchRest.parse "http://localhost:5555"
78
+ db[:host].should == "localhost:5555"
79
+ db[:database].should be_nil
80
+ end
81
+ it "should get docid" do
82
+ db = CouchRest.parse "localhost:5555/my-db/my-doc"
83
+ db[:database].should == "my-db"
84
+ db[:host].should == "localhost:5555"
85
+ db[:doc].should == "my-doc"
86
+ end
87
+ it "should get docid with http" do
88
+ db = CouchRest.parse "http://localhost:5555/my-db/my-doc"
89
+ db[:database].should == "my-db"
90
+ db[:host].should == "localhost:5555"
91
+ db[:doc].should == "my-doc"
92
+ end
93
+
94
+ it "should parse a host and db" do
95
+ db = CouchRest.parse "127.0.0.1/my-db"
96
+ db[:database].should == "my-db"
97
+ db[:host].should == "127.0.0.1"
98
+ end
99
+ it "should parse a host and db with http" do
100
+ db = CouchRest.parse "http://127.0.0.1/my-db"
101
+ db[:database].should == "my-db"
102
+ db[:host].should == "127.0.0.1"
103
+ end
104
+ it "should parse a host with a port and db" do
105
+ db = CouchRest.parse "127.0.0.1:5555/my-db"
106
+ db[:database].should == "my-db"
107
+ db[:host].should == "127.0.0.1:5555"
108
+ end
109
+ it "should parse a host with a port and db with http" do
110
+ db = CouchRest.parse "http://127.0.0.1:5555/my-db"
111
+ db[:database].should == "my-db"
112
+ db[:host].should == "127.0.0.1:5555"
113
+ end
114
+ it "should parse just a host" do
115
+ db = CouchRest.parse "http://127.0.0.1:5555/"
116
+ db[:database].should be_nil
117
+ db[:host].should == "127.0.0.1:5555"
118
+ end
119
+ it "should parse just a host no slash" do
120
+ db = CouchRest.parse "http://127.0.0.1:5555"
121
+ db[:host].should == "127.0.0.1:5555"
122
+ db[:database].should be_nil
123
+ end
124
+ it "should get docid" do
125
+ db = CouchRest.parse "127.0.0.1:5555/my-db/my-doc"
126
+ db[:database].should == "my-db"
127
+ db[:host].should == "127.0.0.1:5555"
128
+ db[:doc].should == "my-doc"
129
+ end
130
+ it "should get docid with http" do
131
+ db = CouchRest.parse "http://127.0.0.1:5555/my-db/my-doc"
132
+ db[:database].should == "my-db"
133
+ db[:host].should == "127.0.0.1:5555"
134
+ db[:doc].should == "my-doc"
135
+ end
136
+ end
137
+
45
138
  describe "easy initializing a database adapter" do
46
139
  it "should be possible without an explicit CouchRest instantiation" do
47
140
  db = CouchRest.database "http://localhost:5984/couchrest-test"
@@ -24,6 +24,60 @@ describe CouchRest::FileManager do
24
24
  end
25
25
  end
26
26
 
27
+ describe CouchRest::FileManager, "generating an app" do
28
+ before(:all) do
29
+ @appdir = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp'
30
+ `rm -rf #{@appdir}`
31
+ `mkdir -p #{@appdir}`
32
+ CouchRest::FileManager.generate_app(@appdir)
33
+ end
34
+ it "should create an attachments directory" do
35
+ Dir["#{@appdir}/*"].select{|x|x =~ /attachments/}.length.should == 1
36
+ end
37
+ it "should create a views directory" do
38
+ Dir["#{@appdir}/*"].select{|x|x =~ /views/}.length.should == 1
39
+ end
40
+ it "should create index.html" do
41
+ html = File.open("#{@appdir}/attachments/index.html").read
42
+ html.should match(/DOCTYPE/)
43
+ end
44
+ it "should create an example view" do
45
+ map = File.open("#{@appdir}/views/example-map.js").read
46
+ map.should match(/function\(doc\)/)
47
+ reduce = File.open("#{@appdir}/views/example-reduce.js").read
48
+ reduce.should match(/rereduce/)
49
+ end
50
+ end
51
+
52
+ describe CouchRest::FileManager, "pushing an app" do
53
+ before(:all) do
54
+ @cr = CouchRest.new(COUCHHOST)
55
+ @db = @cr.database(TESTDB)
56
+ @db.delete! rescue nil
57
+ @db = @cr.create_db(TESTDB) rescue nil
58
+
59
+ @appdir = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp'
60
+ `rm -rf #{@appdir}`
61
+ `mkdir -p #{@appdir}`
62
+ CouchRest::FileManager.generate_app(@appdir)
63
+
64
+ @fm = CouchRest::FileManager.new(TESTDB, COUCHHOST)
65
+ r = @fm.push_app(@appdir, "couchapp")
66
+ end
67
+ it "should create a design document" do
68
+ lambda{@db.get("_design/couchapp")}.should_not raise_error
69
+ end
70
+ it "should create the views" do
71
+ doc = @db.get("_design/couchapp")
72
+ doc['views']['example']['map'].should match(/function/)
73
+ end
74
+ it "should create the index" do
75
+ doc = @db.get("_design/couchapp")
76
+ doc['_attachments']['index.html']["content_type"].should == 'text/html'
77
+ end
78
+ end
79
+
80
+
27
81
  describe CouchRest::FileManager, "pushing views" do
28
82
  before(:all) do
29
83
  @cr = CouchRest.new(COUCHHOST)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jchris-couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - J. Chris Anderson
@@ -46,6 +46,7 @@ files:
46
46
  - README.rdoc
47
47
  - Rakefile
48
48
  - THANKS
49
+ - bin/couchapp
49
50
  - bin/couchdir
50
51
  - bin/couchview
51
52
  - examples/word_count
@@ -73,14 +74,26 @@ files:
73
74
  - lib/couchrest/helper/file_manager.rb
74
75
  - lib/couchrest/helper/pager.rb
75
76
  - lib/couchrest/helper/streamer.rb
77
+ - lib/couchrest/helper/templates
78
+ - lib/couchrest/helper/templates/example-map.js
79
+ - lib/couchrest/helper/templates/example-reduce.js
80
+ - lib/couchrest/helper/templates/index.html
76
81
  - lib/couchrest/monkeypatches.rb
77
82
  - lib/couchrest.rb
83
+ - spec/couchapp_spec.rb
78
84
  - spec/couchrest_spec.rb
79
85
  - spec/database_spec.rb
80
86
  - spec/file_manager_spec.rb
81
87
  - spec/fixtures
82
88
  - spec/fixtures/attachments
83
89
  - spec/fixtures/attachments/test.html
90
+ - spec/fixtures/couchapp
91
+ - spec/fixtures/couchapp/attachments
92
+ - spec/fixtures/couchapp/attachments/index.html
93
+ - spec/fixtures/couchapp/views
94
+ - spec/fixtures/couchapp/views/example-map.js
95
+ - spec/fixtures/couchapp/views/example-reduce.js
96
+ - spec/fixtures/couchapp-test
84
97
  - spec/fixtures/views
85
98
  - spec/fixtures/views/lib.js
86
99
  - spec/fixtures/views/test_view