couch 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/README.rdoc +6 -88
  2. data/Rakefile +3 -54
  3. data/bin/couch +1 -7
  4. data/couch.gemspec +6 -72
  5. data/lib/couch.rb +0 -41
  6. metadata +9 -127
  7. data/.gitmodules +0 -3
  8. data/LICENSE +0 -20
  9. data/lib/couch/actions/base.rb +0 -15
  10. data/lib/couch/actions/pull.rb +0 -28
  11. data/lib/couch/actions/push.rb +0 -53
  12. data/lib/couch/actions/routes.rb +0 -41
  13. data/lib/couch/commands.rb +0 -41
  14. data/lib/couch/commands/destroy.rb +0 -9
  15. data/lib/couch/commands/generate.rb +0 -9
  16. data/lib/couch/commands/pull.rb +0 -4
  17. data/lib/couch/commands/push.rb +0 -4
  18. data/lib/couch/commands/routes.rb +0 -4
  19. data/lib/couch/design_document.rb +0 -314
  20. data/lib/couch/generators.rb +0 -63
  21. data/lib/couch/generators/application/USAGE +0 -10
  22. data/lib/couch/generators/application/application_generator.rb +0 -51
  23. data/lib/couch/generators/application/templates/README +0 -1
  24. data/lib/couch/generators/application/templates/_attachments/index.html +0 -11
  25. data/lib/couch/generators/application/templates/_attachments/stylesheets/application.css +0 -25
  26. data/lib/couch/generators/application/templates/_id +0 -1
  27. data/lib/couch/generators/application/templates/couchrc +0 -1
  28. data/lib/couch/generators/application/templates/gitignore +0 -0
  29. data/lib/couch/generators/application/templates/lib/mustache.js +0 -305
  30. data/lib/couch/generators/application/templates/validate_doc_update.js +0 -3
  31. data/lib/couch/generators/base.rb +0 -66
  32. data/lib/couch/generators/list/USAGE +0 -8
  33. data/lib/couch/generators/list/list_generator.rb +0 -9
  34. data/lib/couch/generators/list/templates/list.js +0 -29
  35. data/lib/couch/generators/named_base.rb +0 -22
  36. data/lib/couch/generators/scaffold/USAGE +0 -10
  37. data/lib/couch/generators/scaffold/scaffold_generator.rb +0 -28
  38. data/lib/couch/generators/show/USAGE +0 -8
  39. data/lib/couch/generators/show/show_generator.rb +0 -9
  40. data/lib/couch/generators/show/templates/show.js +0 -20
  41. data/lib/couch/generators/validation/USAGE +0 -9
  42. data/lib/couch/generators/validation/templates/validate_doc_update.js +0 -3
  43. data/lib/couch/generators/validation/validation_generator.rb +0 -34
  44. data/lib/couch/generators/view/USAGE +0 -8
  45. data/lib/couch/generators/view/templates/map.js +0 -5
  46. data/lib/couch/generators/view/view_generator.rb +0 -17
  47. data/lib/couch/version.rb +0 -3
  48. data/spec/couch/design_document_spec.rb +0 -313
  49. data/spec/couch_spec.rb +0 -7
  50. data/spec/spec.opts +0 -1
  51. data/spec/spec_helper.rb +0 -9
@@ -1,3 +0,0 @@
1
- [submodule "vendor/mustache.js"]
2
- path = vendor/mustache.js
3
- url = git://github.com/janl/mustache.js.git
data/LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2009 Johannes J. Schmidt
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,15 +0,0 @@
1
- require 'thor/group'
2
- require 'active_support/inflector'
3
-
4
- module Couch
5
- module Actions
6
- class Base < Thor::Group
7
- include Thor::Actions
8
-
9
- def self.banner
10
- "couch #{to_s.split('::').last.underscore}"
11
- end
12
- end
13
- end
14
- end
15
-
@@ -1,28 +0,0 @@
1
- require 'couch/actions/base'
2
- require 'couch/design_document'
3
-
4
- require "rest_client"
5
-
6
- module Couch
7
- module Actions
8
- class Pull < Base
9
- add_runtime_options!
10
-
11
- def pull
12
- doc = DesignDocument.new
13
- say "Pulling %s" % doc.url
14
-
15
- resp = RestClient.get doc.url(:attachments => true)
16
- doc.json = resp.body
17
-
18
- doc.write do |filename, content|
19
- create_file filename, content
20
- end
21
-
22
- say "Checked out %s" % doc.rev
23
- rescue RestClient::ResourceNotFound
24
- say "Error: Document %s does not exist!" % doc.id
25
- end
26
- end
27
- end
28
- end
@@ -1,53 +0,0 @@
1
- require 'couch/actions/base'
2
- require 'couch/design_document'
3
-
4
- require 'pathname'
5
- require "rest_client"
6
-
7
- module Couch
8
- module Actions
9
- class Push < Base
10
- def initialize(*args)
11
- super
12
- @doc = DesignDocument.new
13
- end
14
-
15
- def create_database_unless_exists
16
- RestClient.put @doc.database, nil
17
- say "Created database %s" % @doc.database
18
- rescue RestClient::PreconditionFailed
19
- end
20
-
21
- def push
22
- root = Pathname.new(destination_root)
23
- filenames = Dir[root.join "**/*"]
24
- filenames.map! { |file| Pathname.new file }
25
- filenames.map! { |path| path.relative_path_from root }
26
- filenames.delete_if { |path| !path.file? }
27
- filenames.map!(&:to_s)
28
- @doc.read(filenames) do |filename|
29
- File.read File.join(destination_root, filename)
30
- end
31
-
32
- say "Pushing to %s" % @doc.url
33
-
34
- resp = RestClient.put @doc.url, @doc.json
35
- response = JSON.parse(resp.body)
36
-
37
- if response["ok"]
38
- @doc.rev = response["rev"]
39
- File.open File.join(destination_root, "_rev"), "w" do |file|
40
- file << @doc.rev
41
- end
42
-
43
- say "Pushed %s" % @doc.rev
44
- else
45
- say "Error occured: %s" % response.inspect
46
- end
47
-
48
- rescue RestClient::Conflict
49
- say "Conflict! Try to pull first or delete ./_rev."
50
- end
51
- end
52
- end
53
- end
@@ -1,41 +0,0 @@
1
- require 'couch/actions/base'
2
-
3
- module Couch
4
- module Actions
5
- class Routes < Base
6
- def routes
7
- say 'Static:'
8
- Dir.glob(File.join(destination_root, "_attachments/*.html")).each do |file|
9
- say ' %s' % attachment_url(file)
10
- end
11
-
12
- say 'Lists:'
13
- Dir.glob(File.join(destination_root, "lists/*")).each do |list|
14
- Dir.glob(File.join(destination_root, "views/*")).each do |view|
15
- say ' %s' % list_url(list, view)
16
- end
17
- end
18
-
19
- say 'Shows:'
20
- Dir.glob(File.join(destination_root, "shows/*")).each do |show|
21
- say ' %s' % show_url(show)
22
- say ' %s' % show_url(show, '/:id')
23
- end
24
- end
25
-
26
- protected
27
-
28
- def attachment_url(file)
29
- File.join(Couch.database, '_design', File.basename(Couch.database), File.basename(file))
30
- end
31
-
32
- def list_url(list, view)
33
- File.join(Couch.database, '_design', File.basename(Couch.database), '_list', File.basename(view), File.basename(list, '.js'))
34
- end
35
-
36
- def show_url(show, id = '/')
37
- File.join(Couch.database, '_design', File.basename(Couch.database), '_show', File.basename(show, '.js'), id)
38
- end
39
- end
40
- end
41
- end
@@ -1,41 +0,0 @@
1
- if ARGV.empty?
2
- ARGV << '--help'
3
- end
4
-
5
- HELP_TEXT = <<-EOT
6
- Usage: couch COMMAND [ARGS]
7
-
8
- The most common couch commands are:
9
- generate Generate new code (short-cut alias: "g")
10
- push Push application code to CouchDB
11
- pull Pull latest application code from CouchDB
12
- routes List application urls
13
-
14
- In addition to those, there are:
15
- destroy Undo code generated with "generate"
16
-
17
- All commands can be run with -h for more information.
18
- EOT
19
-
20
-
21
- case ARGV.shift
22
- when 'g', 'generate'
23
- require 'couch/commands/generate'
24
- when 'destroy'
25
- require 'couch/commands/destroy'
26
- when 'push'
27
- require 'couch/commands/push'
28
- when 'pull'
29
- require 'couch/commands/pull'
30
- when 'routes'
31
- require 'couch/commands/routes'
32
-
33
- when '--help', '-h'
34
- puts HELP_TEXT
35
- when '--version', '-v'
36
- require 'couch/version'
37
- puts "Couch #{Couch::VERSION}"
38
- else
39
- puts "Error: Command not recognized"
40
- puts HELP_TEXT
41
- end
@@ -1,9 +0,0 @@
1
- require 'couch/generators'
2
-
3
- if [nil, "-h", "--help"].include?(ARGV.first)
4
- Couch::Generators.help 'destroy'
5
- exit
6
- end
7
-
8
- name = ARGV.shift
9
- Couch::Generators.invoke name, ARGV, :behavior => :revoke, :destination_root => Couch.root
@@ -1,9 +0,0 @@
1
- require 'couch/generators'
2
-
3
- if [nil, "-h", "--help"].include?(ARGV.first)
4
- Couch::Generators.help 'generate'
5
- exit
6
- end
7
-
8
- name = ARGV.shift
9
- Couch::Generators.invoke name, ARGV, :behavior => :invoke, :destination_root => Couch.root
@@ -1,4 +0,0 @@
1
- require 'couch'
2
- require 'couch/actions/pull'
3
-
4
- Couch::Actions::Pull.start ARGV, :destination_root => Couch.root
@@ -1,4 +0,0 @@
1
- require 'couch'
2
- require 'couch/actions/push'
3
-
4
- Couch::Actions::Push.start ARGV, :destination_root => Couch.root
@@ -1,4 +0,0 @@
1
- require 'couch'
2
- require 'couch/actions/routes'
3
-
4
- Couch::Actions::Routes.start ARGV, :destination_root => Couch.root
@@ -1,314 +0,0 @@
1
- require 'uri'
2
-
3
- module Couch
4
- class DesignDocument
5
- # Mime type mapping from extensions
6
- MIME_TYPE_MAPPING = {
7
- ".html" => "text/html",
8
- ".js" => "text/javascript",
9
- ".css" => "text/css",
10
- }
11
-
12
- # Files that should have a .js extension
13
- JAVASCRIPT_FILES = %w[
14
- validate_doc_update
15
- lists/*
16
- shows/*
17
- updates/*
18
- views/*/*
19
- ]
20
-
21
- # Files that should not be included in document
22
- EXCLUDE_FILES = %w[
23
- README
24
- ]
25
-
26
- attr_accessor :hash
27
-
28
- def initialize
29
- @hash = {}
30
- end
31
-
32
- # Read document from a filesystem.
33
- #
34
- # Takes a filename,
35
- # many filenames,
36
- # or an array of filenames
37
- # and assign the return value of a yielded block to the hash.
38
- #
39
- # Nested hashes
40
- # like { "hash" => { "key" => "value" } }
41
- # can be constructed if the filename contains a slash (/),
42
- # eg "hash/key".
43
- #
44
- def read(*filenames, &block)
45
- filenames.flatten.uniq.each do |filename|
46
- # skip exclude files
47
- next if EXCLUDE_FILES.include?(filename)
48
-
49
- key = filename.dup
50
- # strip extname from javascript files
51
- key.sub!(/\.js$/, '') if filename =~ /#{JAVASCRIPT_FILES.join('|')}/
52
-
53
- set_hash_at key, block.call(filename)
54
- end
55
-
56
- map_attachments!
57
- inject_makros!
58
- end
59
-
60
- # Write document to a filesystem
61
- #
62
- # Takes a directoy as startpoint (default is nil),
63
- # a document hash (default is the design documents hash)
64
- # and recursively yields all keys and values to the given block.
65
- #
66
- # Nested hashes
67
- # like { "hash" => { "key" => "value" } }
68
- # will result in the yielded filename
69
- # "hash/key".
70
- #
71
- # The key "_attachments" has a special meaning:
72
- # the value holds base64 encoded data as well as other metadata.
73
- # This data will gets decoded and used as value for the key.
74
- #
75
- def write(directory = nil, doc = nil, &block)
76
- reduce_attachments!
77
- reject_makros!
78
-
79
- doc ||= hash
80
- doc.each do |key, value|
81
- filename = directory ? File.join(directory, key) : key.dup
82
- if value.is_a?(Hash)
83
- write(filename, value, &block)
84
- else
85
- # append extname to javascript files
86
- filename << '.js' if filename =~ /#{JAVASCRIPT_FILES.join('|')}/
87
- block.call(filename, value)
88
- end
89
- end
90
- end
91
-
92
- # Returns a JSON string representation of the documents hash
93
- #
94
- def json
95
- hash.to_json
96
- end
97
-
98
- # Build the documents hash from a JSON string
99
- #
100
- def json=(json)
101
- self.hash = JSON.parse(json)
102
- end
103
-
104
-
105
- # Accessor for id
106
- def id
107
- hash["_id"] || Couch.id
108
- end
109
-
110
- # Accessor for rev
111
- def rev
112
- hash["_rev"] || Couch.rev
113
- end
114
-
115
- # Updates rev in documents hash
116
- def rev=(new_rev)
117
- hash["_rev"] = new_rev
118
- end
119
-
120
-
121
- # Accessor for couch database
122
- def database
123
- @database ||= Couch.database
124
- end
125
-
126
- # Base URL for document
127
- def base_url
128
- @base_url ||= File.join(database, id)
129
- end
130
-
131
- # URL for accessing design document
132
- #
133
- # Takes an optional options hash
134
- # which gets converted to url encoded options
135
- # and appended to the documents base url
136
- #
137
- def url(options = {})
138
- base_url + build_options_string(options)
139
- end
140
-
141
- private
142
-
143
- def hash_at(path)
144
- current_hash = hash
145
-
146
- parts = path.split('/')
147
- key = parts.pop
148
-
149
- parts.each do |part|
150
- current_hash[part] ||= {}
151
- current_hash = current_hash[part]
152
- end
153
-
154
- current_hash[key]
155
- end
156
-
157
- def set_hash_at(path, value)
158
- current_hash = hash
159
-
160
- parts = path.split('/')
161
- key = parts.pop
162
-
163
- parts.each do |part|
164
- current_hash[part] ||= {}
165
- current_hash = current_hash[part]
166
- end
167
-
168
- current_hash[key] = value
169
- end
170
-
171
- def build_options_string(options)
172
- return '' if options.empty?
173
- options_array = []
174
- options.each do |key, value|
175
- options_array << URI.escape([key, value].join('='))
176
- end
177
- '?' + options_array.join("&")
178
- end
179
-
180
- def inject_makros!
181
- self.hash = inject_code_makro(hash)
182
- self.hash = inject_json_makro(hash)
183
- end
184
-
185
- def inject_code_makro(doc)
186
- doc.each do |key, value|
187
- doc[key] = if value.is_a?(String)
188
- value.gsub(/\/\/\s*!code.*$/) do |match|
189
- filename = match.sub(/^.*!code\s*(\S+).*$/, '\1')
190
- hash_at File.join('lib', filename)
191
- end
192
- elsif value.is_a?(Hash)
193
- inject_code_makro(value)
194
- else
195
- value
196
- end
197
- end
198
-
199
- doc
200
- end
201
-
202
- def inject_json_makro(doc)
203
- doc.each do |key, value|
204
- doc[key] = if value.is_a?(String)
205
- value.gsub(/\/\/\s*!json.*$/) do |match|
206
- filename = match.sub(/^.*!json\s*(\S+).*$/, '\1')
207
- 'var %s = %s;' % [filename.sub(/\..*$/, ''), hash_at(File.join('lib', filename)).to_json]
208
- end
209
- elsif value.is_a?(Hash)
210
- inject_json_makro(value)
211
- else
212
- value
213
- end
214
- end
215
-
216
- doc
217
- end
218
-
219
- def reject_makros!
220
- # TODO: recursive walk libs
221
- libs = hash["lib"]
222
- return if libs.nil? || libs.empty?
223
- # Attention: replace json makros first!
224
- self.hash = reject_json_makro(hash, libs)
225
- self.hash = reject_code_makro(hash, libs)
226
- end
227
-
228
- def reject_code_makro(doc, libs)
229
- doc = doc.dup
230
- doc.each do |key, value|
231
- next if key == "lib"
232
-
233
- if value.is_a?(String)
234
- libs.each do |name, content|
235
- # only try substituting strings
236
- next unless content.is_a?(String)
237
- next unless value.include?(content)
238
- doc[key] = value.gsub(content, "// !code #{name}")
239
- end
240
- elsif value.is_a?(Hash)
241
- doc[key] = reject_code_makro(value, libs)
242
- end
243
- end
244
- doc
245
- end
246
-
247
- def reject_json_makro(doc, libs)
248
- doc.each do |key, value|
249
- next if key == "lib"
250
- if value.is_a?(String)
251
- libs.each do |name, content|
252
- # only try substituting strings
253
- next unless content.is_a?(String)
254
- json = 'var %s = %s;' % [name.sub(/\..*$/, ''), content.to_json]
255
- next unless value.include?(json)
256
- doc[key] = value.gsub(json, "// !json #{name}")
257
- end
258
- elsif value.is_a?(Hash)
259
- doc[key] = reject_json_makro(value, libs)
260
- end
261
- end
262
- doc
263
- end
264
-
265
- def reduce_attachments!
266
- return hash unless hash["_attachments"]
267
- attachments = {}
268
- hash["_attachments"].each do |key, value|
269
- data = value["data"]
270
- next unless data
271
- attachments.update key => decode_attachment(data)
272
- end
273
- hash.update "_attachments" => attachments
274
- end
275
-
276
- def map_attachments!
277
- return unless hash["_attachments"]
278
- attachments = {}
279
- flatten_attachements(hash["_attachments"]).each do |key, value|
280
- attachments.update key => {
281
- "data" => encode_attachment(value),
282
- "content_type" => mime_type_for(key)
283
- }
284
- end
285
- self.hash.update "_attachments" => attachments
286
- end
287
-
288
- def flatten_attachements(doc, base = nil)
289
- result = {}
290
- doc.each do |key, value|
291
- new_base = base ? [base, key].join('/') : key
292
- if value.is_a?(Hash)
293
- result.update flatten_attachements(value, new_base)
294
- else
295
- result.update new_base => value
296
- end
297
- end
298
- result
299
- end
300
-
301
- def decode_attachment(data)
302
- data.unpack("m").first
303
- end
304
-
305
- def encode_attachment(data)
306
- [data].pack("m").gsub(/\s+/,'')
307
- end
308
-
309
- def mime_type_for(filename)
310
- ext = File.extname(filename)
311
- MIME_TYPE_MAPPING[ext] || 'text/plain'
312
- end
313
- end
314
- end