camping 1.4 → 1.4.2
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/CHANGELOG +12 -1
- data/COPYING +18 -0
- data/Rakefile +89 -0
- data/bin/camping +200 -42
- data/lib/camping-unabridged.rb +32 -15
- data/lib/camping.rb +11 -10
- metadata +74 -90
- data/examples/blog/blog.db +0 -0
- data/examples/blog/blog.sqlite3 +0 -21
- data/examples/blog/camping.log +0 -82
- data/examples/blog/foo.log +0 -0
- data/examples/blog/start +0 -6
- data/examples/blog/test.yml +0 -0
- data/examples/camping.log +0 -1111
- data/examples/charts/1.gif +0 -0
- data/examples/charts/2.gif +0 -0
- data/examples/charts/3.gif +0 -0
- data/examples/charts/start +0 -6
- data/examples/serve +0 -105
- data/examples/serve.db +0 -0
- data/examples/tepee/start +0 -6
data/CHANGELOG
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
= 1.4.2
|
|
2
|
+
=== 10th May, 2006
|
|
3
|
+
|
|
4
|
+
* Efficient file uploads for multipart/form-data POSTs.
|
|
5
|
+
* Camping tool now uses Mongrel, if available. If not, sticks with WEBrick.
|
|
6
|
+
|
|
7
|
+
= 1.4.1
|
|
8
|
+
=== 3rd May, 2006
|
|
9
|
+
|
|
10
|
+
* Streaming HTTP support. If body is IO, will simply pass to the controller. Mongrel, in particular, supports this nicely.
|
|
11
|
+
|
|
1
12
|
= 1.4
|
|
2
|
-
===
|
|
13
|
+
=== 11th April, 2006
|
|
3
14
|
|
|
4
15
|
* Moved Camping::Controllers::Base to Camping::Base.
|
|
5
16
|
* Moved Camping::Controllers::R to Camping::R.
|
data/COPYING
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Copyright (c) 2006 why the lucky stiff
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
|
5
|
+
deal in the Software without restriction, including without limitation the
|
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/clean'
|
|
3
|
+
require 'rake/gempackagetask'
|
|
4
|
+
require 'rake/rdoctask'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
include FileUtils
|
|
7
|
+
|
|
8
|
+
NAME = "camping"
|
|
9
|
+
VERS = "1.4.2"
|
|
10
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
|
11
|
+
RDOC_OPTS = ['--quiet', '--title', "Camping, the Documentation",
|
|
12
|
+
"--template", "extras/flipbook_rdoc.rb",
|
|
13
|
+
"--opname", "index.html",
|
|
14
|
+
"--line-numbers",
|
|
15
|
+
"--main", "README",
|
|
16
|
+
"--inline-source"]
|
|
17
|
+
|
|
18
|
+
desc "Packages up Camping."
|
|
19
|
+
task :default => [:package]
|
|
20
|
+
task :package => [:clean]
|
|
21
|
+
|
|
22
|
+
task :doc => [:before_doc, :rdoc, :after_doc]
|
|
23
|
+
|
|
24
|
+
task :before_doc do
|
|
25
|
+
mv "lib/camping.rb", "lib/camping-mural.rb"
|
|
26
|
+
mv "lib/camping-unabridged.rb", "lib/camping.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Rake::RDocTask.new do |rdoc|
|
|
30
|
+
rdoc.rdoc_dir = 'doc'
|
|
31
|
+
rdoc.options += RDOC_OPTS
|
|
32
|
+
rdoc.template = "extras/flipbook_rdoc.rb"
|
|
33
|
+
rdoc.main = "README"
|
|
34
|
+
rdoc.title = "Camping, the Documentation"
|
|
35
|
+
rdoc.rdoc_files.add ['README', 'CHANGELOG', 'COPYING', 'lib/camping.rb']
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
task :after_doc do
|
|
39
|
+
mv "lib/camping.rb", "lib/camping-unabridged.rb"
|
|
40
|
+
mv "lib/camping-mural.rb", "lib/camping.rb"
|
|
41
|
+
cp "extras/Camping.gif", "doc/"
|
|
42
|
+
cp "extras/permalink.gif", "doc/"
|
|
43
|
+
sh %{scp -r doc/* #{ENV['USER']}@rubyforge.org:/var/www/gforge-projects/camping/}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
spec =
|
|
47
|
+
Gem::Specification.new do |s|
|
|
48
|
+
s.name = NAME
|
|
49
|
+
s.version = VERS
|
|
50
|
+
s.platform = Gem::Platform::RUBY
|
|
51
|
+
s.has_rdoc = true
|
|
52
|
+
s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
|
|
53
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
|
|
54
|
+
s.summary = "minature rails for stay-at-home moms"
|
|
55
|
+
s.description = s.summary
|
|
56
|
+
s.author = "why the lucky stiff"
|
|
57
|
+
s.email = 'why@ruby-lang.org'
|
|
58
|
+
s.homepage = 'http://code.whytheluckystiff.net/camping/'
|
|
59
|
+
s.executables = ['camping']
|
|
60
|
+
|
|
61
|
+
s.add_dependency('activerecord', '>=1.14.2')
|
|
62
|
+
s.add_dependency('markaby', '>=0.4')
|
|
63
|
+
s.add_dependency('metaid')
|
|
64
|
+
s.required_ruby_version = '>= 1.8.2'
|
|
65
|
+
|
|
66
|
+
s.files = %w(COPYING README Rakefile) +
|
|
67
|
+
Dir.glob("{bin,doc/rdoc,test,lib,extras}/**/*") +
|
|
68
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
|
69
|
+
Dir.glob("examples/**/*.rb") +
|
|
70
|
+
Dir.glob("tools/*.rb")
|
|
71
|
+
|
|
72
|
+
s.require_path = "lib"
|
|
73
|
+
# s.extensions = FileList["ext/**/extconf.rb"].to_a
|
|
74
|
+
s.bindir = "bin"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
Rake::GemPackageTask.new(spec) do |p|
|
|
78
|
+
p.need_tar = true
|
|
79
|
+
p.gem_spec = spec
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
task :install do
|
|
83
|
+
sh %{rake package}
|
|
84
|
+
sh %{sudo gem install pkg/#{NAME}-#{VERS}}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
task :uninstall => [:clean] do
|
|
88
|
+
sh %{sudo gem uninstall #{NAME}}
|
|
89
|
+
end
|
data/bin/camping
CHANGED
|
@@ -2,62 +2,220 @@
|
|
|
2
2
|
|
|
3
3
|
# this line prevents other db adapters from being loaded (oci8 was
|
|
4
4
|
# causing some pain.)
|
|
5
|
-
|
|
5
|
+
unless Object.const_defined? :RAILS_CONNECTION_ADAPTERS
|
|
6
|
+
RAILS_CONNECTION_ADAPTERS = []
|
|
7
|
+
end
|
|
8
|
+
RAILS_CONNECTION_ADAPTERS.replace %w[sqlite]
|
|
6
9
|
|
|
10
|
+
require 'delegate'
|
|
11
|
+
require 'optparse'
|
|
7
12
|
require 'stringio'
|
|
8
|
-
require '
|
|
9
|
-
require 'camping
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'camping'
|
|
15
|
+
|
|
16
|
+
host = '0.0.0.0'
|
|
17
|
+
port = 3301
|
|
18
|
+
|
|
19
|
+
db = nil
|
|
20
|
+
homes = []
|
|
21
|
+
homes << File.join( ENV['HOME'], '.camping.db' ) if ENV['HOME']
|
|
22
|
+
homes << File.join( ENV['APPDATA'], 'Camping.db' ) if ENV['APPDATA']
|
|
23
|
+
homes.each do |db|
|
|
24
|
+
break if File.exists?( db )
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
opts = OptionParser.new do |opts|
|
|
29
|
+
opts.banner = "Usage: camping app1.rb, app2.rb..."
|
|
30
|
+
opts.define_head "#{File.basename($0)}, the microframework ON-button for ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
|
31
|
+
opts.separator ""
|
|
32
|
+
opts.separator "Specific options:"
|
|
33
|
+
|
|
34
|
+
opts.on("-h", "--host HOSTNAME", "Host for web server to bind to (default is all IPs)") do |h|
|
|
35
|
+
host = h
|
|
36
|
+
end
|
|
10
37
|
|
|
11
|
-
(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
USAGE
|
|
38
|
+
opts.on("-p", "--port NUM", "Port for web server (defaults to #{port})") do |p|
|
|
39
|
+
port = p
|
|
40
|
+
end
|
|
15
41
|
|
|
16
|
-
|
|
42
|
+
opts.on("-d", "--database FILE", "Database file (defaults to #{db})") do |d|
|
|
43
|
+
db = d
|
|
44
|
+
end
|
|
17
45
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
46
|
+
opts.separator ""
|
|
47
|
+
opts.separator "Common options:"
|
|
48
|
+
|
|
49
|
+
# No argument, shows at tail. This will print an options summary.
|
|
50
|
+
# Try it and see!
|
|
51
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
52
|
+
puts opts
|
|
53
|
+
exit
|
|
24
54
|
end
|
|
55
|
+
|
|
56
|
+
# Another typical switch to print the version.
|
|
57
|
+
opts.on_tail("--version", "Show version") do
|
|
58
|
+
class << Gem; attr_accessor :loaded_specs; end
|
|
59
|
+
puts Gem.loaded_specs['camping'].version
|
|
60
|
+
exit
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
opts.parse! ARGV
|
|
65
|
+
if ARGV.length < 1
|
|
66
|
+
puts opts
|
|
67
|
+
exit
|
|
25
68
|
end
|
|
26
69
|
|
|
27
70
|
Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => db
|
|
28
71
|
|
|
29
|
-
class
|
|
30
|
-
attr_accessor :klass, :mtime
|
|
31
|
-
|
|
72
|
+
class CampingReloader
|
|
73
|
+
attr_accessor :klass, :mtime, :mount
|
|
74
|
+
|
|
75
|
+
def initialize(script)
|
|
76
|
+
@script = script
|
|
77
|
+
@mount = File.basename(script, '.rb')
|
|
78
|
+
load_app
|
|
79
|
+
end
|
|
32
80
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
81
|
+
def load_app
|
|
82
|
+
@mtime = File.mtime(@script)
|
|
83
|
+
title = File.basename(@script)[/^(\w+)/,1]
|
|
84
|
+
begin
|
|
85
|
+
load @script
|
|
86
|
+
rescue Exception => e
|
|
87
|
+
puts "!! trouble loading #{title}: [#{e.class}] #{e.message}"
|
|
88
|
+
@klass = nil
|
|
89
|
+
return
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil
|
|
93
|
+
unless @klass.const_defined? :C
|
|
94
|
+
puts "!! trouble loading #{title}: not a Camping app"
|
|
95
|
+
@klass = nil
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
@klass.create if @klass.respond_to? :create
|
|
99
|
+
@klass
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Load the script, locate the module
|
|
103
|
+
def reload_app
|
|
104
|
+
newtime = File.mtime( @script )
|
|
105
|
+
return if @klass and @mtime and newtime <= @mtime
|
|
106
|
+
|
|
107
|
+
k = @klass
|
|
108
|
+
Object.instance_eval { remove_const k.name } if k
|
|
109
|
+
load_app
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def run(*a)
|
|
113
|
+
reload_app
|
|
114
|
+
if @klass
|
|
115
|
+
@klass.run(*a)
|
|
116
|
+
else
|
|
117
|
+
Camping.run(*a)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def view_source
|
|
122
|
+
File.read(@script)
|
|
123
|
+
end
|
|
45
124
|
end
|
|
46
125
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
126
|
+
apps = ARGV.map { |script| CampingReloader.new(script) }
|
|
127
|
+
def apps.index_page
|
|
128
|
+
welcome = "You are Camping"
|
|
129
|
+
apps = self
|
|
130
|
+
b = Markaby::Builder.new({}, {})
|
|
131
|
+
b = b.instance_eval do
|
|
132
|
+
html do
|
|
133
|
+
head do
|
|
134
|
+
title welcome
|
|
135
|
+
style <<-END, :type => 'text/css'
|
|
136
|
+
body {
|
|
137
|
+
font-family: verdana, arial, sans-serif;
|
|
138
|
+
padding: 10px 40px;
|
|
139
|
+
margin: 0;
|
|
140
|
+
}
|
|
141
|
+
h1, h2, h3, h4, h5, h6 {
|
|
142
|
+
font-family: utopia, georgia, serif;
|
|
143
|
+
}
|
|
144
|
+
END
|
|
145
|
+
end
|
|
146
|
+
body do
|
|
147
|
+
h1 welcome
|
|
148
|
+
p %{Good day. These are the Camping apps you've mounted.}
|
|
149
|
+
ul do
|
|
150
|
+
apps.each do |app|
|
|
151
|
+
next unless app.klass
|
|
152
|
+
li do
|
|
153
|
+
h3(:style => "display: inline") { a app.klass.name, :href => "/#{app.mount}" }
|
|
154
|
+
small { text " / " ; a "View Source", :href => "/code/#{app.mount}" }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
54
160
|
end
|
|
55
|
-
|
|
56
|
-
nil
|
|
161
|
+
b.to_s
|
|
57
162
|
end
|
|
58
163
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
164
|
+
begin
|
|
165
|
+
require 'mongrel'
|
|
166
|
+
require 'mongrel/camping'
|
|
167
|
+
class IndexHandler < Mongrel::HttpHandler
|
|
168
|
+
def initialize(apps)
|
|
169
|
+
@apps = apps
|
|
170
|
+
end
|
|
171
|
+
def process(req, res)
|
|
172
|
+
res.start(200) do |head, out|
|
|
173
|
+
out << @apps.index_page
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
class ViewSource < Mongrel::HttpHandler
|
|
178
|
+
def initialize(app)
|
|
179
|
+
@app = app
|
|
180
|
+
end
|
|
181
|
+
def process(req, res)
|
|
182
|
+
res.start(200) do |head, out|
|
|
183
|
+
head['Content-Type'] = 'text/plain'
|
|
184
|
+
out << @app.view_source
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
config = Mongrel::Configurator.new :host => host do
|
|
189
|
+
listener :port => port do
|
|
190
|
+
apps.each do |app|
|
|
191
|
+
uri "/#{app.mount}", :handler => Mongrel::Camping::CampingHandler.new(app)
|
|
192
|
+
uri "/code/#{app.mount}", :handler => ViewSource.new(app)
|
|
193
|
+
end
|
|
194
|
+
uri "/", :handler => IndexHandler.new(apps)
|
|
195
|
+
uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
|
|
196
|
+
trap("INT") { stop }
|
|
197
|
+
run
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
config.join
|
|
201
|
+
rescue LoadError
|
|
202
|
+
require 'webrick/httpserver'
|
|
203
|
+
require 'camping/webrick'
|
|
204
|
+
|
|
205
|
+
# Mount the root
|
|
206
|
+
s = WEBrick::HTTPServer.new(:BindAddress => host, :Port => port)
|
|
207
|
+
apps.each do |app|
|
|
208
|
+
s.mount "/#{app.mount}", WEBrick::CampingHandler, app
|
|
209
|
+
s.mount_proc("/code/#{app.mount}") do |req, resp|
|
|
210
|
+
resp['Content-Type'] = 'text/plain'
|
|
211
|
+
resp.body = app.view_source
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
s.mount_proc("/") { |req, resp| resp.body = apps.index_page }
|
|
215
|
+
|
|
216
|
+
# Server up
|
|
217
|
+
trap(:INT) do
|
|
218
|
+
s.shutdown
|
|
219
|
+
end
|
|
220
|
+
s.start
|
|
62
221
|
end
|
|
63
|
-
s.start
|
data/lib/camping-unabridged.rb
CHANGED
|
@@ -123,6 +123,7 @@ module Camping
|
|
|
123
123
|
raise NoMethodError, "#{m}"
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
|
+
alias_method :u, :regular_update
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
# Helpers contains methods available in your controllers and views. You may add
|
|
@@ -252,7 +253,7 @@ module Camping
|
|
|
252
253
|
def URL c='/',*a
|
|
253
254
|
c = R(c, *a) if c.respond_to? :urls
|
|
254
255
|
c = self/c
|
|
255
|
-
c = "
|
|
256
|
+
c = "//"+@env.HTTP_HOST+c if c[/^\//]
|
|
256
257
|
URI(c)
|
|
257
258
|
end
|
|
258
259
|
end
|
|
@@ -356,21 +357,37 @@ module Camping
|
|
|
356
357
|
qs = C.qs_parse(e.QUERY_STRING)
|
|
357
358
|
@in = r
|
|
358
359
|
if %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)|n.match(e.CONTENT_TYPE)
|
|
359
|
-
b = "--#$1"
|
|
360
|
-
@in.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
360
|
+
b = /(?:\r?\n|\A)#{Regexp::quote("--#$1")}(?:--)?\r$/
|
|
361
|
+
until @in.eof?
|
|
362
|
+
fh=H[]
|
|
363
|
+
for l in @in
|
|
364
|
+
case l
|
|
365
|
+
when "\r\n": break
|
|
366
|
+
when /^Content-Disposition: form-data;/
|
|
367
|
+
fh.u H[*$'.scan(/(?:\s(\w+)="([^"]+)")/).flatten]
|
|
368
|
+
when /^Content-Type: (.+?)(\r$|\Z)/m
|
|
369
|
+
puts "=> fh[type] = #$1"
|
|
370
|
+
fh[:type] = $1
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
fn=fh[:name]
|
|
374
|
+
o=if fh[:filename]
|
|
375
|
+
o=fh[:tempfile]=Tempfile.new(:C)
|
|
376
|
+
o.binmode
|
|
369
377
|
else
|
|
370
|
-
fh=
|
|
378
|
+
fh=""
|
|
379
|
+
end
|
|
380
|
+
while l=@in.read(16384)
|
|
381
|
+
if l=~b
|
|
382
|
+
o<<$`.chomp
|
|
383
|
+
@in.seek(-$'.size,IO::SEEK_CUR)
|
|
384
|
+
break
|
|
385
|
+
end
|
|
386
|
+
o<<l
|
|
371
387
|
end
|
|
372
388
|
qs[fn]=fh if fn
|
|
373
|
-
|
|
389
|
+
fh[:tempfile].rewind if fh.is_a?H
|
|
390
|
+
end
|
|
374
391
|
elsif @method == "post"
|
|
375
392
|
qs.merge!(C.qs_parse(@in.read))
|
|
376
393
|
end
|
|
@@ -540,11 +557,11 @@ module Camping
|
|
|
540
557
|
# #=> {'post' => {'id' => '1', 'user' => '_why'}}
|
|
541
558
|
#
|
|
542
559
|
def qs_parse(qs, d = '&;')
|
|
543
|
-
m = proc {|_,o,n|o.
|
|
560
|
+
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
|
544
561
|
(qs||'').
|
|
545
562
|
split(/[#{d}] */n).
|
|
546
563
|
inject(H[]) { |h,p| k, v=un(p).split('=',2)
|
|
547
|
-
h.
|
|
564
|
+
h.u(k.split(/[\]\[]+/).reverse.
|
|
548
565
|
inject(v) { |x,i| H[i,x] },&m)
|
|
549
566
|
}
|
|
550
567
|
end
|