couch-shell 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/couch-shell-plugin/core.rb +241 -0
- data/lib/couch-shell-plugin/core_designs.rb +24 -0
- data/lib/couch-shell-plugin/core_edit.rb +38 -0
- data/lib/couch-shell-plugin/core_lucene.rb +54 -0
- data/lib/couch-shell-plugin/core_views.rb +165 -0
- data/lib/couch-shell.rb +5 -0
- data/lib/couch-shell/eval_context.rb +26 -3
- data/lib/couch-shell/exceptions.rb +65 -0
- data/lib/couch-shell/json_value.rb +91 -19
- data/lib/couch-shell/plugin.rb +251 -0
- data/lib/couch-shell/plugin_utils.rb +88 -0
- data/lib/couch-shell/response.rb +19 -14
- data/lib/couch-shell/shell.rb +204 -415
- data/lib/couch-shell/version.rb +1 -1
- metadata +11 -3
@@ -0,0 +1,241 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "couch-shell/plugin"
|
4
|
+
|
5
|
+
module CouchShell
|
6
|
+
|
7
|
+
class CorePlugin < Plugin
|
8
|
+
|
9
|
+
var "A fresh uuid from the CouchDB server."
|
10
|
+
def lookup_uuid
|
11
|
+
shell.execute "uuids"
|
12
|
+
if shell.responses.current(&:ok?)
|
13
|
+
json = shell.responses.current.json_value
|
14
|
+
if json && (uuids = json["uuids"]) && uuids.array? && uuids.length > 0
|
15
|
+
uuids[0]
|
16
|
+
else
|
17
|
+
raise ShellError, "unkown json structure"
|
18
|
+
end
|
19
|
+
else
|
20
|
+
raise ShellError, "uuids request failed"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
var "Value of the id or _id member of the last response."
|
25
|
+
def lookup_id
|
26
|
+
shell.responses.current { |r| r.attr "id", "_id" } or raise VarNotSet
|
27
|
+
end
|
28
|
+
|
29
|
+
var "Value of the rev or _rev member of the last response."
|
30
|
+
def lookup_rev
|
31
|
+
@responses.current { |r| r.attr "rev", "_rev" } or raise VarNotSet
|
32
|
+
end
|
33
|
+
|
34
|
+
var "Shortcut for $(id)?rev=$(rev)."
|
35
|
+
def lookup_idr
|
36
|
+
shell.interpolate "$(id)?rev=$(rev)"
|
37
|
+
end
|
38
|
+
|
39
|
+
var "Content-Type of the last response."
|
40
|
+
def lookup_content_type
|
41
|
+
shell.responses.current(&:content_type)
|
42
|
+
end
|
43
|
+
|
44
|
+
var "Current server url."
|
45
|
+
def lookup_server
|
46
|
+
raise VarNotSet unless shell.server_url
|
47
|
+
u = shell.server_url
|
48
|
+
"#{u.scheme}://#{u.host}:#{u.port}#{u.path}"
|
49
|
+
end
|
50
|
+
|
51
|
+
var "Get response with index X."
|
52
|
+
def lookup_prefix_r(name)
|
53
|
+
i = name.to_i
|
54
|
+
raise VarNotSet unless shell.responses.readable_index?(i)
|
55
|
+
shell.responses[i]
|
56
|
+
end
|
57
|
+
|
58
|
+
var "Get json of response with index X."
|
59
|
+
def lookup_prefix_j(name)
|
60
|
+
i = name.to_i
|
61
|
+
if shell.responses.readable_index?(i)
|
62
|
+
if shell.responses[i].json_value
|
63
|
+
shell.responses[i].json_value
|
64
|
+
else
|
65
|
+
raise ShellError, "no json in response #{i}"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise ShellError, "no response index #{i}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def request_command_with_body(method, argstr)
|
73
|
+
if argstr =~ CouchShell::JSON_DOC_START_RX
|
74
|
+
url, bodyarg = nil, argstr
|
75
|
+
else
|
76
|
+
url, bodyarg= argstr.split(/\s+/, 2)
|
77
|
+
end
|
78
|
+
if bodyarg && bodyarg.start_with?("@")
|
79
|
+
filename, content_type = bodyarg[1..-1].split(/\s+/, 2)
|
80
|
+
body = CouchShell::FileToUpload.new(filename, content_type)
|
81
|
+
else
|
82
|
+
body = bodyarg
|
83
|
+
end
|
84
|
+
real_url = shell.interpolate(url)
|
85
|
+
shell.request method, real_url, body
|
86
|
+
real_url
|
87
|
+
end
|
88
|
+
|
89
|
+
cmd "Perform a GET http request.", synopsis: "[URL]"
|
90
|
+
def execute_get(argstr)
|
91
|
+
shell.request "GET", shell.interpolate(argstr)
|
92
|
+
end
|
93
|
+
|
94
|
+
cmd "Perform a PUT http request.",
|
95
|
+
synopsis: "[URL] [JSON|@FILENAME]"
|
96
|
+
def execute_put(argstr)
|
97
|
+
request_command_with_body("PUT", argstr)
|
98
|
+
end
|
99
|
+
|
100
|
+
cmd "put, followed by cd if put was successful"
|
101
|
+
def execute_cput(argstr)
|
102
|
+
url = request_command_with_body("PUT", argstr)
|
103
|
+
cd url if shell.responses.current(&:ok?)
|
104
|
+
end
|
105
|
+
|
106
|
+
cmd "Perform a POST http request.",
|
107
|
+
synopsis: "[URL] [JSON|@FILENAME]"
|
108
|
+
def execute_post(argstr)
|
109
|
+
request_command_with_body("POST", argstr)
|
110
|
+
end
|
111
|
+
|
112
|
+
cmd "Perform a DELETE http request.", synopsis: "[URL]"
|
113
|
+
def execute_delete(argstr)
|
114
|
+
shell.request "DELETE", shell.interpolate(argstr)
|
115
|
+
end
|
116
|
+
|
117
|
+
cmd "Change current path which will be used to interpret relative urls.",
|
118
|
+
synopsis: "[PATH]"
|
119
|
+
def execute_cd(argstr)
|
120
|
+
shell.cd shell.interpolate(argstr), false
|
121
|
+
end
|
122
|
+
|
123
|
+
cmd "cd followed by get",
|
124
|
+
synopsis: "[PATH]"
|
125
|
+
def execute_cg(argstr)
|
126
|
+
shell.cd shell.interpolate(argstr), true
|
127
|
+
end
|
128
|
+
|
129
|
+
cmd "quit shell"
|
130
|
+
def execute_exit(argstr)
|
131
|
+
raise Quit
|
132
|
+
end
|
133
|
+
|
134
|
+
cmd "quit shell"
|
135
|
+
def execute_quit(argstr)
|
136
|
+
raise Quit
|
137
|
+
end
|
138
|
+
|
139
|
+
cmd "Request uuid(s) from CouchDB server.",
|
140
|
+
synopsis: "[COUNT]"
|
141
|
+
def execute_uuids(argstr)
|
142
|
+
count = argstr ? argstr.to_i : 1
|
143
|
+
shell.request "GET", "/_uuids?count=#{count}"
|
144
|
+
end
|
145
|
+
|
146
|
+
cmd "Echos ARG after interpolating $(...) expressions.",
|
147
|
+
synopsis: "[ARG]"
|
148
|
+
def execute_echo(argstr)
|
149
|
+
if argstr
|
150
|
+
shell.stdout.puts shell.interpolate(argstr)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
cmd "Evaluate EXPR and print the result in a compact form.",
|
155
|
+
synopsis: "EXPR"
|
156
|
+
def execute_print(argstr)
|
157
|
+
raise ShellError, "expression required" unless argstr
|
158
|
+
shell.stdout.puts shell.eval_expr(argstr)
|
159
|
+
end
|
160
|
+
|
161
|
+
cmd "Evaluate EXPR and print the result in a pretty form.",
|
162
|
+
synopsis: "EXPR"
|
163
|
+
def execute_format(argstr)
|
164
|
+
raise ShellError, "expression required" unless argstr
|
165
|
+
val = shell.eval_expr(argstr)
|
166
|
+
if val.respond_to?(:couch_shell_format_string!)
|
167
|
+
shell.stdout.puts val.couch_shell_format_string!
|
168
|
+
else
|
169
|
+
shell.stdout.puts val
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
cmd "Set URL of CouchDB server.",
|
174
|
+
synopsis: "[URL]"
|
175
|
+
def execute_server(argstr)
|
176
|
+
shell.server = argstr
|
177
|
+
end
|
178
|
+
|
179
|
+
cmd "Show full url for PATH after interpolation.",
|
180
|
+
synopsis: "[PATH]"
|
181
|
+
def execute_expand(argstr)
|
182
|
+
shell.stdout.puts shell.expand(shell.interpolate(argstr))
|
183
|
+
end
|
184
|
+
|
185
|
+
cmd "Execute COMMAND in your operating system's shell.",
|
186
|
+
synopsis: "COMMAND"
|
187
|
+
def execute_sh(argstr)
|
188
|
+
raise ShellError, "argument required" unless argstr
|
189
|
+
unless system(argstr)
|
190
|
+
shell.errmsg "command exited with status #{$?.exitstatus}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
cmd "Set member KEY of document at current path to VALUE.",
|
195
|
+
synopsis: "KEY VALUE"
|
196
|
+
def execute_member(argstr)
|
197
|
+
id, rev = nil, nil
|
198
|
+
json = shell.responses.current(&:json_value)
|
199
|
+
unless json && (id = json.attr_or_nil!("_id")) &&
|
200
|
+
(rev = json.attr_or_nil!("_rev")) &&
|
201
|
+
(shell.pathstack.size > 0) &&
|
202
|
+
(shell.pathstack.last == id.to_s)
|
203
|
+
raise ShellError,
|
204
|
+
"`cg' the desired document first, e.g.: `cg /my_db/my_doc_id'"
|
205
|
+
end
|
206
|
+
# TODO: read json string as attribute name if argstr starts with double
|
207
|
+
# quote
|
208
|
+
attr_name, new_valstr = argstr.split(/\s+/, 2)
|
209
|
+
unless attr_name && new_valstr
|
210
|
+
raise ShellError,
|
211
|
+
"attribute name and new value argument required"
|
212
|
+
end
|
213
|
+
if new_valstr == "remove"
|
214
|
+
json.delete_attr!(attr_name)
|
215
|
+
else
|
216
|
+
new_val = JsonValue.parse(new_valstr)
|
217
|
+
json.set_attr!(attr_name, new_val)
|
218
|
+
end
|
219
|
+
shell.request "PUT", "?rev=#{rev}", json.to_s
|
220
|
+
end
|
221
|
+
|
222
|
+
cmd "Set the USERNAME and password for authentication in requests.",
|
223
|
+
synopsis: "USERNAME",
|
224
|
+
doc_text: "Prompts for password."
|
225
|
+
def execute_user(argstr)
|
226
|
+
shell.prompt_msg("Password:", false)
|
227
|
+
shell.password = shell.read_secret
|
228
|
+
# we save the username only after the password was entered
|
229
|
+
# to allow cancellation during password input
|
230
|
+
shell.username = argstr
|
231
|
+
end
|
232
|
+
|
233
|
+
cmd "Use PLUGIN.",
|
234
|
+
synopsis: "PLUGIN"
|
235
|
+
def execute_plugin(argstr)
|
236
|
+
shell.plugin argstr
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "couch-shell/plugin"
|
4
|
+
|
5
|
+
module CouchShell
|
6
|
+
|
7
|
+
class CoreDesignsPlugin < Plugin
|
8
|
+
|
9
|
+
def all_designs_url
|
10
|
+
'/' + dbname! + '/_all_docs?startkey="_design/"&endkey="_design0"'
|
11
|
+
end
|
12
|
+
|
13
|
+
cmd "Get a list of design names in current database."
|
14
|
+
def execute_designs(argstr)
|
15
|
+
raise ShellError, "argument not allowed" if argstr
|
16
|
+
res = request!("GET", all_designs_url, nil, false)
|
17
|
+
res.json["rows"].each { |row|
|
18
|
+
shell.stdout.puts row["key"].sub(%r{\A_design/}, '')
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "couch-shell/plugin"
|
4
|
+
|
5
|
+
module CouchShell
|
6
|
+
|
7
|
+
class CoreEditPlugin < Plugin
|
8
|
+
|
9
|
+
def plugin_initialization
|
10
|
+
@edittext = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
var "Text saved by the last invocation of edit."
|
14
|
+
def lookup_edittext
|
15
|
+
@edittext or raise VarNotSet
|
16
|
+
end
|
17
|
+
|
18
|
+
cmd "Edit a document in your editor.",
|
19
|
+
synopsis: "[URL]"
|
20
|
+
def execute_edit(argstr)
|
21
|
+
url = shell.interpolate(argstr)
|
22
|
+
res = request! "GET", url, nil, false
|
23
|
+
doc = res.json_value.to_s(true)
|
24
|
+
new_doc = edittext!(doc)
|
25
|
+
if new_doc == doc
|
26
|
+
shell.msg "Document hasn't changed. Nothing to submit."
|
27
|
+
return
|
28
|
+
end
|
29
|
+
continue? "Press ENTER to PUT updated document on server " +
|
30
|
+
"or CTRL+C to cancel "
|
31
|
+
unless shell.request("PUT", url, new_doc).ok?
|
32
|
+
shell.msg "recover document text with `print edittext'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "couch-shell/plugin"
|
4
|
+
|
5
|
+
module CouchShell
|
6
|
+
|
7
|
+
class CoreLucenePlugin < Plugin
|
8
|
+
|
9
|
+
def plugin_initialization
|
10
|
+
@ftitext = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
var "Text saved by the last invocation of editfti."
|
14
|
+
def lookup_ftitext
|
15
|
+
@ftitext or raise VarNotSet
|
16
|
+
end
|
17
|
+
|
18
|
+
cmd "Edit fulltext index function in your editor.",
|
19
|
+
synopsis: "DESIGN FULLTEXTINDEX"
|
20
|
+
def execute_editfti(argstr)
|
21
|
+
dbname = dbname!
|
22
|
+
design_name, index_name = argstr.split(/\s+/, 2) if argstr
|
23
|
+
if design_name.nil? || index_name.nil?
|
24
|
+
raise ShellError, "design and fulltext index name required"
|
25
|
+
end
|
26
|
+
res = request! "GET", "/#{dbname}/_design/#{design_name}", nil, false
|
27
|
+
design = res.json_value
|
28
|
+
index = design.fulltext[index_name] if design["fulltext"]
|
29
|
+
indexfun = index && index["index"]
|
30
|
+
new_indexfun = edittext!(indexfun ||
|
31
|
+
"function(doc) {\n var ret = Document.new();\n\n return ret;\n}\n")
|
32
|
+
@ftitext = new_indexfun
|
33
|
+
if new_indexfun == indexfun
|
34
|
+
shell.msg "Index function hasn't changed. Nothing to submit."
|
35
|
+
return
|
36
|
+
end
|
37
|
+
continue? "Press ENTER to submit #{indexfun ? 'updated' : 'new'} " +
|
38
|
+
"index function, CTRL+C to cancel "
|
39
|
+
if design["fulltext"].nil?
|
40
|
+
design.set_attr!("fulltext", {})
|
41
|
+
end
|
42
|
+
if index.nil?
|
43
|
+
design.fulltext.set_attr!(index_name, {})
|
44
|
+
index = design.fulltext[index_name]
|
45
|
+
end
|
46
|
+
index.set_attr!("index", new_indexfun)
|
47
|
+
unless shell.request("PUT", "/#{dbname}/_design/#{design_name}", design.to_s).ok?
|
48
|
+
shell.msg "recover index text with `print ftitext'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require "couch-shell/plugin"
|
4
|
+
|
5
|
+
module CouchShell
|
6
|
+
|
7
|
+
class CoreViewsPlugin < Plugin
|
8
|
+
|
9
|
+
def plugin_initialization
|
10
|
+
@viewtext = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
var "Text saved by the last invocation of editview."
|
14
|
+
def lookup_viewtext
|
15
|
+
@viewtext or raise VarNotSet
|
16
|
+
end
|
17
|
+
|
18
|
+
cmd "Edit map, and optionally reduce function in your editor.",
|
19
|
+
synopsis: "DESIGN VIEW"
|
20
|
+
def execute_editview(argstr)
|
21
|
+
ensure_at_database
|
22
|
+
design_name, view_name = argstr.split(/\s+/, 2)
|
23
|
+
if design_name.nil? || view_name.nil?
|
24
|
+
raise ShellError, "design and view name required"
|
25
|
+
end
|
26
|
+
shell.request "GET", "_design/#{design_name}", nil, false
|
27
|
+
return unless shell.responses.current(&:ok?)
|
28
|
+
design = shell.responses.current.json_value
|
29
|
+
view = design.views[view_name] if design["views"]
|
30
|
+
mapval = view && view["map"]
|
31
|
+
reduceval = view && view["reduce"]
|
32
|
+
t = Tempfile.new(["view", ".js"])
|
33
|
+
t.puts("map")
|
34
|
+
if mapval
|
35
|
+
t.puts mapval
|
36
|
+
else
|
37
|
+
t.puts "function(doc) {\n emit(doc._id, doc);\n}"
|
38
|
+
end
|
39
|
+
if reduceval || view.nil?
|
40
|
+
t.puts
|
41
|
+
t.puts("reduce")
|
42
|
+
if reduceval
|
43
|
+
t.puts reduceval
|
44
|
+
else
|
45
|
+
t.puts "function(keys, values, rereduce) {\n\n}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
t.close
|
49
|
+
continue?("Press ENTER to edit #{view ? 'existing' : 'new'} view, " +
|
50
|
+
"CTRL+C to cancel ")
|
51
|
+
unless system(shell.editor_bin!, t.path)
|
52
|
+
raise ShellError, "editing command failed with exit status #{$?.exitstatus}"
|
53
|
+
end
|
54
|
+
text = t.open.read
|
55
|
+
@viewtext = text
|
56
|
+
t.close
|
57
|
+
mapf = nil
|
58
|
+
reducef = nil
|
59
|
+
inmap = false
|
60
|
+
inreduce = false
|
61
|
+
i = 0
|
62
|
+
text.each_line { |line|
|
63
|
+
i += 1
|
64
|
+
case line
|
65
|
+
when /^map\s*(.*)$/
|
66
|
+
unless $1.empty?
|
67
|
+
shell.msg "recover view text with `print viewtext'"
|
68
|
+
raise ShellError, "invalid map line at line #{i}"
|
69
|
+
end
|
70
|
+
unless mapf.nil?
|
71
|
+
shell.msg "recover view text with `print viewtext'"
|
72
|
+
raise ShellError, "duplicate map line at line #{i}"
|
73
|
+
end
|
74
|
+
inreduce = false
|
75
|
+
inmap = true
|
76
|
+
mapf = ""
|
77
|
+
when /^reduce\s*(.*)$/
|
78
|
+
unless $1.empty?
|
79
|
+
shell.msg "recover view text with `print viewtext'"
|
80
|
+
raise ShellError, "invalid reduce line at line #{i}"
|
81
|
+
end
|
82
|
+
unless reducef.nil?
|
83
|
+
shell.msg "recover view text with `print viewtext'"
|
84
|
+
raise ShellError, "duplicate reduce line at line #{i}"
|
85
|
+
end
|
86
|
+
inmap = false
|
87
|
+
inreduce = true
|
88
|
+
reducef = ""
|
89
|
+
else
|
90
|
+
if inmap
|
91
|
+
mapf << line
|
92
|
+
elsif inreduce
|
93
|
+
reducef << line
|
94
|
+
elsif line =~ /^\s*$/
|
95
|
+
# ignore
|
96
|
+
else
|
97
|
+
shell.msg "recover view text with `print viewtext'"
|
98
|
+
raise ShellError, "unexpected content at line #{i}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
}
|
102
|
+
mapf.strip! if mapf
|
103
|
+
reducef.strip! if reducef
|
104
|
+
mapf = nil if mapf && mapf.empty?
|
105
|
+
reducef = nil if reducef && reducef.empty?
|
106
|
+
shell.prompt_msg "View parsed, following actions would be taken:"
|
107
|
+
if mapf && mapval.nil?
|
108
|
+
shell.prompt_msg " Add map function."
|
109
|
+
elsif mapf.nil? && mapval
|
110
|
+
shell.prompt_msg " Remove map function."
|
111
|
+
elsif mapf && mapval && mapf != mapval
|
112
|
+
shell.prompt_msg " Update map function."
|
113
|
+
end
|
114
|
+
if reducef && reduceval.nil?
|
115
|
+
shell.prompt_msg " Add reduce function."
|
116
|
+
elsif reducef.nil? && reduceval
|
117
|
+
shell.prompt_msg " Remove reduce function."
|
118
|
+
elsif reducef && reduceval && reducef != reduceval
|
119
|
+
shell.prompt_msg " Update reduce function."
|
120
|
+
end
|
121
|
+
continue? "Press ENTER to submit, CTRL+C to cancel "
|
122
|
+
if !design.respond_to?(:views)
|
123
|
+
design.set_attr!("views", {})
|
124
|
+
end
|
125
|
+
if view.nil?
|
126
|
+
design.views.set_attr!(view_name, {})
|
127
|
+
view = design.views[view_name]
|
128
|
+
end
|
129
|
+
if mapf.nil?
|
130
|
+
view.delete_attr!("map")
|
131
|
+
else
|
132
|
+
view.set_attr!("map", mapf)
|
133
|
+
end
|
134
|
+
if reducef.nil?
|
135
|
+
view.delete_attr!("reduce")
|
136
|
+
else
|
137
|
+
view.set_attr!("reduce", reducef)
|
138
|
+
end
|
139
|
+
shell.request "PUT", "_design/#{design_name}", design.to_s
|
140
|
+
unless shell.responses.current(&:ok?)
|
141
|
+
shell.msg "recover view text with `print viewtext'"
|
142
|
+
end
|
143
|
+
ensure
|
144
|
+
if t
|
145
|
+
t.close
|
146
|
+
t.unlink
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
cmd "Shortcut to GET a view.",
|
151
|
+
synopsis: "DESIGN/VIEW[?params]"
|
152
|
+
def execute_view(argstr)
|
153
|
+
if shell.pathstack.size != 1
|
154
|
+
raise ShellError, "current directory must be database"
|
155
|
+
end
|
156
|
+
design_name, view_name = argstr.split("/", 2)
|
157
|
+
if design_name.nil? || view_name.nil?
|
158
|
+
raise ShellError, "argument in the form DESIGN/VIEW required"
|
159
|
+
end
|
160
|
+
shell.request "GET", "_design/#{design_name}/_view/#{view_name}"
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|