diff --git a/README.rdoc b/README.rdoc
index 0d5686f..a9a0255 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -31,7 +31,7 @@ CouchRest install, from the project root directory run `rake`, or `autotest`
Quick Start:
# with !, it creates the database if it doesn't already exist
- @db = CouchRest.database!("http://localhost:5984/couchrest-test")
+ @db = CouchRest.database!("http://127.0.0.1:5984/couchrest-test")
response = @db.save({:key => 'value', 'another key' => 'another value'})
doc = @db.get(response['id'])
puts doc.inspect
diff --git a/Rakefile b/Rakefile
index 6195eee..0df1425 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.name = "couchrest"
- s.version = "0.10.1"
+ s.version = "0.11.1"
s.date = "2008-11-22"
s.summary = "Lean and RESTful interface to CouchDB."
s.email = "jchris@grabb.it"
@@ -19,7 +19,6 @@ spec = Gem::Specification.new do |s|
s.extra_rdoc_files = %w( README.rdoc LICENSE THANKS )
s.require_path = "lib"
s.bindir = 'bin'
- s.executables << 'couchview'
s.executables << 'couchdir'
s.executables << 'couchapp'
s.add_dependency("json", ">= 1.1.2")
diff --git a/bin/couchview b/bin/couchview
deleted file mode 100755
index c87a741..0000000
--- a/bin/couchview
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'optparse'
-require 'couchrest'
-
-%w(generate push).each do |file|
- require File.dirname(__FILE__) + "/../lib/couchrest/commands/#{file}"
-end
-
-# Set defaults
-options = {
- :loud => true,
-}
-
-opts = OptionParser.new do |opts|
- opts.banner = "Usage: #$0 [options] (push|generate) directory database"
- opts.on('-q', '--quiet', "Omit extra debug info") do
- options[:loud] = false
- end
- opts.on_tail('-h', '--help [push|generate]', "Display detailed help and exit") do |help_command|
- puts opts
- case help_command
- when "push"
- puts CouchRest::Commands::Push.help
- when "generate"
- puts CouchRest::Commands::Generate.help
- end
- exit
- end
-end
-opts.parse!(ARGV)
-
-options[:command] = ARGV.shift
-options[:directory] = ARGV.shift
-options[:trailing_args] = ARGV
-
-# There must be a better way to check for extra required args
-unless (["push", "generate"].include?(options[:command]) && options[:directory] && options[:trailing_args])
- puts(opts)
- exit
-end
-
-case options[:command]
-when "push"
- CouchRest::Commands::Push.run(options)
-when "generate"
- CouchRest::Commands::Generate.run(options)
-end
diff --git a/couchrest.gemspec b/couchrest.gemspec
index 9a66fb6..b3da1c9 100644
--- a/couchrest.gemspec
+++ b/couchrest.gemspec
@@ -1,17 +1,16 @@
Gem::Specification.new do |s|
s.extra_rdoc_files = ["README.rdoc", "LICENSE", "THANKS"]
s.date = "Sat Nov 22 00:00:00 -0800 2008"
- s.executables = ["couchview", "couchdir", "couchapp"]
+ s.executables = ["couchdir", "couchapp"]
s.authors = ["J. Chris Anderson"]
s.required_rubygems_version = ">= 0"
- s.version = "0.10.1"
+ s.version = "0.11.1"
s.files = ["LICENSE",
"README.rdoc",
"Rakefile",
"THANKS",
"bin/couchapp",
"bin/couchdir",
- "bin/couchview",
"examples/model",
"examples/model/example.rb",
"examples/word_count",
@@ -43,11 +42,21 @@ Gem::Specification.new do |s|
"lib/couchrest/helper/file_manager.rb",
"lib/couchrest/helper/pager.rb",
"lib/couchrest/helper/streamer.rb",
- "lib/couchrest/helper/templates",
- "lib/couchrest/helper/templates/bar.txt",
- "lib/couchrest/helper/templates/example-map.js",
- "lib/couchrest/helper/templates/example-reduce.js",
- "lib/couchrest/helper/templates/index.html",
+ "lib/couchrest/helper/template-app",
+ "lib/couchrest/helper/template-app/_attachments",
+ "lib/couchrest/helper/template-app/_attachments/index.html",
+ "lib/couchrest/helper/template-app/foo",
+ "lib/couchrest/helper/template-app/foo/bar.txt",
+ "lib/couchrest/helper/template-app/forms",
+ "lib/couchrest/helper/template-app/forms/example-form.js",
+ "lib/couchrest/helper/template-app/forms/lib",
+ "lib/couchrest/helper/template-app/forms/lib/example-template.html",
+ "lib/couchrest/helper/template-app/views",
+ "lib/couchrest/helper/template-app/views/_lib",
+ "lib/couchrest/helper/template-app/views/_lib/helper.js",
+ "lib/couchrest/helper/template-app/views/example",
+ "lib/couchrest/helper/template-app/views/example/map.js",
+ "lib/couchrest/helper/template-app/views/example/reduce.js",
"lib/couchrest/monkeypatches.rb",
"lib/couchrest.rb",
"spec/couchapp_spec.rb",
diff --git a/examples/word_count/markov b/examples/word_count/markov
index af7465e..b873f3e 100755
--- a/examples/word_count/markov
+++ b/examples/word_count/markov
@@ -2,7 +2,7 @@
require File.expand_path(File.dirname(__FILE__)) + '/../../couchrest'
-cr = CouchRest.new("http://localhost:5984")
+cr = CouchRest.new("http://127.0.0.1:5984")
@db = cr.database('word-count-example')
@word_memoizer = {}
diff --git a/examples/word_count/word_count.rb b/examples/word_count/word_count.rb
index 36c1351..f0321ae 100644
--- a/examples/word_count/word_count.rb
+++ b/examples/word_count/word_count.rb
@@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../../couchrest'
-couch = CouchRest.new("http://localhost:5984")
+couch = CouchRest.new("http://127.0.0.1:5984")
db = couch.database('word-count-example')
db.delete! rescue nil
db = couch.create_db('word-count-example')
@@ -62,6 +62,6 @@ end
# }
# })
-# puts "The books have been stored in your CouchDB. To initiate the MapReduce process, visit http://localhost:5984/_utils/ in your browser and click 'word-count-example', then select view 'words' or 'count'. The process could take about 15 minutes on an average MacBook."
+# puts "The books have been stored in your CouchDB. To initiate the MapReduce process, visit http://127.0.0.1:5984/_utils/ in your browser and click 'word-count-example', then select view 'words' or 'count'. The process could take about 15 minutes on an average MacBook."
#
diff --git a/examples/word_count/word_count_query.rb b/examples/word_count/word_count_query.rb
index 31a0222..e69b99a 100644
--- a/examples/word_count/word_count_query.rb
+++ b/examples/word_count/word_count_query.rb
@@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../../couchrest'
-couch = CouchRest.new("http://localhost:5984")
+couch = CouchRest.new("http://127.0.0.1:5984")
db = couch.database('word-count-example')
puts "Now that we've parsed all those books into CouchDB, the queries we can run are incredibly flexible."
@@ -35,5 +35,5 @@ puts "\nHere are the params for 'flight' in the da-vinci book:"
puts params.inspect
puts
puts 'The url looks like this:'
-puts 'http://localhost:5984/word-count-example/_view/word_count/count?key=["flight","da-vinci"]'
+puts 'http://127.0.0.1:5984/word-count-example/_view/word_count/count?key=["flight","da-vinci"]'
puts "\nTry dropping that in your browser..."
\ No newline at end of file
diff --git a/lib/couchrest.rb b/lib/couchrest.rb
index cff840f..1dc3581 100644
--- a/lib/couchrest.rb
+++ b/lib/couchrest.rb
@@ -72,7 +72,7 @@ module CouchRest
db = nil if db && db.empty?
{
- :host => host || "localhost:5984",
+ :host => host || "127.0.0.1:5984",
:database => db,
:doc => docid
}
diff --git a/lib/couchrest/commands/push.rb b/lib/couchrest/commands/push.rb
index 651df6c..a1dcc93 100644
--- a/lib/couchrest/commands/push.rb
+++ b/lib/couchrest/commands/push.rb
@@ -52,7 +52,7 @@ module CouchRest
foo-project/bar-views/my-design/viewname-reduce.js
foo-project/bar-views/my-design/noreduce-map.js
- Pushed to => http://localhost:5984/baz-database/_design/my-design
+ Pushed to => http://127.0.0.1:5984/baz-database/_design/my-design
And the design document:
{
@@ -81,11 +81,11 @@ module CouchRest
(for project global libs). These libraries are only inserted into views which
include the text
- //include-lib
+ // !include lib
or
- #include-lib
+ # !include lib
Couchview is a result of scratching my own itch. I'd be happy to make it more
general, so please contact me at jchris@grabb.it if you'd like to see anything
diff --git a/lib/couchrest/core/model.rb b/lib/couchrest/core/model.rb
index 1d2daa9..199420f 100644
--- a/lib/couchrest/core/model.rb
+++ b/lib/couchrest/core/model.rb
@@ -18,7 +18,7 @@ module CouchRest
# than this example.
#
# class Article < CouchRest::Model
- # use_database CouchRest.database!('http://localhost:5984/couchrest-model-test')
+ # use_database CouchRest.database!('http://127.0.0.1:5984/couchrest-model-test')
# unique_id :slug
#
# view_by :date, :descending => true
@@ -519,16 +519,17 @@ module CouchRest
self.class.casts.each do |k,v|
next unless self[k]
target = v[:as]
+ v[:send] || 'new'
if target.is_a?(Array)
klass = ::Extlib::Inflection.constantize(target[0])
self[k] = self[k].collect do |value|
- klass == Time ? Time.parse(value) : klass.new(value)
+ (!v[:send] && klass == Time) ? Time.parse(value) : klass.send((v[:send] || 'new'), value)
end
else
- self[k] = if target == 'Time'
+ self[k] = if (!v[:send] && target == 'Time')
Time.parse(self[k])
else
- ::Extlib::Inflection.constantize(target).new(self[k])
+ ::Extlib::Inflection.constantize(target).send((v[:send] || 'new'), self[k])
end
end
end
diff --git a/lib/couchrest/core/server.rb b/lib/couchrest/core/server.rb
index 3e62ee2..e76ab82 100644
--- a/lib/couchrest/core/server.rb
+++ b/lib/couchrest/core/server.rb
@@ -1,7 +1,7 @@
module CouchRest
class Server
attr_accessor :uri, :uuid_batch_count
- def initialize server = 'http://localhost:5984', uuid_batch_count = 1000
+ def initialize server = 'http://127.0.0.1:5984', uuid_batch_count = 1000
@uri = server
@uuid_batch_count = uuid_batch_count
end
diff --git a/lib/couchrest/helper/file_manager.rb b/lib/couchrest/helper/file_manager.rb
index 6e02fd8..d7dd0a6 100644
--- a/lib/couchrest/helper/file_manager.rb
+++ b/lib/couchrest/helper/file_manager.rb
@@ -12,12 +12,63 @@ module CouchRest
"png" => "image/png",
"gif" => "image/gif",
"css" => "text/css",
- "js" => "test/javascript"
- }
- def initialize(dbname, host="http://localhost:5984")
+ "js" => "test/javascript",
+ "txt" => "text/plain"
+ }
+
+ def initialize(dbname, host="http://127.0.0.1:5984")
@db = CouchRest.new(host).database(dbname)
end
+ def push_app(appdir, appname)
+ libs = []
+ viewdir = File.join(appdir,"views")
+ attachdir = File.join(appdir,"_attachments")
+
+ @doc = dir_to_fields(appdir)
+ package_forms(@doc["forms"])
+ package_views(@doc["views"])
+
+ docid = "_design/#{appname}"
+ design = @db.get(docid) rescue {}
+ design.merge!(@doc)
+ design['_id'] = docid
+ # design['language'] = lang if lang
+ @db.save(design)
+ push_directory(attachdir, docid)
+ end
+
+ def dir_to_fields(dir)
+ fields = {}
+ (Dir["#{dir}/**/*.*"] -
+ Dir["#{dir}/_attachments/**/*.*"]).each do |file|
+ farray = file.sub(dir, '').sub(/^\//,'').split('/')
+ myfield = fields
+ while farray.length > 1
+ front = farray.shift
+ myfield[front] ||= {}
+ myfield = myfield[front]
+ end
+ fname, fext = farray.shift.split('.')
+ fguts = File.open(file).read
+ if fext == 'json'
+ myfield[fname] = JSON.parse(fguts)
+ else
+ myfield[fname] = fguts
+ end
+ end
+ return fields
+ end
+
+
+ # Generate an application in the given directory.
+ # This is a class method because it doesn't depend on
+ # specifying a database.
+ def self.generate_app(app_dir)
+ templatedir = File.join(File.expand_path(File.dirname(__FILE__)), 'template-app')
+ FileUtils.cp_r(templatedir, app_dir)
+ end
+
def push_directory(push_dir, docid=nil)
docid ||= push_dir.split('/').reverse.find{|part|!part.empty?}
@@ -37,7 +88,7 @@ module CouchRest
@attachments[name] = {
"data" => value,
"content_type" => MIMES[name.split('.').last]
- }
+ }
end
doc = @db.get(docid) rescue nil
@@ -97,191 +148,76 @@ module CouchRest
end
end
- def push_views(view_dir)
- designs = {}
-
- Dir["#{view_dir}/**/*.*"].each do |design_doc|
- design_doc_parts = design_doc.split('/')
- next if /^lib\..*$/.match design_doc_parts.last
- pre_normalized_view_name = design_doc_parts.last.split("-")
- view_name = pre_normalized_view_name[0..pre_normalized_view_name.length-2].join("-")
-
- folder = design_doc_parts[-2]
-
- designs[folder] ||= {}
- designs[folder]["views"] ||= {}
- design_lang = design_doc_parts.last.split(".").last
- designs[folder]["language"] ||= LANGS[design_lang]
-
- libs = ""
- Dir["#{view_dir}/lib.#{design_lang}"].collect do |global_lib|
- libs << open(global_lib).read
- libs << "\n"
- end
- Dir["#{view_dir}/#{folder}/lib.#{design_lang}"].collect do |global_lib|
- libs << open(global_lib).read
- libs << "\n"
- end
- if design_doc_parts.last =~ /-map/
- designs[folder]["views"]["#{view_name}-map"] ||= {}
-
- designs[folder]["views"]["#{view_name}-map"]["map"] = read(design_doc, libs)
-
- designs[folder]["views"]["#{view_name}-reduce"] ||= {}
- designs[folder]["views"]["#{view_name}-reduce"]["map"] = read(design_doc, libs)
- end
-
- if design_doc_parts.last =~ /-reduce/
- designs[folder]["views"]["#{view_name}-reduce"] ||= {}
-
- designs[folder]["views"]["#{view_name}-reduce"]["reduce"] = read(design_doc, libs)
- end
- end
-
- # cleanup empty maps and reduces
- designs.each do |name, props|
- props["views"].each do |view, funcs|
- next unless view.include?("reduce")
- props["views"].delete(view) unless funcs.keys.include?("reduce")
- end
- end
-
- designs.each do |k,v|
- create_or_update("_design/#{k}", v)
- end
-
- designs
- end
-
- def pull_views(view_dir)
- prefix = "_design"
- ds = db.documents(:startkey => '#{prefix}/', :endkey => '#{prefix}/ZZZZZZZZZ')
- ds['rows'].collect{|r|r['id']}.each do |id|
- puts directory = id.split('/').last
- FileUtils.mkdir_p(File.join(view_dir,directory))
- views = db.get(id)['views']
-
- vgroups = views.keys.group_by{|k|k.sub(/\-(map|reduce)$/,'')}
- vgroups.each do|g,vs|
- mapname = vs.find {|v|views[v]["map"]}
- if mapname
- # save map
- mapfunc = views[mapname]["map"]
- mapfile = File.join(view_dir, directory, "#{g}-map.js") # todo support non-js views
- File.open(mapfile,'w') do |f|
- f.write mapfunc
- end
- end
-
- reducename = vs.find {|v|views[v]["reduce"]}
- if reducename
- # save reduce
- reducefunc = views[reducename]["reduce"]
- reducefile = File.join(view_dir, directory, "#{g}-reduce.js") # todo support non-js views
- File.open(reducefile,'w') do |f|
- f.write reducefunc
- end
- end
- end
- end
-
- end
-
- def push_app(appdir, appname)
- libs = []
- viewdir = File.join(appdir,"views")
- attachdir = File.join(appdir,"_attachments")
- views, lang = read_design_views(viewdir)
-
- docid = "_design/#{appname}"
- design = @db.get(docid) rescue {}
- design['_id'] = docid
- design['views'] = views
- design['language'] = lang if lang
- @db.save(design)
- push_directory(attachdir, docid)
-
- push_fields(appdir, docid)
- end
-
- def push_fields(appdir, docid)
- fields = {}
- (Dir["#{appdir}/**/*.*"] -
- Dir["#{appdir}/views/**/*.*"] -
- Dir["#{appdir}/doc.json"] -
- Dir["#{appdir}/_attachments/**/*.*"]).each do |file|
- farray = file.sub(appdir, '').sub(/^\//,'').split('/')
- myfield = fields
- while farray.length > 1
- front = farray.shift
- myfield[front] ||= {}
- myfield = myfield[front]
- end
- fname, fext = farray.shift.split('.')
- fguts = File.open(file).read
- if fext == 'json'
- myfield[fname] = JSON.parse(fguts)
- else
- myfield[fname] = fguts
- end
- end
- if File.exists?("#{appdir}/doc.json")
- default_json = JSON.parse(File.open("#{appdir}/doc.json").read)
- end
- design = @db.get(docid) rescue {}
- design.merge!(fields)
- design.merge!(default_json) if default_json
- @db.save(design)
- end
-
- # Generate an application in the given directory.
- # This is a class method because it doesn't depend on
- # specifying a database.
- def self.generate_app(app_dir)
- FileUtils.mkdir_p(app_dir)
- FileUtils.mkdir_p(File.join(app_dir,"_attachments"))
- FileUtils.mkdir_p(File.join(app_dir,"views"))
- FileUtils.mkdir_p(File.join(app_dir,"foo"))
-
- {
- "index.html" => "_attachments",
- 'example-map.js' => "views",
- 'example-reduce.js' => "views",
- 'bar.txt' => "foo",
- }.each do |filename, targetdir|
- template = File.join(File.expand_path(File.dirname(__FILE__)), 'templates',filename)
- dest = File.join(app_dir,targetdir,filename)
- FileUtils.cp(template, dest)
- end
- end
-
private
- def read_design_views(viewdir)
- libs = []
- language = nil
- views = {}
- Dir["#{viewdir}/*.*"].each do |viewfile|
- view_parts = viewfile.split('/')
- viewfile_name = view_parts.last
- # example-map.js
- viewfile_name_parts = viewfile_name.split('.')
- viewfile_ext = viewfile_name_parts.last
- view_name_parts = viewfile_name_parts.first.split('-')
- func_type = view_name_parts.pop
- view_name = view_name_parts.join('-')
- contents = File.open(viewfile).read
- if /^lib\..*$/.match viewfile_name
- libs.push(contents)
- else
- views[view_name] ||= {}
- language = LANGS[viewfile_ext]
- views[view_name][func_type] = contents.sub(/(\/\/|#)include-lib/,libs.join("\n"))
- end
- end
- [views, language]
+ def package_forms(funcs)
+ apply_lib(funcs)
end
+ def package_views(views)
+ views.each do |view, funcs|
+ apply_lib(funcs)
+ end
+ end
+
+ def apply_lib(funcs)
+ funcs.each do |k,v|
+ next unless v.is_a?(String)
+ funcs[k] = process_include(process_require(v))
+ end
+ end
+
+ # process requires
+ def process_require(f_string)
+ f_string.gsub /(\/\/|#)\ ?!require (.*)/ do
+ fields = $2.split('.')
+ library = @doc
+ fields.each do |field|
+ library = library[field]
+ break unless library
+ end
+ library
+ end
+ end
+
+
+ def process_include(f_string)
+
+ # process includes
+ included = {}
+ f_string.gsub /(\/\/|#)\ ?!include (.*)/ do
+ fields = $2.split('.')
+ library = @doc
+ include_to = included
+ count = fields.length
+ fields.each_with_index do |field, i|
+ break unless library[field]
+ library = library[field]
+ # normal case
+ if i+1 < count
+ include_to[field] = include_to[field] || {}
+ include_to = include_to[field]
+ else
+ # last one
+ include_to[field] = library
+ end
+ end
+
+ end
+ # puts included.inspect
+ rval = if included == {}
+ f_string
+ else
+ varstrings = included.collect do |k, v|
+ "var #{k} = #{v.to_json};"
+ end
+ f_string.sub /(\/\/|#)\ ?!include (.*)/, varstrings.join("\n")
+ end
+
+ rval
+ end
+
+
def say words
puts words if @loud
end
@@ -289,29 +225,5 @@ module CouchRest
def md5 string
Digest::MD5.hexdigest(string)
end
-
- def read(file, libs=nil)
- st = open(file).read
- st.sub!(/(\/\/|#)include-lib/,libs) if libs
- st
- end
-
- def create_or_update(id, fields)
- existing = @db.get(id) rescue nil
-
- if existing
- updated = existing.merge(fields)
- if existing != updated
- say "replacing #{id}"
- db.save(updated)
- else
- say "skipping #{id}"
- end
- else
- say "creating #{id}"
- db.save(fields.merge({"_id" => id}))
- end
-
- end
end
end
diff --git a/lib/couchrest/helper/templates/index.html b/lib/couchrest/helper/template-app/_attachments/index.html
similarity index 100%
rename from lib/couchrest/helper/templates/index.html
rename to lib/couchrest/helper/template-app/_attachments/index.html
diff --git a/lib/couchrest/helper/templates/bar.txt b/lib/couchrest/helper/template-app/foo/bar.txt
similarity index 100%
rename from lib/couchrest/helper/templates/bar.txt
rename to lib/couchrest/helper/template-app/foo/bar.txt
diff --git a/lib/couchrest/helper/template-app/forms/example-form.js b/lib/couchrest/helper/template-app/forms/example-form.js
new file mode 100644
index 0000000..5f3079f
--- /dev/null
+++ b/lib/couchrest/helper/template-app/forms/example-form.js
@@ -0,0 +1,17 @@
+function(doc, req) {
+ // !include lib.templates
+
+ // !require lib.helpers.template
+
+ respondWith(req, {
+ html : function() {
+ var html = template(lib.templates.example, doc);
+ return {body:html}
+ },
+ xml : function() {
+ return {
+ body :