sinatra 2.2.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +133 -16
- data/CONTRIBUTING.md +11 -11
- data/Gemfile +48 -62
- data/MAINTENANCE.md +2 -2
- data/README.md +199 -386
- data/Rakefile +66 -75
- data/VERSION +1 -1
- data/examples/chat.rb +25 -12
- data/examples/lifecycle_events.rb +20 -0
- data/examples/rainbows.rb +3 -1
- data/examples/simple.rb +2 -0
- data/examples/stream.ru +2 -1
- data/lib/sinatra/base.rb +420 -338
- data/lib/sinatra/indifferent_hash.rb +25 -33
- data/lib/sinatra/main.rb +18 -16
- data/lib/sinatra/show_exceptions.rb +17 -15
- data/lib/sinatra/version.rb +3 -1
- data/lib/sinatra.rb +2 -0
- data/sinatra.gemspec +38 -33
- metadata +40 -30
- data/README.de.md +0 -3239
- data/README.es.md +0 -3231
- data/README.fr.md +0 -3111
- data/README.hu.md +0 -728
- data/README.ja.md +0 -2844
- data/README.ko.md +0 -2967
- data/README.malayalam.md +0 -3141
- data/README.pt-br.md +0 -3787
- data/README.pt-pt.md +0 -791
- data/README.ru.md +0 -3207
- data/README.zh.md +0 -2934
@@ -1,11 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
$stderr.puts <<EOF if !Hash.method_defined?(:slice) && !$LOAD_PATH.grep(%r{gems/activesupport}).empty? && ENV['SINATRA_ACTIVESUPPORT_WARNING'] != 'false'
|
3
|
-
WARNING: If you plan to load any of ActiveSupport's core extensions to Hash, be
|
4
|
-
sure to do so *before* loading Sinatra::Application or Sinatra::Base. If not,
|
5
|
-
you may disregard this warning.
|
6
|
-
|
7
|
-
Set SINATRA_ACTIVESUPPORT_WARNING=false in the environment to hide this warning.
|
8
|
-
EOF
|
9
2
|
|
10
3
|
module Sinatra
|
11
4
|
# A poor man's ActiveSupport::HashWithIndifferentAccess, with all the Rails-y
|
@@ -86,7 +79,7 @@ module Sinatra
|
|
86
79
|
super(convert_key(key), convert_value(value))
|
87
80
|
end
|
88
81
|
|
89
|
-
|
82
|
+
alias store []=
|
90
83
|
|
91
84
|
def key(value)
|
92
85
|
super(convert_value(value))
|
@@ -96,35 +89,36 @@ module Sinatra
|
|
96
89
|
super(convert_key(key))
|
97
90
|
end
|
98
91
|
|
99
|
-
|
100
|
-
|
101
|
-
|
92
|
+
alias has_key? key?
|
93
|
+
alias include? key?
|
94
|
+
alias member? key?
|
102
95
|
|
103
96
|
def value?(value)
|
104
97
|
super(convert_value(value))
|
105
98
|
end
|
106
99
|
|
107
|
-
|
100
|
+
alias has_value? value?
|
108
101
|
|
109
102
|
def delete(key)
|
110
103
|
super(convert_key(key))
|
111
104
|
end
|
112
105
|
|
106
|
+
# Added in Ruby 2.3
|
113
107
|
def dig(key, *other_keys)
|
114
108
|
super(convert_key(key), *other_keys)
|
115
|
-
end
|
109
|
+
end
|
116
110
|
|
117
111
|
def fetch_values(*keys)
|
118
112
|
keys.map!(&method(:convert_key))
|
119
113
|
|
120
114
|
super(*keys)
|
121
|
-
end
|
115
|
+
end
|
122
116
|
|
123
117
|
def slice(*keys)
|
124
118
|
keys.map!(&method(:convert_key))
|
125
119
|
|
126
120
|
self.class[super(*keys)]
|
127
|
-
end
|
121
|
+
end
|
128
122
|
|
129
123
|
def values_at(*keys)
|
130
124
|
keys.map!(&method(:convert_key))
|
@@ -148,7 +142,7 @@ module Sinatra
|
|
148
142
|
self
|
149
143
|
end
|
150
144
|
|
151
|
-
|
145
|
+
alias update merge!
|
152
146
|
|
153
147
|
def merge(*other_hashes, &block)
|
154
148
|
dup.merge!(*other_hashes, &block)
|
@@ -158,41 +152,39 @@ module Sinatra
|
|
158
152
|
super(other_hash.is_a?(self.class) ? other_hash : self.class[other_hash])
|
159
153
|
end
|
160
154
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
155
|
+
def transform_values(&block)
|
156
|
+
dup.transform_values!(&block)
|
157
|
+
end
|
165
158
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
159
|
+
def transform_values!
|
160
|
+
super
|
161
|
+
super(&method(:convert_value))
|
170
162
|
end
|
171
163
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
164
|
+
def transform_keys(&block)
|
165
|
+
dup.transform_keys!(&block)
|
166
|
+
end
|
176
167
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
168
|
+
def transform_keys!
|
169
|
+
super
|
170
|
+
super(&method(:convert_key))
|
181
171
|
end
|
182
172
|
|
183
173
|
def select(*args, &block)
|
184
174
|
return to_enum(:select) unless block_given?
|
175
|
+
|
185
176
|
dup.tap { |hash| hash.select!(*args, &block) }
|
186
177
|
end
|
187
178
|
|
188
179
|
def reject(*args, &block)
|
189
180
|
return to_enum(:reject) unless block_given?
|
181
|
+
|
190
182
|
dup.tap { |hash| hash.reject!(*args, &block) }
|
191
183
|
end
|
192
184
|
|
193
185
|
def compact
|
194
186
|
dup.tap(&:compact!)
|
195
|
-
end
|
187
|
+
end
|
196
188
|
|
197
189
|
private
|
198
190
|
|
data/lib/sinatra/main.rb
CHANGED
@@ -1,47 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Sinatra
|
2
|
-
|
4
|
+
PARAMS_CONFIG = {}
|
3
5
|
|
4
6
|
if ARGV.any?
|
5
7
|
require 'optparse'
|
6
|
-
parser = OptionParser.new
|
7
|
-
op.on('-p port', 'set the port (default is 4567)') { |val|
|
8
|
-
op.on('-s server', 'specify rack server/handler') { |val|
|
9
|
-
op.on('-q', 'turn on quiet mode (default is off)') {
|
10
|
-
op.on('-x', 'turn on the mutex lock (default is off)') {
|
8
|
+
parser = OptionParser.new do |op|
|
9
|
+
op.on('-p port', 'set the port (default is 4567)') { |val| PARAMS_CONFIG[:port] = Integer(val) }
|
10
|
+
op.on('-s server', 'specify rack server/handler') { |val| PARAMS_CONFIG[:server] = val }
|
11
|
+
op.on('-q', 'turn on quiet mode (default is off)') { PARAMS_CONFIG[:quiet] = true }
|
12
|
+
op.on('-x', 'turn on the mutex lock (default is off)') { PARAMS_CONFIG[:lock] = true }
|
11
13
|
op.on('-e env', 'set the environment (default is development)') do |val|
|
12
14
|
ENV['RACK_ENV'] = val
|
13
|
-
|
15
|
+
PARAMS_CONFIG[:environment] = val.to_sym
|
14
16
|
end
|
15
17
|
op.on('-o addr', "set the host (default is (env == 'development' ? 'localhost' : '0.0.0.0'))") do |val|
|
16
|
-
|
18
|
+
PARAMS_CONFIG[:bind] = val
|
17
19
|
end
|
18
|
-
|
20
|
+
end
|
19
21
|
begin
|
20
22
|
parser.parse!(ARGV.dup)
|
21
|
-
rescue =>
|
22
|
-
|
23
|
+
rescue StandardError => e
|
24
|
+
PARAMS_CONFIG[:optparse_error] = e
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
require 'sinatra/base'
|
27
29
|
|
28
30
|
class Application < Base
|
29
|
-
|
30
31
|
# we assume that the first file that requires 'sinatra' is the
|
31
32
|
# app_file. all other path related options are calculated based
|
32
33
|
# on this path by default.
|
33
34
|
set :app_file, caller_files.first || $0
|
34
35
|
|
35
|
-
set :run,
|
36
|
+
set :run, proc { File.expand_path($0) == File.expand_path(app_file) }
|
36
37
|
|
37
38
|
if run? && ARGV.any?
|
38
|
-
error =
|
39
|
+
error = PARAMS_CONFIG.delete(:optparse_error)
|
39
40
|
raise error if error
|
40
|
-
|
41
|
+
|
42
|
+
PARAMS_CONFIG.each { |k, v| set k, v }
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
|
-
remove_const(:
|
46
|
+
remove_const(:PARAMS_CONFIG)
|
45
47
|
at_exit { Application.run! if $!.nil? && Application.run? }
|
46
48
|
end
|
47
49
|
|
@@ -12,6 +12,7 @@ module Sinatra
|
|
12
12
|
class ShowExceptions < Rack::ShowExceptions
|
13
13
|
@@eats_errors = Object.new
|
14
14
|
def @@eats_errors.flush(*) end
|
15
|
+
|
15
16
|
def @@eats_errors.puts(*) end
|
16
17
|
|
17
18
|
def initialize(app)
|
@@ -21,23 +22,24 @@ module Sinatra
|
|
21
22
|
def call(env)
|
22
23
|
@app.call(env)
|
23
24
|
rescue Exception => e
|
24
|
-
errors
|
25
|
+
errors = env['rack.errors']
|
26
|
+
env['rack.errors'] = @@eats_errors
|
25
27
|
|
26
28
|
if prefers_plain_text?(env)
|
27
|
-
content_type =
|
29
|
+
content_type = 'text/plain'
|
28
30
|
body = dump_exception(e)
|
29
31
|
else
|
30
|
-
content_type =
|
32
|
+
content_type = 'text/html'
|
31
33
|
body = pretty(env, e)
|
32
34
|
end
|
33
35
|
|
34
|
-
env[
|
36
|
+
env['rack.errors'] = errors
|
35
37
|
|
36
38
|
[
|
37
39
|
500,
|
38
40
|
{
|
39
|
-
|
40
|
-
|
41
|
+
'Content-Type' => content_type,
|
42
|
+
'Content-Length' => body.bytesize.to_s
|
41
43
|
},
|
42
44
|
[body]
|
43
45
|
]
|
@@ -49,27 +51,27 @@ module Sinatra
|
|
49
51
|
|
50
52
|
private
|
51
53
|
|
52
|
-
def bad_request?(
|
53
|
-
Sinatra::BadRequest ===
|
54
|
+
def bad_request?(exception)
|
55
|
+
Sinatra::BadRequest === exception
|
54
56
|
end
|
55
57
|
|
56
58
|
def prefers_plain_text?(env)
|
57
|
-
|
58
|
-
|
59
|
+
Request.new(env).preferred_type('text/plain', 'text/html') != 'text/html' &&
|
60
|
+
[/curl/].index { |item| item =~ env['HTTP_USER_AGENT'] }
|
59
61
|
end
|
60
62
|
|
61
63
|
def frame_class(frame)
|
62
64
|
if frame.filename =~ %r{lib/sinatra.*\.rb}
|
63
|
-
|
65
|
+
'framework'
|
64
66
|
elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
|
65
67
|
frame.filename =~ %r{/bin/(\w+)\z}
|
66
|
-
|
68
|
+
'system'
|
67
69
|
else
|
68
|
-
|
70
|
+
'app'
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
TEMPLATE = ERB.new <<-HTML # :nodoc:
|
74
|
+
TEMPLATE = ERB.new <<-HTML # :nodoc:
|
73
75
|
<!DOCTYPE html>
|
74
76
|
<html>
|
75
77
|
<head>
|
@@ -357,6 +359,6 @@ enabled the <code>show_exceptions</code> setting.</p>
|
|
357
359
|
</div> <!-- /WRAP -->
|
358
360
|
</body>
|
359
361
|
</html>
|
360
|
-
HTML
|
362
|
+
HTML
|
361
363
|
end
|
362
364
|
end
|
data/lib/sinatra/version.rb
CHANGED
data/lib/sinatra.rb
CHANGED
data/sinatra.gemspec
CHANGED
@@ -1,49 +1,54 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
version = File.read(File.expand_path('VERSION', __dir__)).strip
|
2
4
|
|
3
5
|
Gem::Specification.new 'sinatra', version do |s|
|
4
|
-
s.description =
|
5
|
-
s.summary =
|
6
|
-
s.authors = [
|
7
|
-
s.email =
|
8
|
-
s.homepage =
|
6
|
+
s.description = 'Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort.'
|
7
|
+
s.summary = 'Classy web-development dressed in a DSL'
|
8
|
+
s.authors = ['Blake Mizerany', 'Ryan Tomayko', 'Simon Rozet', 'Konstantin Haase']
|
9
|
+
s.email = 'sinatrarb@googlegroups.com'
|
10
|
+
s.homepage = 'http://sinatrarb.com/'
|
9
11
|
s.license = 'MIT'
|
10
12
|
s.files = Dir['README*.md', 'lib/**/*', 'examples/*'] + [
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
'.yardopts',
|
14
|
+
'AUTHORS.md',
|
15
|
+
'CHANGELOG.md',
|
16
|
+
'CONTRIBUTING.md',
|
17
|
+
'Gemfile',
|
18
|
+
'LICENSE',
|
19
|
+
'MAINTENANCE.md',
|
20
|
+
'Rakefile',
|
21
|
+
'SECURITY.md',
|
22
|
+
'sinatra.gemspec',
|
23
|
+
'VERSION'
|
24
|
+
]
|
22
25
|
s.extra_rdoc_files = %w[README.md LICENSE]
|
23
26
|
s.rdoc_options = %w[--line-numbers --title Sinatra --main README.rdoc --encoding=UTF-8]
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
'source_code_uri' => 'https://github.com/sinatra/sinatra',
|
28
|
-
'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/master/CHANGELOG.md',
|
29
|
-
'homepage_uri' => 'http://sinatrarb.com/',
|
30
|
-
'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
|
31
|
-
'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
|
32
|
-
'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
|
33
|
-
}
|
34
|
-
else
|
35
|
-
raise <<-EOF
|
28
|
+
unless s.respond_to?(:metadata)
|
29
|
+
raise <<-WARN
|
36
30
|
RubyGems 2.0 or newer is required to protect against public gem pushes. You can update your rubygems version by running:
|
37
31
|
gem install rubygems-update
|
38
32
|
update_rubygems:
|
39
33
|
gem update --system
|
40
|
-
|
34
|
+
WARN
|
41
35
|
end
|
42
36
|
|
43
|
-
s.
|
37
|
+
s.metadata = {
|
38
|
+
'source_code_uri' => 'https://github.com/sinatra/sinatra',
|
39
|
+
'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/main/CHANGELOG.md',
|
40
|
+
'homepage_uri' => 'http://sinatrarb.com/',
|
41
|
+
'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
|
42
|
+
'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
|
43
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
|
44
|
+
}
|
44
45
|
|
45
|
-
s.
|
46
|
-
|
46
|
+
s.required_ruby_version = '>= 2.6.0'
|
47
|
+
|
48
|
+
s.add_dependency 'mustermann', '~> 3.0'
|
49
|
+
s.add_dependency 'rack', '~> 2.2', '>= 2.2.4'
|
47
50
|
s.add_dependency 'rack-protection', version
|
48
|
-
s.add_dependency '
|
51
|
+
s.add_dependency 'tilt', '~> 2.0'
|
52
|
+
|
53
|
+
s.add_development_dependency 'rack-test', '~> 2'
|
49
54
|
end
|
metadata
CHANGED
@@ -1,74 +1,94 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blake Mizerany
|
8
8
|
- Ryan Tomayko
|
9
9
|
- Simon Rozet
|
10
10
|
- Konstantin Haase
|
11
|
-
autorequire:
|
11
|
+
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2023-08-07 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
17
|
+
name: mustermann
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
20
|
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '3.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '3.0'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: rack
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
34
|
- - "~>"
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: '2.
|
36
|
+
version: '2.2'
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.2.4
|
37
40
|
type: :runtime
|
38
41
|
prerelease: false
|
39
42
|
version_requirements: !ruby/object:Gem::Requirement
|
40
43
|
requirements:
|
41
44
|
- - "~>"
|
42
45
|
- !ruby/object:Gem::Version
|
43
|
-
version: '2.
|
46
|
+
version: '2.2'
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 2.2.4
|
44
50
|
- !ruby/object:Gem::Dependency
|
45
51
|
name: rack-protection
|
46
52
|
requirement: !ruby/object:Gem::Requirement
|
47
53
|
requirements:
|
48
54
|
- - '='
|
49
55
|
- !ruby/object:Gem::Version
|
50
|
-
version:
|
56
|
+
version: 3.1.0
|
51
57
|
type: :runtime
|
52
58
|
prerelease: false
|
53
59
|
version_requirements: !ruby/object:Gem::Requirement
|
54
60
|
requirements:
|
55
61
|
- - '='
|
56
62
|
- !ruby/object:Gem::Version
|
57
|
-
version:
|
63
|
+
version: 3.1.0
|
58
64
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
65
|
+
name: tilt
|
60
66
|
requirement: !ruby/object:Gem::Requirement
|
61
67
|
requirements:
|
62
68
|
- - "~>"
|
63
69
|
- !ruby/object:Gem::Version
|
64
|
-
version: '
|
70
|
+
version: '2.0'
|
65
71
|
type: :runtime
|
66
72
|
prerelease: false
|
67
73
|
version_requirements: !ruby/object:Gem::Requirement
|
68
74
|
requirements:
|
69
75
|
- - "~>"
|
70
76
|
- !ruby/object:Gem::Version
|
71
|
-
version: '
|
77
|
+
version: '2.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rack-test
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - "~>"
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '2'
|
85
|
+
type: :development
|
86
|
+
prerelease: false
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '2'
|
72
92
|
description: Sinatra is a DSL for quickly creating web applications in Ruby with minimal
|
73
93
|
effort.
|
74
94
|
email: sinatrarb@googlegroups.com
|
@@ -85,22 +105,12 @@ files:
|
|
85
105
|
- Gemfile
|
86
106
|
- LICENSE
|
87
107
|
- MAINTENANCE.md
|
88
|
-
- README.de.md
|
89
|
-
- README.es.md
|
90
|
-
- README.fr.md
|
91
|
-
- README.hu.md
|
92
|
-
- README.ja.md
|
93
|
-
- README.ko.md
|
94
|
-
- README.malayalam.md
|
95
108
|
- README.md
|
96
|
-
- README.pt-br.md
|
97
|
-
- README.pt-pt.md
|
98
|
-
- README.ru.md
|
99
|
-
- README.zh.md
|
100
109
|
- Rakefile
|
101
110
|
- SECURITY.md
|
102
111
|
- VERSION
|
103
112
|
- examples/chat.rb
|
113
|
+
- examples/lifecycle_events.rb
|
104
114
|
- examples/rainbows.conf
|
105
115
|
- examples/rainbows.rb
|
106
116
|
- examples/simple.rb
|
@@ -119,12 +129,12 @@ licenses:
|
|
119
129
|
- MIT
|
120
130
|
metadata:
|
121
131
|
source_code_uri: https://github.com/sinatra/sinatra
|
122
|
-
changelog_uri: https://github.com/sinatra/sinatra/blob/
|
132
|
+
changelog_uri: https://github.com/sinatra/sinatra/blob/main/CHANGELOG.md
|
123
133
|
homepage_uri: http://sinatrarb.com/
|
124
134
|
bug_tracker_uri: https://github.com/sinatra/sinatra/issues
|
125
135
|
mailing_list_uri: http://groups.google.com/group/sinatrarb
|
126
136
|
documentation_uri: https://www.rubydoc.info/gems/sinatra
|
127
|
-
post_install_message:
|
137
|
+
post_install_message:
|
128
138
|
rdoc_options:
|
129
139
|
- "--line-numbers"
|
130
140
|
- "--title"
|
@@ -138,15 +148,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
148
|
requirements:
|
139
149
|
- - ">="
|
140
150
|
- !ruby/object:Gem::Version
|
141
|
-
version: 2.
|
151
|
+
version: 2.6.0
|
142
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
153
|
requirements:
|
144
154
|
- - ">="
|
145
155
|
- !ruby/object:Gem::Version
|
146
156
|
version: '0'
|
147
157
|
requirements: []
|
148
|
-
rubygems_version: 3.
|
149
|
-
signing_key:
|
158
|
+
rubygems_version: 3.4.18
|
159
|
+
signing_key:
|
150
160
|
specification_version: 4
|
151
161
|
summary: Classy web-development dressed in a DSL
|
152
162
|
test_files: []
|