js_from_routes 2.0.0 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 120fa96855c034323a664b851019f5d1df7bd418de5578af5fa074b80451ca3e
4
- data.tar.gz: 7a39747149294d21f74568296c4cab44f2e93ea172c85bad0d721e686c16ce74
3
+ metadata.gz: 9053499de4df17e1ef042b1b54e4c172ab8ab94b2c0c353e6b8035efa8d3c0e5
4
+ data.tar.gz: 6fc5926dfcf3b0f94bde9c7176af31522c36bf206d6636563415143655414c3e
5
5
  SHA512:
6
- metadata.gz: 77d1b1b6bef1a59d68bc1452dcb8ade3de38123ae907687bab9017347897a440b6f8b57ce6874f46d8873c6fcddf0e0480e38aa46a02fd91c39600c5c9980eca
7
- data.tar.gz: 993bf91cfd887d1c645313b305f2f5d1c095ed31f3232d34b7dcbf575eefafdbadaeb3fb6948e00f1b1614e8f38e86217dd6c5bb2c55c508df771584441bde53
6
+ metadata.gz: 30c30f206f148dcb9ca5dac38f4f5eb07d0b6d49f5ca4c87dd4e358a3b7af12ce1fe0750cb0554c91c0655c2cadcaf6ad47438a35b9ee9678eaf3cf4945ba2d5
7
+ data.tar.gz: 2515d4737e9202d93e99b3f573f43667d2beea1dc61a2fafdabf2a3752478e36289fd742649fe55396185604b3ff8b04d0742048e6d8ec7ef7530627107e0931
data/CHANGELOG.md CHANGED
@@ -1,3 +1,54 @@
1
+ ## [2.0.6](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.5...js_from_routes@2.0.6) (2022-05-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * should not attempt to export routes without controllers ([47f8f70](https://github.com/ElMassimo/js_from_routes/commit/47f8f70b0db98baa240470f8b2b891a730499518))
7
+
8
+
9
+
10
+ ## [2.0.5](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.4...js_from_routes@2.0.5) (2021-09-02)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * ensure :export is not added as a required default in routes ([40126ac](https://github.com/ElMassimo/js_from_routes/commit/40126ac27caeee33abef1c7067ba1db88ea03660))
16
+
17
+
18
+
19
+ ## [2.0.4](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.3...js_from_routes@2.0.4) (2021-03-16)
20
+
21
+ ### Features
22
+
23
+ * Allow importing individual helpers from "~/api" by adding exports ([2dfb8a2](https://github.com/ElMassimo/js_from_routes/commit/2dfb8a27d182376d75f0b037258bc772553e43f3)). Thanks @matias-capeletto!
24
+
25
+
26
+ ## [2.0.3](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.2...js_from_routes@2.0.3) (2021-03-16)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * Ensure changing the client library triggers code generation ([6cf2bdf](https://github.com/ElMassimo/js_from_routes/commit/6cf2bdf4896dafe0d1e80668551665c46bfcadc6))
32
+
33
+
34
+ ### Features
35
+
36
+ * Allow passing JS_FROM_ROUTES_FORCE=true to ignore cache keys ([8a6d2a8](https://github.com/ElMassimo/js_from_routes/commit/8a6d2a807e0a9926c6b24e1fc9127f917ec0ed5d))
37
+
38
+
39
+
40
+ ## [2.0.2](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.1...js_from_routes@2.0.2) (2021-03-14)
41
+
42
+ ### Improvements
43
+
44
+ - Remove underscores from namespaced controllers in global file ([90fdcc2](https://github.com/ElMassimo/js_from_routes/commit/90fdcc2))
45
+
46
+ ## [2.0.1](https://github.com/ElMassimo/js_from_routes/compare/js_from_routes@2.0.0...js_from_routes@2.0.1) (2021-03-14)
47
+
48
+ ### Features ⚡️
49
+
50
+ - Enable generation of index combining and exporting all helpers (#9)
51
+
1
52
  # [2.0.0](https://github.com/ElMassimo/js_from_routes/compare/v1.0.3...js_from_routes@2.0.0) (2021-03-13)
2
53
 
3
54
  ### Features ⚡️
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2021 Máximo Mussini
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -9,11 +9,11 @@ require "pathname"
9
9
  # Generates one file per controller, and one function per route.
10
10
  module JsFromRoutes
11
11
  # Internal: Helper class used as a presenter for the routes template.
12
- class Routes
12
+ class ControllerRoutes
13
13
  attr_reader :routes
14
14
 
15
- def initialize(routes, config)
16
- @config = config
15
+ def initialize(controller, routes, config)
16
+ @controller, @config = controller, config
17
17
  @routes = routes
18
18
  .uniq { |route| route.requirements.fetch(:action) }
19
19
  .map { |route| Route.new(route, config.helper_mappings) }
@@ -22,7 +22,7 @@ module JsFromRoutes
22
22
  # Public: Used to check whether the file should be generated again, changes
23
23
  # based on the configuration, and route definition.
24
24
  def cache_key
25
- Digest::MD5.hexdigest(routes.map(&:inspect).join + [File.read(@config.template_path), @config.helper_mappings.inspect].join)
25
+ routes.map(&:inspect).join + [File.read(@config.template_path), @config.helper_mappings.inspect, @config.client_library].join
26
26
  end
27
27
 
28
28
  # Public: Exposes the preferred import library to the generator.
@@ -30,10 +30,24 @@ module JsFromRoutes
30
30
  @config.client_library
31
31
  end
32
32
 
33
- # Internal: By performing the evaluation here, we ensure only "routes" is
34
- # exposed to the ERB template as a local variable.
35
- def evaluate(compiled_template)
36
- instance_eval(compiled_template)
33
+ # Internal: Name of the JS file with helpers for the the given controller.
34
+ def filename
35
+ @config.output_folder.join(basename)
36
+ end
37
+
38
+ # Public: Name of the JS file with helpers for the the given controller.
39
+ def import_filename
40
+ @config.output_folder.basename.join((basename.split(".")[0]).to_s)
41
+ end
42
+
43
+ # Public: Name of the file as a valid JS variable.
44
+ def js_name
45
+ @controller.camelize(:lower).tr(":", "")
46
+ end
47
+
48
+ # Internal: The base name of the JS file to be written.
49
+ def basename
50
+ "#{@controller.camelize}#{@config.file_suffix}".tr_s(":", "/")
37
51
  end
38
52
  end
39
53
 
@@ -70,6 +84,48 @@ module JsFromRoutes
70
84
  end
71
85
  end
72
86
 
87
+ # Internal: Represents a compiled template that can write itself to a file.
88
+ class Template
89
+ def initialize(template_path)
90
+ # NOTE: The compiled ERB template, used to generate JS code.
91
+ @compiled_template = Erubi::Engine.new(File.read(template_path), filename: template_path).src
92
+ end
93
+
94
+ # Public: Checks if the cache is fresh, or renders the template with the
95
+ # specified variables, and writes the updated result to a file.
96
+ def write_if_changed(object)
97
+ write_file_if_changed(object.filename, object.cache_key) { render_template(object) }
98
+ end
99
+
100
+ private
101
+
102
+ # Internal: Returns a String with the generated JS code.
103
+ def render_template(object)
104
+ object.instance_eval(@compiled_template)
105
+ end
106
+
107
+ # Internal: Returns true if the cache key has changed since the last codegen.
108
+ def stale?(file, cache_key_comment)
109
+ ENV["JS_FROM_ROUTES_FORCE"] || file.gets != cache_key_comment
110
+ end
111
+
112
+ # Internal: Writes if the file does not exist or the cache key has changed.
113
+ # The cache strategy consists of a comment on the first line of the file.
114
+ #
115
+ # Yields to receive the rendered file content when it needs to.
116
+ def write_file_if_changed(name, cache_key)
117
+ FileUtils.mkdir_p(name.dirname)
118
+ cache_key_comment = "// JsFromRoutes CacheKey #{Digest::MD5.hexdigest(cache_key)}\n"
119
+ File.open(name, "a+") { |file|
120
+ if stale?(file, cache_key_comment)
121
+ file.truncate(0)
122
+ file.write(cache_key_comment)
123
+ file.write(yield)
124
+ end
125
+ }
126
+ end
127
+ end
128
+
73
129
  class << self
74
130
  # Public: Configuration of the code generator.
75
131
  def config
@@ -82,67 +138,58 @@ module JsFromRoutes
82
138
  def generate!(app_or_routes = Rails.application)
83
139
  raise ArgumentError, "A Rails app must be defined, or you must specify a custom `output_folder`" if config.output_folder.blank?
84
140
  rails_routes = app_or_routes.is_a?(::Rails::Engine) ? app_or_routes.routes.routes : app_or_routes
85
- @compiled_template = nil # Clear on every code reload in case the template changed.
86
- exported_routes_by_controller(rails_routes).each do |controller, controller_routes|
87
- routes = Routes.new(controller_routes, config)
88
- write_if_changed(filename_for(controller), routes.cache_key) { render_template(routes) }
89
- end
141
+ generate_files exported_routes_by_controller(rails_routes)
90
142
  end
91
143
 
92
144
  private
93
145
 
146
+ def generate_files(exported_routes)
147
+ template = Template.new(config.template_path)
148
+ generate_file_for_all exported_routes.map { |controller, routes|
149
+ ControllerRoutes.new(controller, routes, config).tap do |routes|
150
+ template.write_if_changed routes
151
+ end
152
+ }
153
+ end
154
+
155
+ def generate_file_for_all(routes)
156
+ return unless config.all_helpers_file && !routes.empty?
157
+
158
+ preferred_extension = File.extname(config.file_suffix)
159
+ index_file = config.all_helpers_file == true ? "index#{preferred_extension}" : config.all_helpers_file
160
+
161
+ Template.new(config.template_all_path).write_if_changed OpenStruct.new(
162
+ cache_key: routes.map(&:import_filename).join + File.read(config.template_all_path),
163
+ filename: config.output_folder.join("all#{preferred_extension}"),
164
+ helpers: routes,
165
+ )
166
+ Template.new(config.template_index_path).write_if_changed OpenStruct.new(
167
+ cache_key: File.read(config.template_index_path),
168
+ filename: config.output_folder.join(index_file),
169
+ )
170
+ end
171
+
94
172
  def default_config(root)
95
173
  dir = %w[frontend packs javascript assets].find { |dir| root.join("app", dir).exist? }
96
174
  {
175
+ all_helpers_file: true,
97
176
  client_library: "@js-from-routes/client",
98
177
  file_suffix: "Api.js",
99
178
  helper_mappings: {"index" => "list", "show" => "get"},
100
179
  output_folder: root.join("app", dir, "api"),
101
180
  template_path: File.expand_path("template.js.erb", __dir__),
181
+ template_all_path: File.expand_path("template_all.js.erb", __dir__),
182
+ template_index_path: File.expand_path("template_index.js.erb", __dir__),
102
183
  }
103
184
  end
104
185
 
105
186
  # Internal: Returns exported routes grouped by controller name.
106
187
  def exported_routes_by_controller(routes)
107
188
  routes.select { |route|
108
- route.defaults.fetch(:export, false)
189
+ route.defaults.fetch(:export, false) && route.requirements[:controller]
109
190
  }.group_by { |route|
110
191
  route.requirements.fetch(:controller)
111
192
  }
112
193
  end
113
-
114
- # Internal: Name of the JS file with helpers for the the given controller.
115
- def filename_for(controller)
116
- config.output_folder.join("#{controller.camelize}#{config.file_suffix}".tr_s(":", "/"))
117
- end
118
-
119
- # Internal: Returns a String with the JS generated for a controller's routes.
120
- def render_template(routes)
121
- routes.evaluate(compiled_template)
122
- end
123
-
124
- # Internal: Returns the compiled ERB to generate JS from a set of routes.
125
- def compiled_template
126
- @compiled_template ||= begin
127
- template = File.read(config.template_path)
128
- Erubi::Engine.new(template, filename: config.template_path).src
129
- end
130
- end
131
-
132
- # Internal: Writes if the file does not exist or the cache key has changed.
133
- # The cache strategy consists of a comment on the first line of the file.
134
- #
135
- # Yields to receive the rendered file content when it needs to.
136
- def write_if_changed(name, cache_key)
137
- FileUtils.mkdir_p(name.dirname)
138
- cache_key_comment = "// JsFromRoutes CacheKey #{cache_key}\n"
139
- File.open(name, "a+") { |file|
140
- if file.gets != cache_key_comment
141
- file.truncate(0)
142
- file.write(cache_key_comment)
143
- file.write(yield)
144
- end
145
- }
146
- end
147
194
  end
148
195
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/railtie"
4
+
3
5
  # NOTE: Not strictly required, but it helps to simplify the setup.
4
6
  class JsFromRoutes::Railtie < Rails::Railtie
5
7
  railtie_name :js_from_routes
@@ -22,4 +24,14 @@ class JsFromRoutes::Railtie < Rails::Railtie
22
24
  end
23
25
  end
24
26
  end
27
+
28
+ # Prevents Rails from interpreting the :export option as a required default,
29
+ # which would cause controller tests to fail.
30
+ initializer "js_from_routes.required_defaults" do |app|
31
+ ActionDispatch::Journey::Route.prepend Module.new {
32
+ def required_default?(key)
33
+ key == :export ? false : super
34
+ end
35
+ }
36
+ end
25
37
  end
@@ -3,7 +3,7 @@
3
3
  import { definePathHelper } from '<%= client_library %>'
4
4
 
5
5
  export default {
6
- <% routes.each_with_index do |route, index| %>
6
+ <% routes.each do |route| %>
7
7
  <%= route.helper %>: definePathHelper('<%= route.verb %>', '<%= route.path %>'),
8
8
  <% end %>
9
9
  }
@@ -0,0 +1,5 @@
1
+ //
2
+ // DO NOT MODIFY: This file was automatically generated by JsFromRoutes.
3
+ <% helpers.each do |helper| %>
4
+ export { default as <%= helper.js_name %> } from '~/<%= helper.import_filename %>'
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+ //
2
+ // DO NOT MODIFY: This file was automatically generated by JsFromRoutes.
3
+ import * as helpers from '~/api/all'
4
+ export * from '~/api/all'
5
+ export default helpers
@@ -4,5 +4,5 @@
4
4
  # Generates one file per controller, and one function per route.
5
5
  module JsFromRoutes
6
6
  # Public: This library adheres to semantic versioning.
7
- VERSION = "2.0.0"
7
+ VERSION = "2.0.6"
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js_from_routes
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Máximo Mussini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-13 00:00:00.000000000 Z
11
+ date: 2022-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -139,11 +139,14 @@ extra_rdoc_files:
139
139
  - README.md
140
140
  files:
141
141
  - CHANGELOG.md
142
+ - LICENSE.txt
142
143
  - README.md
143
144
  - lib/js_from_routes.rb
144
145
  - lib/js_from_routes/generator.rb
145
146
  - lib/js_from_routes/railtie.rb
146
147
  - lib/js_from_routes/template.js.erb
148
+ - lib/js_from_routes/template_all.js.erb
149
+ - lib/js_from_routes/template_index.js.erb
147
150
  - lib/js_from_routes/version.rb
148
151
  homepage: https://github.com/ElMassimo/js_from_routes
149
152
  licenses:
@@ -164,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
167
  - !ruby/object:Gem::Version
165
168
  version: '0'
166
169
  requirements: []
167
- rubygems_version: 3.1.4
170
+ rubygems_version: 3.2.32
168
171
  signing_key:
169
172
  specification_version: 4
170
173
  summary: Generate JS automatically from Rails routes.