wunderbar 1.3.0 → 1.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7f6f11a34c8540709e572a829025fd0d28e8522d9882d336e842caafe91c9f2
4
- data.tar.gz: 57a8d1338c347dacc8beea0ffbf5de77181f88cb5f472ce6078a4e18ae14a08c
3
+ metadata.gz: 01faf4f44e9f567cf1ab080f8f4a54879c0eb6b5d07e0ad6d265ede2cbd4b86f
4
+ data.tar.gz: a0db8a63944a68305a337a1eb5cef8114f5f88034e7a9c451cd37228ad97bc40
5
5
  SHA512:
6
- metadata.gz: 59c38775e59733f8255c29f251cdd6770084517913c0b10fceb3eb809301ddf4487ba01c6b831109fd5d516fdb816ef3ba64729da86b1a0c29d3f5f3e1cd9d82
7
- data.tar.gz: b0b57ca9a258115bb3eb1edb4aaf2df2eee16cbb0229e20f497f70734741e116c21fc045a55fe6bc9e1b04f8f87acd29625f50c061dcdf30840b889435ffdce9
6
+ metadata.gz: d104318bd2e0e2ee9cd721c3b49790ede15b4459dbe253bc58b9108b6fc91fcb077e932a399b9194417a5dc31c62500684db424db534b4b72c1c709e2defedc1
7
+ data.tar.gz: e2d9ca6b2bec1fdb12a253e1ca9df2f73296388c60ee2b165c63305216a353fc0fe37e3ede10c48b6524b7391e060230ca53fa8f393b4d24b957f225da3363a2
data/README.md CHANGED
@@ -366,26 +366,7 @@ Secure by default
366
366
 
367
367
  Wunderbar will properly escape all HTML and JSON output, eliminating problems
368
368
  of HTML or JavaScript injection. This includes calls to `_` to insert text
369
- directly. Unless `nokogiri` was previously required (see [optional
370
- dependencies](#optional-dependencies) below), calls to insert markup
371
- (`_{...}`) will escape the markup if the input is `tainted` and not explicitly
372
- marked as `html-safe?` (when using Rails).
373
-
374
- For Ruby version < 2.6.0:
375
-
376
- > For all environments other than Rails, unless you call `Wunderbar.unsafe!` at
377
- > the top of your script, Wunderbar will also set
378
- > [`$SAFE=1`](http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html)
379
- > before processing requests. This means that you will need to
380
- > [`untaint`](ruby-doc.org/core/Object.html#method-i-untaint) all inputs
381
- > received from external sources before you make system calls or access the file
382
- > system.
383
-
384
- A special feature that effectively is only available in the Rails environment:
385
- if the first argument to call that creates an element is `html_safe?`, then
386
- that argument will be treated as a markup instead of as text. This allows one
387
- to make calls like `_td link_to...` without placing the call to `link_to` in a
388
- block.
369
+ directly.
389
370
 
390
371
  Globals provided
391
372
  ---
@@ -454,7 +435,6 @@ The following gems, if installed, will produce cleaner and prettier output:
454
435
  * `nokogumbo` also cleans up HTML fragments inserted via `<<` and `_{}`. If
455
436
  this gem is available, it will be preferred over direct usage of `nokogiri`.
456
437
  * `escape` prettier quoting of `system` commands
457
- * `sanitize` will remove unsafe markup from tainted input
458
438
 
459
439
  Related efforts
460
440
  ---
@@ -30,14 +30,14 @@ module Wunderbar
30
30
  return @path if @path or @contents
31
31
 
32
32
  if @options[:name]
33
- source = (@options[:file] || __FILE__).untaint
33
+ source = @options[:file] || __FILE__
34
34
  @mtime = File.mtime(source)
35
35
  @path = @options[:name]
36
36
 
37
37
  # look for asset in site
38
38
  if ENV['DOCUMENT_ROOT']
39
- root = File.join(ENV['DOCUMENT_ROOT'], 'assets').untaint
40
- dest = File.expand_path(@path, root).untaint
39
+ root = File.join(ENV['DOCUMENT_ROOT'], 'assets')
40
+ dest = File.expand_path(@path, root)
41
41
  if File.exist?(dest) and File.size(dest) == File.size(source)
42
42
  @path = "/assets/#{@path}"
43
43
  return @path
@@ -45,7 +45,7 @@ module Wunderbar
45
45
  end
46
46
 
47
47
  # look for asset in app
48
- dest = File.expand_path(@path, Asset.root).untaint
48
+ dest = File.expand_path(@path, Asset.root)
49
49
  if File.exist?(dest) and File.size(dest) == File.size(source)
50
50
  return @path
51
51
  end
@@ -103,7 +103,7 @@ module Wunderbar
103
103
  @path = '../' * env['PATH_INFO'].to_s.count('/') + 'assets'
104
104
  @root ||= nil
105
105
  @root = File.dirname(env['SCRIPT_FILENAME']) if env['SCRIPT_FILENAME']
106
- @root = File.expand_path((@root || Dir.pwd) + "/assets").untaint
106
+ @root = File.expand_path((@root || Dir.pwd) + "/assets")
107
107
 
108
108
  # Options: typically :name plus either :file or :contents
109
109
  # :name => name to be used for the asset
@@ -51,14 +51,14 @@ module Wunderbar
51
51
  secret = command - flat
52
52
  begin
53
53
  # if available, use escape as it does prettier quoting
54
- raise LoadError if $SAFE > 0 and not defined? Escape
54
+ raise LoadError if not defined? Escape
55
55
  require 'escape'
56
56
  echo = Escape.shell_command(command.compact - secret)
57
57
  rescue LoadError
58
58
  # std-lib function that gets the job done
59
59
  echo = Shellwords.join(command.compact - secret)
60
60
  end
61
- command = flat.compact.map(&:dup).map(&:untaint)
61
+ command = flat.compact
62
62
  else
63
63
  echo = command
64
64
  command = [command]
@@ -461,8 +461,14 @@ module Wunderbar
461
461
  output_prefix = opts[:prefix] || {}
462
462
  output_prefix[:stdin] ||= '$ '
463
463
 
464
- super do |kind, line|
465
- @_target.puts "#{output_prefix[kind]}#{line}"
464
+ if Hash === args.last # support original code which needed two hashes
465
+ super do |kind, line|
466
+ @_target.puts "#{output_prefix[kind]}#{line}"
467
+ end
468
+ else
469
+ super(*args, opts) do |kind, line|
470
+ @_target.puts "#{output_prefix[kind]}#{line}"
471
+ end
466
472
  end
467
473
  end
468
474
 
@@ -587,8 +593,14 @@ module Wunderbar
587
593
  @_target[transcript] = []
588
594
  end
589
595
 
590
- super do |kind, line|
591
- @_target[transcript] << "#{output_prefix[kind]}#{line}"
596
+ if Hash === args.last # support original code which needed two hashes
597
+ super do |kind, line|
598
+ @_target[transcript] << "#{output_prefix[kind]}#{line}"
599
+ end
600
+ else
601
+ super(*args, opts) do |kind, line|
602
+ @_target[transcript] << "#{output_prefix[kind]}#{line}"
603
+ end
592
604
  end
593
605
  end
594
606
 
@@ -120,7 +120,7 @@ module Wunderbar
120
120
  # asset support for Rack
121
121
  request = (scope.respond_to? :request) ? scope.request : nil
122
122
  if request and request.path =~ %r{/assets/\w[-.\w]+}
123
- path = ('.' + scope.request.path).untaint
123
+ path = '.' + scope.request.path
124
124
  headers = {'type' => 'text/plain'}
125
125
  headers['type'] = 'application/javascript' if path =~ /\.js$/
126
126
  out?(scope, headers) { File.read path if File.exist? path }
@@ -2,15 +2,6 @@ require 'wunderbar'
2
2
  require 'coderay'
3
3
  require 'nokogiri'
4
4
 
5
- # workaround for https://github.com/rubychan/coderay/pull/159
6
- module CodeRay::PluginHost
7
- alias_method :old_plugin_path, :plugin_path
8
- def plugin_path *args
9
- args.first.untaint if args.first == CodeRay::CODERAY_PATH
10
- old_plugin_path(*args)
11
- end
12
- end
13
-
14
5
  module Wunderbar
15
6
  class HtmlMarkup
16
7
  def _coderay(*args)
@@ -5,40 +5,6 @@ module Wunderbar
5
5
  TEXT = ARGV.delete('--text')
6
6
  end
7
7
 
8
- # Ruby 2.6.0 gets rid of $SAFE > 1; unfortunately in the process it
9
- # treats $SAFE = 1 as a higher level; @FAFE = 1 no longer is limited
10
- # to taintness checks, it not treats all File operations as unsafe
11
- @@unsafe = (RUBY_VERSION.split('.').map(&:to_i) <=> [2, 6, 0]) == 1
12
-
13
- def self.unsafe!(mode=true)
14
- @@unsafe=mode
15
- end
16
-
17
- def self.safe?
18
- if $SAFE == 0 and not @@unsafe
19
- # some gems (e.g. em-websocket-0.3.6) insert unsafe entries into the
20
- # path, and that prevents requires from succeeding. If it looks like
21
- # we are about to make a transition to $SAFE=1, clean up that mess
22
- # before proceeding.
23
- #
24
- # the goal of $SAFE is not to protect us against software which was
25
- # installed by the owner of the site, but from injection attacks
26
- # contained within data provided by users of the site.
27
- $:.each_with_index do |path, index|
28
- if path.tainted?
29
- $:[index] = File.expand_path(path.dup.untaint).untaint
30
- end
31
- end
32
-
33
- # avoid: "Insecure PATH - (SecurityError)" when using Bundler
34
- if defined? Bundler
35
- ENV['PATH'] = ENV['PATH'].dup.untaint
36
- end
37
- end
38
-
39
- not @@unsafe
40
- end
41
-
42
8
  class Scope
43
9
  attr_accessor :env
44
10
  def initialize(env)
@@ -47,11 +13,16 @@ module Wunderbar
47
13
  end
48
14
 
49
15
  @@templates = {}
16
+ @@files = {}
50
17
 
51
18
  def self.templates
52
19
  @@templates
53
20
  end
54
21
 
22
+ def self.files
23
+ @@files
24
+ end
25
+
55
26
  module API
56
27
  def _html(*args, &block)
57
28
  Wunderbar.html(*args, &block)
@@ -77,6 +48,11 @@ module Wunderbar
77
48
  def _template(name, &block)
78
49
  Wunderbar.templates[name.to_s.gsub('_','-')] = block
79
50
  end
51
+
52
+ def _file(name, options={}, &block)
53
+ options[:source] = block if block
54
+ Wunderbar.files[name] = options
55
+ end
80
56
  end
81
57
 
82
58
  #
@@ -133,7 +133,7 @@ module Wunderbar
133
133
  # * Proxied Rack server. Document base may be relate to the
134
134
  # HTTP_X_WUNDERBAR_BASE
135
135
  #
136
- cwd = File.realpath(Dir.pwd.untaint)
136
+ cwd = File.realpath(Dir.pwd)
137
137
  base = @_scope.env['DOCUMENT_ROOT'] if @_scope.env.respond_to? :[]
138
138
  base ||= cwd
139
139
  href = (head.children[1].attrs[:href] || '')
@@ -189,8 +189,8 @@ module Wunderbar
189
189
  name = name.to_s.gsub('_', '-')
190
190
 
191
191
  if flag != '!'
192
- if String === args.first and args.first.respond_to? :html_safe?
193
- if args.first.html_safe? and not block and args.first =~ /[>&]/
192
+ if String === args.first
193
+ if not block and args.first =~ /[>&]/
194
194
  markup = args.shift
195
195
  block = Proc.new {_ {markup}}
196
196
  end
@@ -362,11 +362,7 @@ module Wunderbar
362
362
  def _(text=nil, &block)
363
363
  unless block
364
364
  if text
365
- if text.respond_to? :html_safe? and text.html_safe?
366
- _ {text}
367
- else
368
- @_x.indented_text! text.to_s
369
- end
365
+ _ {text}
370
366
  end
371
367
  return @_x
372
368
  end
@@ -374,20 +370,13 @@ module Wunderbar
374
370
  children = instance_eval(&block)
375
371
 
376
372
  if String === children
377
- safe = !children.tainted?
378
- safe ||= children.html_safe? if children.respond_to? :html_safe?
379
- safe &&= defined? Nokogiri
380
- ok = safe || defined? Sanitize
381
- safe = true
382
-
383
- if ok and (children.include? '<' or children.include? '&')
373
+ if children.include? '<' or children.include? '&'
384
374
  if defined? Nokogiri::HTML5.fragment
385
375
  doc = Nokogiri::HTML5.fragment(children.to_s)
386
376
  else
387
377
  doc = Nokogiri::HTML.fragment(children.to_s)
388
378
  end
389
379
 
390
- Sanitize.new.clean_node! doc.dup.untaint if not safe
391
380
  children = doc.children.to_a
392
381
 
393
382
  # ignore leading whitespace
@@ -25,7 +25,7 @@ module Wunderbar
25
25
  # clear environment of cgi cruft
26
26
  require 'cgi'
27
27
  ENV.keys.select {|key| key =~ /^HTTP_/}.each do |key|
28
- ENV.delete key.dup.untaint
28
+ ENV.delete key
29
29
  end
30
30
  ::CGI::QueryExtension.public_instance_methods.each do |method|
31
31
  ENV.delete method.to_s.upcase
@@ -1,5 +1,6 @@
1
1
  require 'wunderbar'
2
2
  require 'rack'
3
+ require 'rack/media_type'
3
4
 
4
5
  module Wunderbar
5
6
  class RackApp
@@ -9,8 +10,13 @@ module Wunderbar
9
10
  @_request = Rack::Request.new(env)
10
11
  @_response = Rack::Response.new
11
12
  Wunderbar.logger = @_request.logger
12
- if Wunderbar.safe? and $SAFE==0
13
- Proc.new { $SAFE=1; Wunderbar::CGI.call(self) }.call
13
+ file = Wunderbar.files[env['PATH_INFO']]
14
+
15
+ if file
16
+ mime = file[:mime] ||
17
+ Rack::Mime::MIME_TYPES[File.extname(env['PATH_INFO'])]
18
+ @_response.set_header('Content-Type', mime) if mime
19
+ @_response.write(file[:content] || file[:source].call)
14
20
  else
15
21
  Wunderbar::CGI.call(self)
16
22
  end
@@ -43,6 +49,12 @@ module Wunderbar
43
49
  def response
44
50
  @_response
45
51
  end
52
+
53
+ %w(delete get head options post put trace).each do |http_method|
54
+ define_method "#{http_method}?" do
55
+ @_env['REQUEST_METHOD'].to_s.downcase == http_method
56
+ end
57
+ end
46
58
  end
47
59
  end
48
60
 
@@ -7,7 +7,7 @@ module Wunderbar
7
7
  cattr_accessor :default_format
8
8
  self.default_format = Mime[:html]
9
9
 
10
- def self.call(template)
10
+ def self.call(template, source=nil)
11
11
  %{
12
12
  compiled = Proc.new {#{template.source}}
13
13
  x = Wunderbar::HtmlMarkup.new(self);
@@ -24,7 +24,7 @@ module Wunderbar
24
24
  cattr_accessor :default_format
25
25
  self.default_format = Mime[:json]
26
26
 
27
- def self.call(template)
27
+ def self.call(template, source=nil)
28
28
  %{
29
29
  compiled = Proc.new {#{template.source}}
30
30
  x = Wunderbar::JsonBuilder.new(self);
@@ -16,6 +16,12 @@ class Wunderbar::Render
16
16
  "ReactDOMServer.renderToString(#{common})"
17
17
  end
18
18
 
19
+ # return all nodes on server rendering, as there is no wrapper element
20
+ # like there is for vue
21
+ def self.extract(nodes)
22
+ nodes
23
+ end
24
+
19
25
  def self.client(common, element, target)
20
26
  "ReactDOM.render(#{common}, #{element})"
21
27
  end
@@ -29,17 +35,3 @@ class Wunderbar::Render
29
35
  "</pre>"
30
36
  end
31
37
  end
32
-
33
- # Monkeypatch to address https://github.com/sstephenson/execjs/pull/180
34
- require 'execjs'
35
- class ExecJS::ExternalRuntime::Context
36
- alias_method :w_write_to_tempfile, :write_to_tempfile
37
- def write_to_tempfile(*args)
38
- tmpfile = w_write_to_tempfile(*args).path.untaint
39
- tmpfile = Struct.new(:path, :to_str).new(tmpfile, tmpfile)
40
- def tmpfile.unlink
41
- File.unlink path
42
- end
43
- tmpfile
44
- end
45
- end
@@ -82,14 +82,12 @@ class Wunderbar::XmlMarkup
82
82
 
83
83
  src = File.join(base, src) if not base.empty?
84
84
  src = src.sub(/\?.*$/, '') # strip queries (typically mtimes)
85
- src.untaint
86
85
 
87
- name = File.expand_path(src, @_scope.settings.public_folder.untaint)
88
- name.untaint unless src.tainted?
86
+ name = File.expand_path(src, @_scope.settings.public_folder)
89
87
  if File.exist? name
90
88
  result = File.read(name)
91
89
  else
92
- file = File.expand_path(src+'.rb', @_scope.settings.views.untaint)
90
+ file = File.expand_path(src+'.rb', @_scope.settings.views)
93
91
  result = Wunderbar::Asset.convert(file)
94
92
  end
95
93
  else
@@ -113,11 +111,11 @@ class Wunderbar::XmlMarkup
113
111
  scripts.unshift script.contents
114
112
  elsif script.path
115
113
  if script.path.start_with? '/'
116
- path = (ENV['DOCUMENT_ROOT'] + script.path).untaint
114
+ path = ENV['DOCUMENT_ROOT'] + script.path
117
115
  else
118
- path = File.expand_path(script.path, Wunderbar::Asset.root).untaint
116
+ path = File.expand_path(script.path, Wunderbar::Asset.root)
119
117
  end
120
- setup << File.read(script.options[:server].untaint || path)
118
+ setup << File.read(script.options[:server] || path)
121
119
  end
122
120
  end
123
121
 
@@ -131,7 +129,7 @@ class Wunderbar::XmlMarkup
131
129
  html = Wunderbar::Render.eval(scripts, server)
132
130
 
133
131
  # insert results into target
134
- nodes = builder._ { html }
132
+ nodes = Wunderbar::Render.extract(builder._ { html })
135
133
 
136
134
  begin
137
135
  if nodes.length == 1
@@ -82,22 +82,14 @@ module Wunderbar
82
82
  builder.set_variables_from_params(locals)
83
83
 
84
84
  if not block
85
- builder.instance_eval(data.untaint, eval_file)
85
+ builder.instance_eval(data, eval_file)
86
86
  elsif not data
87
87
  builder.instance_eval(&block)
88
88
  else
89
89
  context = builder.get_binding do
90
90
  builder.instance_eval {_(&block)}
91
91
  end
92
- context.eval(data.untaint, eval_file)
93
- end
94
- end
95
-
96
- def _evaluate_safely(*args, &block)
97
- if Wunderbar.safe? and $SAFE==0
98
- Proc.new { $SAFE=1; _evaluate(*args, &block) }.call
99
- else
100
- _evaluate(*args, &block)
92
+ context.eval(data, eval_file)
101
93
  end
102
94
  end
103
95
  end
@@ -108,7 +100,7 @@ module Wunderbar
108
100
  def evaluate(scope, locals, &block)
109
101
  builder = HtmlMarkup.new(scope)
110
102
  begin
111
- _evaluate_safely(builder, scope, locals, &block)
103
+ _evaluate(builder, scope, locals, &block)
112
104
  rescue Exception => exception
113
105
  scope.response.status = Wunderbar::ServerError.status
114
106
  builder.clear!
@@ -132,7 +124,7 @@ module Wunderbar
132
124
  def evaluate(scope, locals, &block)
133
125
  builder = JsonBuilder.new(scope)
134
126
  begin
135
- result = _evaluate_safely(builder, scope, locals, &block)
127
+ result = _evaluate(builder, scope, locals, &block)
136
128
 
137
129
  # if no output was produced, use the result
138
130
  builder._! result if builder.target? == {} and result
@@ -154,7 +146,7 @@ module Wunderbar
154
146
  def evaluate(scope, locals, &block)
155
147
  builder = TextBuilder.new(scope)
156
148
  begin
157
- result = _evaluate_safely(builder, scope, locals, &block)
149
+ result = _evaluate(builder, scope, locals, &block)
158
150
 
159
151
  # if no output was produced, use the result
160
152
  builder._ result.to_s if builder.target!.empty? and result
@@ -240,13 +232,12 @@ Tilt.register 'xhtml.rb', Wunderbar::Template::Xhtml
240
232
  helpers Wunderbar::SinatraHelpers
241
233
 
242
234
  if Dir.exist? settings.public_folder
243
- Wunderbar::Asset.root = File.join(settings.public_folder, 'assets').untaint
235
+ Wunderbar::Asset.root = File.join(settings.public_folder, 'assets')
244
236
  end
245
237
 
246
238
  Wunderbar::Asset.virtual = true
247
239
 
248
240
  get "/#{Wunderbar::Asset.path}/:name" do |name|
249
- name.untaint if name =~ /^([-\w]\.?)+$/
250
241
  file = "#{Wunderbar::Asset.root}/#{name}"
251
242
  _text do
252
243
  if File.exist? file
@@ -1,7 +1,7 @@
1
1
  module Wunderbar
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 3
4
+ MINOR = 4
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -14,17 +14,30 @@ class Wunderbar::Render
14
14
  path = `which nodejs`.chomp
15
15
  path = `which node`.chomp if path.empty?
16
16
  raise RuntimeError.new('Unable to locate nodejs') if path.empty?
17
- @nodejs = path.untaint
17
+ @nodejs = path
18
18
  end
19
19
 
20
20
  def self.server(common)
21
- "VueServer.renderToString(new Vue({render: function($h) {return #{common}}}))"
21
+ "VueServer.renderToString(new Vue({render: function($h) {
22
+ return $h('div', #{common})}}))"
23
+ end
24
+
25
+ # unwrap children from div wrapper inserted by self.server
26
+ def self.extract(nodes)
27
+ if
28
+ nodes.length == 1 and nodes.first.name == 'div' and
29
+ nodes.first.attrs['data-server-rendered'].to_s == 'true'
30
+ then
31
+ nodes.first.children
32
+ else
33
+ nodes
34
+ end
22
35
  end
23
36
 
24
37
  def self.client(common, element, target)
25
38
  wrap = "$h(#{target.name.inspect}, " +
26
39
  "{attrs: {#{target.attrs.map {|name, value|
27
- "#{name}: #{value.inspect}"}.join(', ')}}}, [#{common}])"
40
+ "#{name}: #{value.inspect}"}.join(', ')}}}, #{common})"
28
41
  "new Vue({el: #{element}, render: function($h) {return #{wrap}}})"
29
42
  end
30
43
 
@@ -37,7 +50,7 @@ class Wunderbar::Render
37
50
  stdout += "\n<pre>#{CGI.escapeHTML(stderr)}</pre>"
38
51
  end
39
52
 
40
- stdout.untaint
53
+ stdout
41
54
  rescue => e
42
55
  Wunderbar.error e
43
56
  "<pre>#{CGI.escapeHTML(e.message)}</pre>"
@@ -1,21 +1,23 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: wunderbar 1.3.0 ruby lib
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require 'wunderbar/version'
3
5
 
4
6
  Gem::Specification.new do |s|
5
7
  s.name = "wunderbar".freeze
6
- s.version = "1.3.0"
8
+ s.version = Wunderbar::VERSION::STRING
7
9
 
8
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
11
  s.require_paths = ["lib".freeze]
10
12
  s.authors = ["Sam Ruby".freeze]
11
- s.date = "2019-03-06"
13
+ s.date = "2019-06-07"
12
14
  s.description = " Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode\n (utf-8), consistently indented, readable applications.'\n".freeze
13
15
  s.email = "rubys@intertwingly.net".freeze
14
16
  s.files = ["COPYING".freeze, "README.md".freeze, "lib/wunderbar".freeze, "lib/wunderbar.rb".freeze, "lib/wunderbar/asset.rb".freeze, "lib/wunderbar/backtick.rb".freeze, "lib/wunderbar/bootstrap".freeze, "lib/wunderbar/bootstrap.rb".freeze, "lib/wunderbar/bootstrap/theme.rb".freeze, "lib/wunderbar/builder.rb".freeze, "lib/wunderbar/cgi-methods.rb".freeze, "lib/wunderbar/coderay.rb".freeze, "lib/wunderbar/coffeescript.rb".freeze, "lib/wunderbar/cssproxy.rb".freeze, "lib/wunderbar/environment.rb".freeze, "lib/wunderbar/eventsource.rb".freeze, "lib/wunderbar/html-methods.rb".freeze, "lib/wunderbar/installation.rb".freeze, "lib/wunderbar/job-control.rb".freeze, "lib/wunderbar/jquery".freeze, "lib/wunderbar/jquery.rb".freeze, "lib/wunderbar/jquery/filter.rb".freeze, "lib/wunderbar/jquery/stupidtable.rb".freeze, "lib/wunderbar/listen.rb".freeze, "lib/wunderbar/logger.rb".freeze, "lib/wunderbar/markdown.rb".freeze, "lib/wunderbar/marked.rb".freeze, "lib/wunderbar/node.rb".freeze, "lib/wunderbar/pagedown.rb".freeze, "lib/wunderbar/polymer.rb".freeze, "lib/wunderbar/rack.rb".freeze, "lib/wunderbar/rails.rb".freeze, "lib/wunderbar/react.rb".freeze, "lib/wunderbar/render.rb".freeze, "lib/wunderbar/script.rb".freeze, "lib/wunderbar/server.rb".freeze, "lib/wunderbar/sinatra.rb".freeze, "lib/wunderbar/underscore.rb".freeze, "lib/wunderbar/vendor".freeze, "lib/wunderbar/vendor/Markdown.Converter.js".freeze, "lib/wunderbar/vendor/bootstrap-theme.min.css".freeze, "lib/wunderbar/vendor/bootstrap.min.css".freeze, "lib/wunderbar/vendor/bootstrap.min.js".freeze, "lib/wunderbar/vendor/eventsource.min.js".freeze, "lib/wunderbar/vendor/jquery-3.2.1.min.js".freeze, "lib/wunderbar/vendor/marked.min.js".freeze, "lib/wunderbar/vendor/polymer-v0.0.20131003.min.js".freeze, "lib/wunderbar/vendor/react-dom-server.min.js".freeze, "lib/wunderbar/vendor/react-dom.min.js".freeze, "lib/wunderbar/vendor/react-with-addons.min.js".freeze, "lib/wunderbar/vendor/stupidtable.min.js".freeze, "lib/wunderbar/vendor/underscore-min.js".freeze, "lib/wunderbar/vendor/vue-server.min.js".freeze, "lib/wunderbar/vendor/vue.min.js".freeze, "lib/wunderbar/version.rb".freeze, "lib/wunderbar/vue.rb".freeze, "lib/wunderbar/websocket.rb".freeze, "wunderbar.gemspec".freeze]
15
17
  s.homepage = "http://github.com/rubys/wunderbar".freeze
16
18
  s.licenses = ["MIT".freeze]
17
19
  s.required_ruby_version = Gem::Requirement.new(">= 1.9.3".freeze)
18
- s.rubygems_version = "2.7.6".freeze
20
+ s.rubygems_version = "3.0.6".freeze
19
21
  s.summary = "HTML Generator and CGI application support".freeze
20
22
 
21
23
  if s.respond_to? :specification_version then
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wunderbar
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-06 00:00:00.000000000 Z
11
+ date: 2019-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -105,8 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  - !ruby/object:Gem::Version
106
106
  version: '0'
107
107
  requirements: []
108
- rubyforge_project:
109
- rubygems_version: 2.7.6
108
+ rubygems_version: 3.1.2
110
109
  signing_key:
111
110
  specification_version: 4
112
111
  summary: HTML Generator and CGI application support