roda-sprockets 1.0.2 → 2.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +32 -0
  3. data/.gitignore +2 -1
  4. data/.rspec +1 -0
  5. data/CHANGELOG.md +29 -0
  6. data/Gemfile +13 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +69 -15
  9. data/Rakefile +4 -0
  10. data/examples/opal-doc/Gemfile +6 -0
  11. data/examples/opal-doc/README.md +4 -0
  12. data/examples/opal-doc/Rakefile +6 -0
  13. data/examples/opal-doc/app.rb +25 -0
  14. data/examples/opal-doc/app/application.rb +3 -0
  15. data/examples/opal-doc/config.ru +3 -0
  16. data/examples/opal-exception/Gemfile +8 -0
  17. data/examples/opal-exception/README.md +1 -0
  18. data/examples/opal-exception/Rakefile +6 -0
  19. data/examples/opal-exception/app.rb +26 -0
  20. data/examples/opal-exception/app/application.rb +16 -0
  21. data/examples/opal-exception/config.ru +3 -0
  22. data/examples/sass/Gemfile +6 -0
  23. data/examples/sass/Rakefile +6 -0
  24. data/examples/sass/app.rb +23 -0
  25. data/examples/sass/assets/css/application.scss +3 -0
  26. data/examples/sass/config.ru +3 -0
  27. data/lib/roda/plugins/sprockets.rb +26 -15
  28. data/lib/roda/plugins/sprockets_task.rb +30 -6
  29. data/roda-sprockets.gemspec +1 -1
  30. data/spec/assets/css/scss_css.scss +7 -0
  31. data/spec/assets/css/scss_css_with_erb.scss.erb +13 -0
  32. data/spec/assets/css/scss_css_with_imports.scss +1 -0
  33. data/spec/assets/css/simple_css.css +1 -0
  34. data/spec/assets/js/opal_js.rb +4 -0
  35. data/spec/assets/js/opal_js_with_erb.rb.erb +10 -0
  36. data/spec/assets/js/opal_js_with_requires.rb +1 -0
  37. data/spec/assets/js/simple_js.js +1 -0
  38. data/spec/examples_spec.rb +141 -0
  39. data/spec/plugin_spec.rb +237 -0
  40. data/spec/server_helper.rb +68 -0
  41. data/spec/spec_helper.rb +59 -0
  42. metadata +49 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64c7457f4f6cca73af4aede33d7fbbe2922aac43a05a399af1658a2fa54250ec
4
- data.tar.gz: d14d895b6704ae71b20958df0e3d0fa054054b25862b529d6b94b15a6acbbb3d
3
+ metadata.gz: 508fedb44f1daee79f32b2ef1746fb5a266aa771465dc44b42a9d2de03787d6a
4
+ data.tar.gz: 15678eedb2c7d2aa7d5e8c1136dd2b16c77d4336514e689875488a7ec593e934
5
5
  SHA512:
6
- metadata.gz: 4e2fa406bc9c3d22c508e683bbe65e5424361b3cf73c6c10e09f470c2fe43bd288789a6c11df8d5c3deea7d3f28ece35cfc609f6942cbe2046309384aae05535
7
- data.tar.gz: 565f7f00f965f808cde41a95c9c1309cbfea91068f4f9090760b0d47e58bef41966855836c32f8981db72322f899aee32d109de046d3327d47a378330b330b92
6
+ metadata.gz: eac697f7e01b05019e19a48704088910ceb1451ad2d6e40dccfc3ee8d8c67ef3b301d61876ca6089b4ce8454f19a1b02afbe3065abd812fdd804017188ddd838
7
+ data.tar.gz: c25b68ac51d50b091f398a5d98780f15987c20643535e289ed034d60929229ca0af556545dd6268981c83921dd763080c1a572306e5ef009161c37c20b57716c
@@ -0,0 +1,32 @@
1
+ name: rake
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ - "*-stable"
8
+ - "*/ci-check"
9
+ pull_request: {}
10
+
11
+ jobs:
12
+ rspec:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ ruby-version: ['2.6', '2.7', '3.0']
17
+ os: ['ubuntu-latest', 'windows-latest', 'macos-latest']
18
+
19
+ runs-on: ${{ matrix.os }}
20
+
21
+ env:
22
+ CI: 'true'
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby-version }}
30
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
31
+ - name: Run tests
32
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -1,12 +1,13 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
3
+ Gemfile.lock
4
4
  /_yardoc/
5
5
  /coverage/
6
6
  /doc/
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /vendor/
10
11
  *.bundle
11
12
  *.so
12
13
  *.o
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ v2.0.0
2
+
3
+ - :debug argument now defaults to true, if RACK_ENV is development, false
4
+ otherwise - also false in rake tasks (#10)
5
+ - add sprockets_env class method, so you can shorten code like
6
+ `App.sprockets_options[:sprockets]` to just `App.sprockets_env` (#11)
7
+
8
+ v1.1.0
9
+
10
+ - Fix the Rack::Lint issue (#1)
11
+ - :public_path argument to receive a default value (#7)
12
+ - Roda::RodaPlugins::Sprockets::Task to superclass SprocketsTask (#6)
13
+ - The debug logic now depends on :debug argument, not environment (#4)
14
+
15
+ v1.0.2
16
+
17
+ - :root argument to select a default argument more robustly (#2)
18
+
19
+ v1.0.1
20
+
21
+ - New :cache argument to plugin configuration
22
+
23
+ v1.0.0
24
+
25
+ - Sprockets 4.0 support
26
+
27
+ v0.0.11
28
+
29
+ - Support polyinstantiation, improve Opal support
data/Gemfile CHANGED
@@ -2,3 +2,16 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in roda-sprocket_assets.gemspec
4
4
  gemspec
5
+
6
+ # For rspec:
7
+ gem "rspec"
8
+ gem "opal"
9
+ gem "opal-sprockets"
10
+ gem "sassc"
11
+ gem "uglifier"
12
+ # Until janbiedermann's Windows paths patch is released, we are using git ref:
13
+ # https://github.com/rubycdp/ferrum/commit/5beca76588d1544026eb8b8ee841f305803c67e5
14
+ # Also, my patch that captures stacktraces should be merged:
15
+ # https://github.com/hmdne/ferrum/commit/fbc74b7ca7b670d29cc53a2d779470a05c60ef9d
16
+ gem "ferrum", github: "hmdne/ferrum", ref: "capture-stacktrace"
17
+ gem "puma"
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2015 cj
2
- Copyright (c) 2020 hmdne
2
+ Copyright (c) 2020-2021 hmdne and opal-sprockets contributors
3
3
 
4
4
  MIT License
5
5
 
data/README.md CHANGED
@@ -22,35 +22,44 @@ And then execute:
22
22
  ```ruby
23
23
  class App < Roda
24
24
  plugin :sprockets, precompile: %w(site.js site.css),
25
- public_path: 'public/',
26
25
  opal: true,
27
- debug: true
28
26
  plugin :public
29
27
 
30
28
  route do |r|
31
29
  r.public
32
- r.sprockets
33
- end
30
+ r.sprockets if ENV['RACK_ENV'] == 'development'
31
+ end
34
32
  end
35
33
  ```
36
34
 
37
35
  ### Parameters for the plugin:
38
36
 
39
37
  * `precompile` - an array of files that you want to precompile
40
- * `prefix` (relative to the `root`, which is `app.opts[:root]` by default,
41
- but also supports absolute paths) - an array of directories where your
42
- assets are located, by default: `%w(assets vendor/assets)`.
43
- * `root` - a filesystem root directory of your app. By default, `app.opts[:root]`.
44
- * `public_path` - filesystem path to your `public` directory, for all your
45
- precompilation needs. (REQUIRED)
38
+ * `prefix` (relative to the `root`, which is `app.opts[:root]` by default) -
39
+ an array of directories where your assets are located, by default:
40
+ `%w(assets vendor/assets)`.
41
+ * Here, an element of the array can be an absolute path instead of relative.
42
+ * If an element ends with `/`, it will use assets directly in a given
43
+ subdirectory.
44
+ * If it doesn't end with `/` (which is by default) if will be equivalent to
45
+ `value/*`, which means, that assets will be loaded for instance from
46
+ `assets/javascripts` and `assets/stylesheets`, but not directly from `assets`.
47
+ * An element can contain glob characters.
48
+ * `root` - a filesystem root directory of your app. By default, same as
49
+ `app.opts[:root]`, that is: `Dir.pwd`.
50
+ * `public_path` - filesystem path to a place, where precompiled assets will be
51
+ stored, by default: `public/assets` (it should be a directory from which `:public`
52
+ plugin takes files + `path_prefix`)
46
53
  * `path_prefix` - a Roda prefix of your assets directory. By default: `/assets`
47
54
  * `protocol` - either :http (default) or :https.
48
- * `css_compressor`, `js_compressor` - pick a compressor of your choice.
55
+ * `css_compressor`, `js_compressor` - pick a compressor of your choice (respected
56
+ only if `debug` is false)
49
57
  * `host` - for hosting your assets on a different server
50
58
  * `digest` (bool) - digest your assets for unique filenames, default: true
51
59
  * `opal` (bool) - Opal support, default: false
52
- * `debug` (bool) - debug mode, default: false
53
- * `cache` - a `Sprockets::Cache` instance
60
+ * `debug` (bool) - debug mode, default: true if RACK_ENV is development,
61
+ false otherwise (in rake tasks it's false too)
62
+ * `cache` - a `Sprockets::Cache` instance, default: nil (no cache)
54
63
 
55
64
  ### Templates:
56
65
 
@@ -88,15 +97,39 @@ $document.body << DOM {
88
97
  ```
89
98
 
90
99
  You will need to tell Opal to load this file. Add this in your template
91
- after everything has been loaded or somewhere else:
100
+ after everything has been loaded (after your `javascript_tag` call, it is
101
+ needed too!):
92
102
 
93
- ```html
103
+ ```erb
94
104
  <%= opal_require 'site' %>
95
105
  ```
96
106
 
97
107
  Note that it won't be needed for plain Javascript use, only Opal needs that
98
108
  line.
99
109
 
110
+ ### Caching:
111
+
112
+ To speed up page loads during development, you can enable cache. Be warned,
113
+ there are some caveats with how Sprockets cache works. This will improve your
114
+ experience, but be prepared for some rough edges.
115
+
116
+ To enable memory cache, add an argument to your plugin config:
117
+
118
+ ```ruby
119
+ cache: Sprockets::Cache::MemoryStore.new(65536)
120
+ ```
121
+
122
+ To enable filesystem cache, for it to persist across application restarts,
123
+ add an argument to your plugin config:
124
+
125
+ ```ruby
126
+ cache: Sprockets::Cache::FileStore.new("var/cache/")
127
+ ```
128
+
129
+ Remember: with filesystem cache problems may happen if you, for instance,
130
+ update your Gems. You will then have to remove the cache for it to get
131
+ repopulated.
132
+
100
133
  ### Rake precompilation tasks:
101
134
 
102
135
  In your Rakefile:
@@ -110,6 +143,27 @@ Roda::RodaPlugins::Sprockets::Task.define!(App)
110
143
 
111
144
  And launch: `rake assets:precompile` or `rake assets:clean`
112
145
 
146
+ ### Sprockets extended configuration:
147
+
148
+ You can configure your environment directly by accessing a method `sprockets_env`
149
+ (which is a `Sprockets::Environment`) on your `App` class.
150
+
151
+ For instance, to enable Brotli with [sprockets-exporters_pack](https://github.com/hansottowirtz/sprockets-exporters_pack)
152
+ you will need to either add this code inside your `App` class:
153
+
154
+ ```ruby
155
+ sprockets_env.register_exporter %w(text/css application/javascript image/svg+xml), Sprockets::ExportersPack::BrotliExporter
156
+ ```
157
+
158
+ Or at some other point:
159
+
160
+ ```ruby
161
+ App.sprockets_env.register_exporter %w(text/css application/javascript image/svg+xml), Sprockets::ExportersPack::BrotliExporter
162
+ ```
163
+
164
+ Do note, that some extensions, like for example [opal-optimizer](https://github.com/hmdne/opal-optimizer)
165
+ won't need any configuration at all beside `require "opal/optimizer/sprockets"`.
166
+
113
167
  ## Contributing
114
168
 
115
169
  1. Fork it ( https://github.com/hmdne/roda-sprockets/fork )
data/Rakefile CHANGED
@@ -1,2 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "roda-sprockets", path: "../.."
4
+ gem "opal-sprockets"
5
+ gem "puma"
6
+ gem "rake"
@@ -0,0 +1,4 @@
1
+ This is an example found in `opal` repository in `docs/roda-sprockets.md`.
2
+
3
+ It describes how to create the simplest application with roda, roda-sprockets
4
+ and opal.
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ require "roda/plugins/sprockets_task"
3
+
4
+ require_relative "app"
5
+
6
+ Roda::RodaPlugins::Sprockets::Task.define!(App)
@@ -0,0 +1,25 @@
1
+ require 'roda'
2
+
3
+ class App < Roda
4
+ plugin :sprockets, precompile: %w(application.js),
5
+ prefix: %w(app/),
6
+ opal: true
7
+ plugin :public
8
+
9
+ route do |r|
10
+ r.public
11
+ r.sprockets if ENV['RACK_ENV'] == 'development'
12
+
13
+ r.root do
14
+ <<~END
15
+ <!doctype html>
16
+ <html>
17
+ <head>
18
+ #{ javascript_tag 'application' }
19
+ #{ opal_require 'application' }
20
+ </head>
21
+ </html>
22
+ END
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ require 'opal'
2
+
3
+ puts "wow, running ruby!"
@@ -0,0 +1,3 @@
1
+ require_relative "app"
2
+
3
+ run App.app
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "roda-sprockets", path: "../.."
4
+ gem "opal"
5
+ gem "opal-sprockets"
6
+ gem "puma"
7
+ gem "rake"
8
+ gem "uglifier"
@@ -0,0 +1 @@
1
+ This example drops an exception. It's for a test of source maps.
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ require "roda/plugins/sprockets_task"
3
+
4
+ require_relative "app"
5
+
6
+ Roda::RodaPlugins::Sprockets::Task.define!(App)
@@ -0,0 +1,26 @@
1
+ require 'roda'
2
+
3
+ class App < Roda
4
+ plugin :sprockets, precompile: %w(application.js),
5
+ prefix: %w(app/),
6
+ js_compressor: :uglify,
7
+ opal: true
8
+ plugin :public
9
+
10
+ route do |r|
11
+ r.public
12
+ r.sprockets if ENV['RACK_ENV'] == 'development'
13
+
14
+ r.root do
15
+ <<~END
16
+ <!doctype html>
17
+ <html>
18
+ <head>
19
+ #{ javascript_tag 'application' }
20
+ #{ opal_require 'application' }
21
+ </head>
22
+ </html>
23
+ END
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ require 'opal'
2
+
3
+ def a
4
+ b
5
+ end
6
+
7
+ def b
8
+ 2 + 2 == 5
9
+ c
10
+ end
11
+
12
+ def c
13
+ nomethoderror
14
+ end
15
+
16
+ a
@@ -0,0 +1,3 @@
1
+ require_relative "app"
2
+
3
+ run App.app
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "roda-sprockets", path: "../.."
4
+ gem "sassc"
5
+ gem "puma"
6
+ gem "rake"
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ require "roda/plugins/sprockets_task"
3
+
4
+ require_relative "app"
5
+
6
+ Roda::RodaPlugins::Sprockets::Task.define!(App)
@@ -0,0 +1,23 @@
1
+ require 'roda'
2
+
3
+ class App < Roda
4
+ plugin :sprockets, precompile: %w(application.css)
5
+ plugin :public
6
+
7
+ route do |r|
8
+ r.public
9
+ r.sprockets if ENV['RACK_ENV'] == 'development'
10
+
11
+ r.root do
12
+ <<~END
13
+ <!doctype html>
14
+ <html>
15
+ <head>
16
+ #{ stylesheet_tag 'application' }
17
+ </head>
18
+ </html>
19
+ END
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,3 @@
1
+ html, body {
2
+ background-color: green;
3
+ }
@@ -0,0 +1,3 @@
1
+ require_relative "app"
2
+
3
+ run App.app
@@ -10,7 +10,7 @@ class Roda
10
10
  precompile: %w(app.js app.css *.png *.jpg *.svg *.eot *.ttf *.woff *.woff2),
11
11
  prefix: %w(assets vendor/assets),
12
12
  root: false,
13
- public_path: false,
13
+ public_path: "public/assets/",
14
14
  path_prefix: "/assets",
15
15
  protocol: :http,
16
16
  css_compressor: nil,
@@ -18,14 +18,10 @@ class Roda
18
18
  host: nil,
19
19
  digest: true,
20
20
  opal: false,
21
- debug: false,
21
+ debug: nil,
22
22
  cache: nil,
23
23
  }.freeze
24
24
 
25
- def self.load_dependencies(app, _opts = nil)
26
- app.plugin :environments
27
- end
28
-
29
25
  def self.configure(app, plugin_options = {})
30
26
  if app.opts[:sprockets]
31
27
  app.opts[:sprockets].merge!(plugin_options)
@@ -39,7 +35,13 @@ class Roda
39
35
  options[:root] = app.opts[:root] || Dir.pwd
40
36
  end
41
37
 
42
- %i(root public_path).each { |type| raise "#{type} needs to be set." unless options[type] }
38
+ if options[:debug].nil?
39
+ if ENV['RACK_ENV'] == 'development'
40
+ options[:debug] = true
41
+ else
42
+ options[:debug] = false
43
+ end
44
+ end
43
45
 
44
46
  # opal-sprockets registers engines when required, but if we create Sprockets::Environment before
45
47
  # requiring that, they don't get registered
@@ -85,9 +87,9 @@ class Roda
85
87
  options[:sprockets_helpers].prefix = options[:path_prefix] unless options[:path_prefix].nil?
86
88
  options[:sprockets_helpers].debug = options[:debug]
87
89
 
88
- app.configure :staging, :production do
90
+ unless options[:debug]
89
91
  options[:sprockets].css_compressor = options[:css_compressor] unless options[:css_compressor].nil?
90
- options[:sprockets].js_compressor = options[:js_compressor] unless options[:js_compressor].nil?
92
+ options[:sprockets].js_compressor = options[:js_compressor] unless options[:js_compressor].nil?
91
93
 
92
94
  options[:sprockets_helpers].manifest = ::Sprockets::Manifest.new(options[:sprockets], options[:public_path])
93
95
  options[:sprockets_helpers].protocol = options[:protocol]
@@ -100,6 +102,10 @@ class Roda
100
102
  opts[:sprockets]
101
103
  end
102
104
 
105
+ def sprockets_env
106
+ sprockets_options[:sprockets]
107
+ end
108
+
103
109
  def sprockets_regexp
104
110
  %r{#{sprockets_options[:sprockets_helpers].prefix[1..-1]}/(.*)}
105
111
  end
@@ -138,15 +144,20 @@ class Roda
138
144
 
139
145
  status, headers, response = options[:sprockets].call env_sprockets
140
146
 
141
- # Appease Rack::Lint
142
- if (300..399).include? status
143
- headers.delete("Content-type")
144
- end
145
-
146
147
  scope.response.status = status
147
148
  scope.response.headers.merge! headers
148
149
 
149
- response.is_a?(Array) ? response.join('\n') : response.to_s
150
+ case response
151
+ when nil, []
152
+ # Empty response happens for example when 304 Not Modified happens.
153
+ # We want to return nil in this case.
154
+ # (See: https://github.com/hmdne/roda-sprockets/issues/1)
155
+ nil
156
+ when Array
157
+ response.join
158
+ else
159
+ response.to_s
160
+ end
150
161
  end
151
162
  end
152
163
  end
@@ -5,20 +5,44 @@ require 'rake/sprocketstask'
5
5
  class Roda
6
6
  module RodaPlugins
7
7
  module Sprockets
8
- class Task < Rake::TaskLib
8
+ class Task < Rake::SprocketsTask
9
9
  def initialize(app_klass)
10
+ if app_klass.class != Class
11
+ raise "#{app_klass} is not a Class"
12
+ elsif !app_klass.ancestors.map(&:name).include? "Roda"
13
+ raise "#{app_klass} doesn't inherit Roda"
14
+ elsif !app_klass.respond_to? :sprockets_options
15
+ raise "#{app_klass} doesn't load 'plugin :sprockets` - we can build nothing from it"
16
+ end
17
+
18
+ @app_klass = app_klass
19
+ super() { update_values }
20
+ end
21
+
22
+ def update_values
23
+ @environment = sprockets_options[:sprockets]
24
+ @output = sprockets_options[:public_path]
25
+ @assets = sprockets_options[:precompile]
26
+ end
27
+
28
+ def sprockets_options
29
+ @app_klass.sprockets_options
30
+ end
31
+
32
+ def define
10
33
  namespace :assets do
11
34
  desc "Precompile assets"
12
35
  task :precompile do
13
- options = app_klass.sprockets_options
14
- environment = options[:sprockets]
15
- manifest = ::Sprockets::Manifest.new(environment.index, options[:public_path])
16
- manifest.compile(options[:precompile])
36
+ with_logger do
37
+ manifest.compile(assets)
38
+ end
17
39
  end
18
40
 
19
41
  desc "Clean assets"
20
42
  task :clean do
21
- FileUtils.rm_rf(app_klass.sprockets_options[:public_path])
43
+ with_logger do
44
+ manifest.clobber
45
+ end
22
46
  end
23
47
  end
24
48
  end
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "roda-sprockets"
7
- spec.version = '1.0.2'
7
+ spec.version = '2.0.0rc1'
8
8
  spec.authors = ["cj", "hmdne"]
9
9
  spec.email = ["cjlazell@gmail.com"]
10
10
  spec.summary = %q{Use sprockets to serve assets in roda.}
@@ -0,0 +1,7 @@
1
+ @mixin scss($this, $is, $scss) {
2
+ content: "#{$this} #{$is} #{$scss}"
3
+ }
4
+
5
+ body {
6
+ @include scss(This, is, SCSS);
7
+ }
@@ -0,0 +1,13 @@
1
+ <%=
2
+ mixin = "@mixin"
3
+ incl = "@include"
4
+ <<~EOF
5
+ #{mixin} scss($this, $is, $scss) {
6
+ content: "\#{$this} \#{$is} \#{$scss}"
7
+ }
8
+
9
+ body {
10
+ #{incl} scss(This, is, ERBSCSS);
11
+ }
12
+ EOF
13
+ %>
@@ -0,0 +1 @@
1
+ @import "scss_css";
@@ -0,0 +1 @@
1
+ body { content: "Simple CSS"; }
@@ -0,0 +1,4 @@
1
+ class Test
2
+ end
3
+
4
+ Test.new
@@ -0,0 +1,10 @@
1
+ <%=
2
+ classname = "TestERB"
3
+
4
+ <<~EOF
5
+ class #{classname}
6
+ end
7
+
8
+ #{classname}.new
9
+ EOF
10
+ %>
@@ -0,0 +1 @@
1
+ require "opal_js"
@@ -0,0 +1 @@
1
+ alert("Simple JS");
@@ -0,0 +1,141 @@
1
+ require "ferrum"
2
+
3
+ def sigterm(pid)
4
+ if Gem.win_platform?
5
+ system("taskkill /F /pid #{pid}")
6
+ else
7
+ Process.kill("TERM", pid)
8
+ Process.wait(pid)
9
+ end
10
+ rescue Errno::ECHILD
11
+ # Ignore
12
+ end
13
+
14
+ def free_port
15
+ $free_port ||= 19900
16
+ $free_port += 1
17
+ $free_port
18
+ end
19
+
20
+ browser_options = {
21
+ timeout: 15,
22
+ process_timeout: 20,
23
+ js_errors: true,
24
+ browser_options: {
25
+ 'no-sandbox': nil,
26
+ 'auto-open-devtools-for-tabs': nil
27
+ }
28
+ }
29
+
30
+ RSpec.describe "Example" do
31
+ Dir[__dir__+"/../examples/*"].each do |example|
32
+ example_name = File.basename(example)
33
+
34
+ example_specific = proc do |browser, debug, error|
35
+ case example_name
36
+ when "opal-doc"
37
+ browser.evaluate("typeof(Opal.Kernel.$puts)").should be == "function"
38
+ when "opal-exception"
39
+ error.should_not be_nil
40
+ functions = error.stack_trace["callFrames"].map { |i| i["functionName"] }
41
+ if debug
42
+ functions.should include "$$a"
43
+ functions.should include "$$b"
44
+ functions.should include "$$c"
45
+ else
46
+ # We minify this example, so functions should be mangled
47
+ functions.should_not include "$$a"
48
+ functions.should_not include "$$b"
49
+ functions.should_not include "$$c"
50
+ end
51
+ # Nevertheless, "Opal.modules.application" should stay unmangled.
52
+ functions.should include "Opal.modules.application"
53
+ # How can we now test that source maps work? The browser doesn't help us...
54
+ when "sass"
55
+ browser.evaluate("window.getComputedStyle(document.body).backgroundColor").should be == "rgb(0, 128, 0)"
56
+ end
57
+ end
58
+
59
+ context example_name do
60
+ it "works in development mode" do
61
+ Dir.chdir example do
62
+ Bundler.with_unbundled_env do
63
+ if ENV.key? 'CI'
64
+ # Ensure we will reuse the cached gems
65
+ system("bundle config --local path #{__dir__+"/../vendor/bundle"}")
66
+ end
67
+
68
+ system("bundle install").should be true
69
+
70
+ begin
71
+ port = free_port
72
+ pid = spawn("bundle exec rackup -p#{port}")
73
+ sleep 1
74
+
75
+ begin
76
+ browser = Ferrum::Browser.new(**browser_options)
77
+ browser.go_to("http://localhost:#{port}/")
78
+ browser.network.wait_for_idle
79
+
80
+ # Sometimes we need to wait a little bit longer for the exception to appear.
81
+ sleep 3 if example_name == "opal-exception"
82
+
83
+ instance_exec(browser, true, nil, &example_specific)
84
+ rescue Ferrum::JavaScriptError => e
85
+ instance_exec(browser, true, e, &example_specific)
86
+ ensure
87
+ browser.quit if browser
88
+ end
89
+ ensure
90
+ sigterm(pid)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ it "works in production mode" do
97
+ Dir.chdir example do
98
+ Bundler.with_unbundled_env do
99
+ if ENV.key? 'CI'
100
+ system("bundle config --local path #{__dir__+"/../vendor/bundle"}")
101
+ end
102
+
103
+ system("bundle install").should be true
104
+
105
+ system("bundle exec rake assets:clean").should be true
106
+ -> { Dir.children(example + "/public/assets") }.should raise_error Errno::ENOENT
107
+ system("bundle exec rake assets:precompile").should be true
108
+ Dir.children(example + "/public/assets").length.should be == 3
109
+
110
+ ENV['RACK_ENV'] = 'production'
111
+
112
+ begin
113
+ port = free_port
114
+ pid = spawn("bundle exec rackup -p#{port}")
115
+ sleep 1
116
+
117
+ begin
118
+ browser = Ferrum::Browser.new(**browser_options)
119
+ browser.go_to("http://localhost:#{port}/")
120
+ browser.network.wait_for_idle
121
+
122
+ instance_exec(browser, false, nil, &example_specific)
123
+ rescue Ferrum::JavaScriptError => e
124
+ instance_exec(browser, false, e, &example_specific)
125
+ ensure
126
+ browser.quit if browser
127
+ end
128
+ ensure
129
+ system("bundle exec rake assets:clean").should be true
130
+ -> { Dir.children(example + "/public/assets") }.should raise_error Errno::ENOENT
131
+
132
+ sigterm(pid)
133
+ end
134
+ ensure
135
+ ENV.delete('RACK_ENV')
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,237 @@
1
+ require "time"
2
+ require "roda/plugins/sprockets"
3
+
4
+ RSpec.shared_examples("basic checks") do
5
+ it "returns a OK result" do
6
+ req = app.req
7
+ req.should be_ok
8
+ end
9
+
10
+ it "generates the necessary tags" do
11
+ req = app.req
12
+ if debug
13
+ req.body.should include %'<script src="/assets/#{js_name}.debug.js" type="text/javascript"'
14
+ req.body.should include %'<link rel="stylesheet" type="text/css" href="/assets/#{css_name}.debug.css">'
15
+ else
16
+ req.body.should include %'<script src="/assets/#{js_name}-'
17
+ req.body.should include %'<link rel="stylesheet" type="text/css" href="/assets/#{css_name}-'
18
+ end
19
+ end
20
+
21
+ it "generates the necessary files" do
22
+ req = app.req
23
+ req_css = app.req(req.css_path)
24
+ req_js = app.req(req.js_path)
25
+
26
+ req_css.should be_ok
27
+ req_css.body.should include css_value
28
+
29
+ req_js.should be_ok
30
+ req_js.body.should include js_value
31
+ end
32
+
33
+ it "returns a not modified status if the assets haven't been modified" do
34
+ req = app.req
35
+
36
+ etag_css = app.req(req.css_path).headers["ETag"]
37
+ etag_js = app.req(req.js_path).headers["ETag"]
38
+
39
+ req_css = app.req(req.css_path, "HTTP_IF_NONE_MATCH" => etag_css)
40
+ req_js = app.req(req.js_path, "HTTP_IF_NONE_MATCH" => etag_js)
41
+
42
+ req_css.should be_not_modified
43
+ req_js.should be_not_modified
44
+ end
45
+
46
+ it "appropriately includes source maps or not" do
47
+ req = app.req
48
+ req_css = app.req(req.css_path)
49
+ req_js = app.req(req.js_path)
50
+
51
+ if debug
52
+ req_css.body.should include "/*# sourceMappingURL="
53
+ req_js.body.should include "//# sourceMappingURL="
54
+ else
55
+ req_css.body.should_not include "/*# sourceMappingURL="
56
+ req_js.body.should_not include "//# sourceMappingURL="
57
+ end
58
+ end
59
+ end
60
+
61
+ RSpec.describe Roda::RodaPlugins::Sprockets do
62
+ context "simple app" do
63
+ let(:css_name) { "simple_css" }
64
+ let(:js_name) { "simple_js" }
65
+ let(:css_value) { "Simple CSS" }
66
+ let(:js_value) { "Simple JS" }
67
+
68
+ context "in debug mode" do
69
+ simple_debug_app = Server.gen("simple_css", "simple_js", debug: true)
70
+
71
+ let(:app) { simple_debug_app }
72
+ let(:debug) { true }
73
+
74
+ include_examples "basic checks"
75
+ end
76
+
77
+ context "in production mode" do
78
+ simple_prod_app = Server.gen("simple_css", "simple_js", debug: false)
79
+
80
+ let(:app) { simple_prod_app }
81
+ let(:debug) { false }
82
+
83
+ include_examples "basic checks"
84
+ end
85
+ end
86
+
87
+ context "opal + scss app" do
88
+ let(:css_name) { "scss_css" }
89
+ let(:js_name) { "opal_js" }
90
+ let(:css_value) { "This is SCSS" }
91
+ let(:js_value) { "$$($nesting, 'Test').$new();" }
92
+
93
+ context "in debug mode" do
94
+ opalscss_debug_app = Server.gen("scss_css", "opal_js", opal: true, debug: true)
95
+
96
+ let(:app) { opalscss_debug_app }
97
+ let(:debug) { true }
98
+
99
+ include_examples "basic checks"
100
+ end
101
+
102
+ context "in production mode" do
103
+ opalscss_prod_app = Server.gen("scss_css", "opal_js", opal: true, debug: false)
104
+
105
+ let(:app) { opalscss_prod_app }
106
+ let(:debug) { false }
107
+
108
+ include_examples "basic checks"
109
+ end
110
+ end
111
+
112
+ context "opal with requires + scss with imports app" do
113
+ let(:css_name) { "scss_css_with_imports" }
114
+ let(:js_name) { "opal_js_with_requires" }
115
+ let(:css_value) { "This is SCSS" }
116
+ let(:js_value) { "$$($nesting, 'Test').$new();" }
117
+
118
+ context "in debug mode" do
119
+ opalscssreq_debug_app = Server.gen("scss_css_with_imports", "opal_js_with_requires", opal: true, debug: true)
120
+
121
+ let(:app) { opalscssreq_debug_app }
122
+ let(:debug) { true }
123
+
124
+ include_examples "basic checks"
125
+ end
126
+
127
+ context "in production mode" do
128
+ opalscssreq_prod_app = Server.gen("scss_css_with_imports", "opal_js_with_requires", opal: true, debug: false)
129
+
130
+ let(:app) { opalscssreq_prod_app }
131
+ let(:debug) { false }
132
+
133
+ include_examples "basic checks"
134
+ end
135
+ end
136
+
137
+ context "opal with erb + scss with erb app" do
138
+ let(:css_name) { "scss_css_with_erb" }
139
+ let(:js_name) { "opal_js_with_erb" }
140
+ let(:css_value) { "This is ERBSCSS" }
141
+ let(:js_value) { "$$($nesting, 'TestERB').$new();" }
142
+
143
+ context "in debug mode" do
144
+ opalscsserb_debug_app = Server.gen("scss_css_with_erb", "opal_js_with_erb", opal: true, debug: true)
145
+
146
+ let(:app) { opalscsserb_debug_app }
147
+ let(:debug) { true }
148
+
149
+ include_examples "basic checks"
150
+ end
151
+
152
+ context "in production mode" do
153
+ opalscsserb_prod_app = Server.gen("scss_css_with_erb", "opal_js_with_erb", opal: true, debug: false)
154
+
155
+ let(:app) { opalscsserb_prod_app }
156
+ let(:debug) { false }
157
+
158
+ include_examples "basic checks"
159
+ end
160
+ end
161
+
162
+ context "opal + scss app with compressor" do
163
+ let(:css_name) { "scss_css" }
164
+ let(:js_name) { "opal_js" }
165
+ let(:css_value) { "This is SCSS" }
166
+ let(:js_value) { "Test" }
167
+
168
+ context "in debug mode" do
169
+ opalscsscomp_debug_app = Server.gen("scss_css", "opal_js",
170
+ css_compressor: :sassc, js_compressor: :uglify,
171
+ opal: true, debug: true
172
+ )
173
+
174
+ let(:app) { opalscsscomp_debug_app }
175
+ let(:debug) { true }
176
+
177
+ include_examples "basic checks"
178
+
179
+ it "should run uglifier" do
180
+ req = app.req
181
+ req_js = app.req(req.js_path)
182
+
183
+ req_js.body.should include "$$($nesting, 'Test').$new();"
184
+ end
185
+ end
186
+
187
+ context "in production mode" do
188
+ opalscsscomp_prod_app = Server.gen("scss_css", "opal_js",
189
+ css_compressor: :sassc, js_compressor: :uglify,
190
+ opal: true, debug: false
191
+ )
192
+
193
+ let(:app) { opalscsscomp_prod_app }
194
+ let(:debug) { false }
195
+
196
+ include_examples "basic checks"
197
+
198
+ it "should not run uglifier" do
199
+ req = app.req
200
+ req_js = app.req(req.js_path)
201
+
202
+ req_js.body.should_not include "$$($nesting, 'Test').$new();"
203
+ end
204
+ end
205
+ end
206
+
207
+ context "opal + scss app with cache" do
208
+ let(:css_name) { "scss_css" }
209
+ let(:js_name) { "opal_js" }
210
+ let(:css_value) { "This is SCSS" }
211
+ let(:js_value) { "$$($nesting, 'Test').$new();" }
212
+
213
+ context "in debug mode" do
214
+ opalscsscache_debug_app = Server.gen("scss_css", "opal_js",
215
+ opal: true, debug: true,
216
+ cache: Sprockets::Cache::MemoryStore.new(65536)
217
+ )
218
+
219
+ let(:app) { opalscsscache_debug_app }
220
+ let(:debug) { true }
221
+
222
+ include_examples "basic checks"
223
+ end
224
+
225
+ context "in production mode" do
226
+ opalscsscache_prod_app = Server.gen("scss_css", "opal_js",
227
+ opal: true, debug: false,
228
+ cache: Sprockets::Cache::MemoryStore.new(65536)
229
+ )
230
+
231
+ let(:app) { opalscsscache_prod_app }
232
+ let(:debug) { false }
233
+
234
+ include_examples "basic checks"
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,68 @@
1
+ require "roda"
2
+
3
+ class Server < Roda
4
+ class << self
5
+ def gen(css=nil, js=nil, **kwargs, &block)
6
+ Class.new Server do |c|
7
+ unless kwargs.empty?
8
+ c.plugin :sprockets, root: __dir__, **kwargs
9
+ end
10
+
11
+ if css || js
12
+ c.route do |r|
13
+ r.sprockets
14
+
15
+ r.root do
16
+ out = ""
17
+ out += stylesheet_tag(css) if css
18
+ out += javascript_tag(js) if js
19
+ out
20
+ end
21
+ end
22
+ end
23
+
24
+ yield(c) if block_given?
25
+ end
26
+ end
27
+
28
+ # Slightly adapted from https://github.com/jeremyevans/roda/blob/master/spec/spec_helper.rb
29
+ def req(path = '/', env = {})
30
+ env['PATH_INFO'] = path.dup
31
+ env = {"REQUEST_METHOD" => "GET", "PATH_INFO" => "/", "SCRIPT_NAME" => ""}.merge(env)
32
+ Response.new(*self.call(env))
33
+ end
34
+ end
35
+
36
+ class Response
37
+ def initialize(status, headers, body)
38
+ @status, @headers, @body = status, headers, body
39
+ end
40
+
41
+ def ok?
42
+ status == 200
43
+ end
44
+
45
+ attr_reader :status, :headers
46
+
47
+ def body
48
+ @body&.join
49
+ end
50
+
51
+ def js_path
52
+ body =~ /<script src="(.*?)"/
53
+ $1
54
+ end
55
+
56
+ def css_path
57
+ body =~ /<link rel="stylesheet" type="text\/css" href="(.*?)"/
58
+ $1
59
+ end
60
+
61
+ def not_modified?
62
+ status == 304 &&
63
+ (body.nil? || body.empty?) &&
64
+ !headers.key?("Content-Length") &&
65
+ !headers.key?("Content-Type")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,59 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ # This option will default to `true` in RSpec 4. It makes the `description`
4
+ # and `failure_message` of custom matchers include text for helper methods
5
+ # defined using `chain`, e.g.:
6
+ # be_bigger_than(2).and_smaller_than(4).description
7
+ # # => "be bigger than 2 and smaller than 4"
8
+ # ...rather than:
9
+ # # => "be bigger than 2"
10
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
+ expectations.syntax = :should
12
+ end
13
+
14
+ config.mock_with :rspec do |mocks|
15
+ # Prevents you from mocking or stubbing a method that does not exist on
16
+ # a real object. This is generally recommended, and will default to
17
+ # `true` in RSpec 4.
18
+ mocks.verify_partial_doubles = true
19
+ end
20
+
21
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
22
+ # have no way to turn it off -- the option exists only for backwards
23
+ # compatibility in RSpec 3). It causes shared context metadata to be
24
+ # inherited by the metadata hash of host groups and examples, rather than
25
+ # triggering implicit auto-inclusion in groups with matching metadata.
26
+ config.shared_context_metadata_behavior = :apply_to_host_groups
27
+
28
+ # This allows you to limit a spec run to individual examples or groups
29
+ # you care about by tagging them with `:focus` metadata. When nothing
30
+ # is tagged with `:focus`, all examples get run. RSpec also provides
31
+ # aliases for `it`, `describe`, and `context` that include `:focus`
32
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
33
+ config.filter_run_when_matching :focus
34
+
35
+ # This setting enables warnings. It's recommended, but in some cases may
36
+ # be too noisy due to issues in dependencies.
37
+ config.warnings = true
38
+
39
+ # Print the 10 slowest examples and example groups at the
40
+ # end of the spec run, to help surface which specs are running
41
+ # particularly slow.
42
+ config.profile_examples = 10
43
+
44
+ # Run specs in random order to surface order dependencies. If you find an
45
+ # order dependency and want to debug it, you can fix the order by providing
46
+ # the seed, which is printed after each run.
47
+ # --seed 1234
48
+ config.order = :random
49
+
50
+ # Seed global randomization in this process using the `--seed` CLI option.
51
+ # Setting this allows you to use `--seed` to deterministically reproduce
52
+ # test failures related to randomization by passing the same `--seed` value
53
+ # as the one that triggered the failure.
54
+ Kernel.srand config.seed
55
+ end
56
+
57
+ require "bundler/setup"
58
+
59
+ require_relative "server_helper"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda-sprockets
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 2.0.0rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - cj
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-13 00:00:00.000000000 Z
12
+ date: 2021-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: roda
@@ -88,15 +88,47 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
+ - ".github/workflows/rake.yml"
91
92
  - ".gitignore"
93
+ - ".rspec"
94
+ - CHANGELOG.md
92
95
  - Gemfile
93
96
  - LICENSE.txt
94
97
  - README.md
95
98
  - Rakefile
99
+ - examples/opal-doc/Gemfile
100
+ - examples/opal-doc/README.md
101
+ - examples/opal-doc/Rakefile
102
+ - examples/opal-doc/app.rb
103
+ - examples/opal-doc/app/application.rb
104
+ - examples/opal-doc/config.ru
105
+ - examples/opal-exception/Gemfile
106
+ - examples/opal-exception/README.md
107
+ - examples/opal-exception/Rakefile
108
+ - examples/opal-exception/app.rb
109
+ - examples/opal-exception/app/application.rb
110
+ - examples/opal-exception/config.ru
111
+ - examples/sass/Gemfile
112
+ - examples/sass/Rakefile
113
+ - examples/sass/app.rb
114
+ - examples/sass/assets/css/application.scss
115
+ - examples/sass/config.ru
96
116
  - lib/roda/plugins/sprockets.rb
97
117
  - lib/roda/plugins/sprockets_task.rb
98
118
  - lib/roda/sprockets.rb
99
119
  - roda-sprockets.gemspec
120
+ - spec/assets/css/scss_css.scss
121
+ - spec/assets/css/scss_css_with_erb.scss.erb
122
+ - spec/assets/css/scss_css_with_imports.scss
123
+ - spec/assets/css/simple_css.css
124
+ - spec/assets/js/opal_js.rb
125
+ - spec/assets/js/opal_js_with_erb.rb.erb
126
+ - spec/assets/js/opal_js_with_requires.rb
127
+ - spec/assets/js/simple_js.js
128
+ - spec/examples_spec.rb
129
+ - spec/plugin_spec.rb
130
+ - spec/server_helper.rb
131
+ - spec/spec_helper.rb
100
132
  homepage: https://github.com/hmdne/roda-sprockets
101
133
  licenses:
102
134
  - MIT
@@ -112,12 +144,24 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
144
  version: '0'
113
145
  required_rubygems_version: !ruby/object:Gem::Requirement
114
146
  requirements:
115
- - - ">="
147
+ - - ">"
116
148
  - !ruby/object:Gem::Version
117
- version: '0'
149
+ version: 1.3.1
118
150
  requirements: []
119
151
  rubygems_version: 3.2.22
120
152
  signing_key:
121
153
  specification_version: 4
122
154
  summary: Use sprockets to serve assets in roda.
123
- test_files: []
155
+ test_files:
156
+ - spec/assets/css/scss_css.scss
157
+ - spec/assets/css/scss_css_with_erb.scss.erb
158
+ - spec/assets/css/scss_css_with_imports.scss
159
+ - spec/assets/css/simple_css.css
160
+ - spec/assets/js/opal_js.rb
161
+ - spec/assets/js/opal_js_with_erb.rb.erb
162
+ - spec/assets/js/opal_js_with_requires.rb
163
+ - spec/assets/js/simple_js.js
164
+ - spec/examples_spec.rb
165
+ - spec/plugin_spec.rb
166
+ - spec/server_helper.rb
167
+ - spec/spec_helper.rb