js-routes-zigexn 1.3.3
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 +7 -0
- data/.document +5 -0
- data/.gitignore +60 -0
- data/.rspec +1 -0
- data/.travis.yml +60 -0
- data/Appraisals +16 -0
- data/CHANGELOG.md +112 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/Rakefile +33 -0
- data/Readme.md +234 -0
- data/app/assets/javascripts/js-routes.js.erb +2 -0
- data/gemfiles/rails32.gemfile +8 -0
- data/gemfiles/rails40.gemfile +8 -0
- data/gemfiles/rails40_sprockets3.gemfile +8 -0
- data/gemfiles/rails41.gemfile +8 -0
- data/gemfiles/rails41_sprockets3.gemfile +8 -0
- data/gemfiles/rails42.gemfile +8 -0
- data/gemfiles/rails42_sprockets3.gemfile +8 -0
- data/gemfiles/rails50.gemfile +8 -0
- data/gemfiles/rails50_sprockets3.gemfile +8 -0
- data/js-routes-zigexn.gemspec +36 -0
- data/lib/js-routes.rb +1 -0
- data/lib/js_routes/engine.rb +73 -0
- data/lib/js_routes/version.rb +3 -0
- data/lib/js_routes.rb +293 -0
- data/lib/routes.js +470 -0
- data/lib/routes.js.coffee +368 -0
- data/lib/tasks/js_routes.rake +8 -0
- data/spec/dummy/app/assets/javascripts/.gitkeep +0 -0
- data/spec/dummy/config/routes.rb +55 -0
- data/spec/js_routes/amd_compatibility_spec.rb +42 -0
- data/spec/js_routes/default_serializer_spec.rb +15 -0
- data/spec/js_routes/generated_javascript_spec.rb +83 -0
- data/spec/js_routes/options_spec.rb +450 -0
- data/spec/js_routes/rails_routes_compatibility_spec.rb +414 -0
- data/spec/js_routes/zzz_last_post_rails_init_spec.rb +135 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/support/routes.rb +76 -0
- metadata +209 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'js_routes/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{js-routes-zigexn}
|
8
|
+
s.version = JsRoutes::VERSION
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["VietTH"]
|
12
|
+
s.description = %q{Generates javascript file that defines all Rails named routes as javascript helpers}
|
13
|
+
s.email = %q{vietth@zigexn.vn}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE.txt"
|
16
|
+
]
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.homepage = %q{https://github.com/vietthventura/js-routes}
|
19
|
+
s.licenses = ["MIT"]
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.summary = %q{Brings Rails named routes to javascript}
|
22
|
+
|
23
|
+
s.add_runtime_dependency(%q<railties>, [">= 3.2"])
|
24
|
+
s.add_runtime_dependency(%q<sprockets-rails>)
|
25
|
+
s.add_development_dependency(%q<rspec>, [">= 3.0.0"])
|
26
|
+
s.add_development_dependency(%q<bundler>, [">= 1.1.0"])
|
27
|
+
s.add_development_dependency(%q<coffee-script>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<appraisal>, [">= 0.5.2"])
|
29
|
+
if defined?(JRUBY_VERSION)
|
30
|
+
s.add_development_dependency(%q<therubyrhino>, [">= 2.0.4"])
|
31
|
+
else
|
32
|
+
s.add_development_dependency(%q<byebug>)
|
33
|
+
s.add_development_dependency(%q<pry-byebug>)
|
34
|
+
s.add_development_dependency(%q<therubyracer>, [">= 0.12.3"])
|
35
|
+
end
|
36
|
+
end
|
data/lib/js-routes.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'js_routes'
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class JsRoutesSprocketsExtension
|
2
|
+
def initialize(filename, &block)
|
3
|
+
@filename = filename
|
4
|
+
@source = block.call
|
5
|
+
end
|
6
|
+
|
7
|
+
def render(context, empty_hash_wtf)
|
8
|
+
self.class.run(@filename, @source, context)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.run(filename, source, context)
|
12
|
+
if context.logical_path == 'js-routes'
|
13
|
+
routes = Rails.root.join('config', 'routes.rb').to_s
|
14
|
+
context.depend_on(routes)
|
15
|
+
end
|
16
|
+
source
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.call(input)
|
20
|
+
filename = input[:filename]
|
21
|
+
source = input[:data]
|
22
|
+
context = input[:environment].context_class.new(input)
|
23
|
+
|
24
|
+
result = run(filename, source, context)
|
25
|
+
context.metadata.merge(data: result)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
class Engine < ::Rails::Engine
|
31
|
+
require 'sprockets/version'
|
32
|
+
v2 = Gem::Dependency.new('', ' ~> 2')
|
33
|
+
vgte3 = Gem::Dependency.new('', ' >= 3')
|
34
|
+
sprockets_version = Gem::Version.new(::Sprockets::VERSION).release
|
35
|
+
initializer_args = case sprockets_version
|
36
|
+
when -> (v) { v2.match?('', v) }
|
37
|
+
{ after: "sprockets.environment" }
|
38
|
+
when -> (v) { vgte3.match?('', v) }
|
39
|
+
{ after: :engines_blank_point, before: :finisher_hook }
|
40
|
+
else
|
41
|
+
raise StandardError, "Sprockets version #{sprockets_version} is not supported"
|
42
|
+
end
|
43
|
+
|
44
|
+
is_running_rails = defined?(Rails) && Rails.respond_to?(:version)
|
45
|
+
is_running_rails_32 = is_running_rails && Rails.version.match(/3\.2/)
|
46
|
+
|
47
|
+
initializer 'js-routes.dependent_on_routes', initializer_args do
|
48
|
+
case sprockets_version
|
49
|
+
when -> (v) { v2.match?('', v) },
|
50
|
+
-> (v) { vgte3.match?('', v) }
|
51
|
+
|
52
|
+
# It seems rails 3.2 is not working if
|
53
|
+
# `Rails.application.config.assets.configure` is used for
|
54
|
+
# registering preprocessor
|
55
|
+
if is_running_rails_32
|
56
|
+
Rails.application.assets.register_preprocessor(
|
57
|
+
"application/javascript",
|
58
|
+
JsRoutesSprocketsExtension,
|
59
|
+
)
|
60
|
+
else
|
61
|
+
# Other rails version, assumed newer
|
62
|
+
Rails.application.config.assets.configure do |config|
|
63
|
+
config.register_preprocessor(
|
64
|
+
"application/javascript",
|
65
|
+
JsRoutesSprocketsExtension,
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise StandardError, "Sprockets version #{sprockets_version} is not supported"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/js_routes.rb
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'js_routes/engine' if defined?(Rails)
|
3
|
+
require 'js_routes/version'
|
4
|
+
|
5
|
+
class JsRoutes
|
6
|
+
|
7
|
+
#
|
8
|
+
# OPTIONS
|
9
|
+
#
|
10
|
+
|
11
|
+
DEFAULT_PATH = File.join('app','assets','javascripts','routes.js')
|
12
|
+
|
13
|
+
DEFAULTS = {
|
14
|
+
namespace: "Routes",
|
15
|
+
exclude: [],
|
16
|
+
include: //,
|
17
|
+
file: DEFAULT_PATH,
|
18
|
+
prefix: -> { Rails.application.config.relative_url_root || "" },
|
19
|
+
url_links: false,
|
20
|
+
camel_case: false,
|
21
|
+
default_url_options: {},
|
22
|
+
compact: false,
|
23
|
+
serializer: nil,
|
24
|
+
special_options_key: "_options",
|
25
|
+
application: -> { Rails.application }
|
26
|
+
}
|
27
|
+
|
28
|
+
NODE_TYPES = {
|
29
|
+
GROUP: 1,
|
30
|
+
CAT: 2,
|
31
|
+
SYMBOL: 3,
|
32
|
+
OR: 4,
|
33
|
+
STAR: 5,
|
34
|
+
LITERAL: 6,
|
35
|
+
SLASH: 7,
|
36
|
+
DOT: 8
|
37
|
+
}
|
38
|
+
|
39
|
+
LAST_OPTIONS_KEY = "options".freeze
|
40
|
+
FILTERED_DEFAULT_PARTS = [:controller, :action, :subdomain]
|
41
|
+
URL_OPTIONS = [:protocol, :domain, :host, :port, :subdomain]
|
42
|
+
|
43
|
+
class Configuration < Struct.new(*DEFAULTS.keys)
|
44
|
+
def initialize(attributes = nil)
|
45
|
+
assign(DEFAULTS)
|
46
|
+
return unless attributes
|
47
|
+
assign(attributes)
|
48
|
+
end
|
49
|
+
|
50
|
+
def assign(attributes)
|
51
|
+
attributes.each do |attribute, value|
|
52
|
+
value = value.call if value.is_a?(Proc)
|
53
|
+
send(:"#{attribute}=", value)
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](attribute)
|
59
|
+
send(attribute)
|
60
|
+
end
|
61
|
+
|
62
|
+
def merge(attributes)
|
63
|
+
clone.assign(attributes)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_hash
|
67
|
+
Hash[*members.zip(values).flatten(1)].symbolize_keys
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# API
|
73
|
+
#
|
74
|
+
|
75
|
+
class << self
|
76
|
+
def setup(&block)
|
77
|
+
configuration.tap(&block) if block
|
78
|
+
end
|
79
|
+
|
80
|
+
def options
|
81
|
+
ActiveSupport::Deprecation.warn('JsRoutes.options method is deprecated use JsRoutes.configuration instead')
|
82
|
+
configuration
|
83
|
+
end
|
84
|
+
|
85
|
+
def configuration
|
86
|
+
@configuration ||= Configuration.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def generate(opts = {})
|
90
|
+
new(opts).generate
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate!(file_name=nil, opts = {})
|
94
|
+
if file_name.is_a?(Hash)
|
95
|
+
opts = file_name
|
96
|
+
file_name = opts[:file]
|
97
|
+
end
|
98
|
+
new(opts).generate!(file_name)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Under rails 3.1.1 and higher, perform a check to ensure that the
|
102
|
+
# full environment will be available during asset compilation.
|
103
|
+
# This is required to ensure routes are loaded.
|
104
|
+
def assert_usable_configuration!
|
105
|
+
if 3 == Rails::VERSION::MAJOR && !Rails.application.config.assets.initialize_on_precompile
|
106
|
+
raise("Cannot precompile js-routes unless environment is initialized. Please set config.assets.initialize_on_precompile to true.")
|
107
|
+
end
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
def json(string)
|
112
|
+
ActiveSupport::JSON.encode(string)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Implementation
|
118
|
+
#
|
119
|
+
|
120
|
+
def initialize(options = {})
|
121
|
+
@configuration = self.class.configuration.merge(options)
|
122
|
+
end
|
123
|
+
|
124
|
+
def generate
|
125
|
+
# Ensure routes are loaded. If they're not, load them.
|
126
|
+
if named_routes.to_a.empty? && application.respond_to?(:reload_routes!)
|
127
|
+
application.reload_routes!
|
128
|
+
end
|
129
|
+
|
130
|
+
{
|
131
|
+
"GEM_VERSION" => JsRoutes::VERSION,
|
132
|
+
"ROUTES" => js_routes,
|
133
|
+
"DEPRECATED_BEHAVIOR" => Rails.version < "4",
|
134
|
+
"NODE_TYPES" => json(NODE_TYPES),
|
135
|
+
|
136
|
+
"APP_CLASS" => application.class.to_s,
|
137
|
+
"NAMESPACE" => json(@configuration.namespace),
|
138
|
+
"DEFAULT_URL_OPTIONS" => json(@configuration.default_url_options),
|
139
|
+
"PREFIX" => json(@configuration.prefix),
|
140
|
+
"SPECIAL_OPTIONS_KEY" => json(@configuration.special_options_key),
|
141
|
+
"SERIALIZER" => @configuration.serializer || json(nil),
|
142
|
+
}.inject(File.read(File.dirname(__FILE__) + "/routes.js")) do |js, (key, value)|
|
143
|
+
js.gsub!(key, value.to_s)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def generate!(file_name = nil)
|
148
|
+
# Some libraries like Devise do not yet loaded their routes so we will wait
|
149
|
+
# until initialization process finish
|
150
|
+
# https://github.com/railsware/js-routes/issues/7
|
151
|
+
Rails.configuration.after_initialize do
|
152
|
+
file_name ||= self.class.configuration['file']
|
153
|
+
File.open(Rails.root.join(file_name), 'w') do |f|
|
154
|
+
f.write generate
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
protected
|
160
|
+
|
161
|
+
def application
|
162
|
+
@configuration.application
|
163
|
+
end
|
164
|
+
|
165
|
+
def named_routes
|
166
|
+
application.routes.named_routes.to_a
|
167
|
+
end
|
168
|
+
|
169
|
+
def js_routes
|
170
|
+
js_routes = named_routes.sort_by(&:first).flat_map do |_, route|
|
171
|
+
[build_route_if_match(route)] + mounted_app_routes(route)
|
172
|
+
end.compact
|
173
|
+
"{\n" + js_routes.join(",\n") + "}\n"
|
174
|
+
end
|
175
|
+
|
176
|
+
def mounted_app_routes(route)
|
177
|
+
rails_engine_app = get_app_from_route(route)
|
178
|
+
if rails_engine_app.respond_to?(:superclass) && rails_engine_app.superclass == Rails::Engine && !route.path.anchored
|
179
|
+
rails_engine_app.routes.named_routes.map do |_, engine_route|
|
180
|
+
build_route_if_match(engine_route, route)
|
181
|
+
end
|
182
|
+
else
|
183
|
+
[]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_app_from_route(route)
|
188
|
+
# rails engine in Rails 4.2 use additional ActionDispatch::Routing::Mapper::Constraints, which contain app
|
189
|
+
if route.app.respond_to?(:app) && route.app.respond_to?(:constraints)
|
190
|
+
route.app.app
|
191
|
+
else
|
192
|
+
route.app
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def build_route_if_match(route, parent_route=nil)
|
197
|
+
if any_match?(route, parent_route, @configuration[:exclude]) || !any_match?(route, parent_route, @configuration[:include])
|
198
|
+
nil
|
199
|
+
else
|
200
|
+
build_js(route, parent_route)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def any_match?(route, parent_route, matchers)
|
205
|
+
full_route = [parent_route.try(:name), route.name].compact.join('_')
|
206
|
+
|
207
|
+
matchers = Array(matchers)
|
208
|
+
matchers.any? {|regex| full_route =~ regex}
|
209
|
+
end
|
210
|
+
|
211
|
+
def build_js(route, parent_route)
|
212
|
+
name = [parent_route.try(:name), route.name].compact
|
213
|
+
route_name = generate_route_name(name, (:path unless @configuration[:compact]))
|
214
|
+
parent_spec = parent_route.try(:path).try(:spec)
|
215
|
+
route_arguments = route_js_arguments(route, parent_spec)
|
216
|
+
url_link = generate_url_link(name, route_name, route_arguments, route)
|
217
|
+
_ = <<-JS.strip!
|
218
|
+
// #{name.join('.')} => #{parent_spec}#{route.path.spec}
|
219
|
+
// function(#{build_params(route.required_parts)})
|
220
|
+
#{route_name}: Utils.route(#{route_arguments})#{",\n" + url_link if url_link.length > 0}
|
221
|
+
JS
|
222
|
+
end
|
223
|
+
|
224
|
+
def route_js_arguments(route, parent_spec)
|
225
|
+
required_parts = route.required_parts
|
226
|
+
parts_table = route.parts.each_with_object({}) do |part, hash|
|
227
|
+
hash[part] = required_parts.include?(part)
|
228
|
+
end
|
229
|
+
default_options = route.defaults.select do |part, _|
|
230
|
+
FILTERED_DEFAULT_PARTS.exclude?(part) && URL_OPTIONS.include?(part) || parts_table[part]
|
231
|
+
end
|
232
|
+
[
|
233
|
+
# JS objects don't preserve the order of properties which is crucial,
|
234
|
+
# so array is a better choice.
|
235
|
+
parts_table.to_a,
|
236
|
+
default_options,
|
237
|
+
serialize(route.path.spec, parent_spec)
|
238
|
+
].map do |argument|
|
239
|
+
json(argument)
|
240
|
+
end.join(", ")
|
241
|
+
end
|
242
|
+
|
243
|
+
def generate_url_link(name, route_name, route_arguments, route)
|
244
|
+
return "" unless @configuration[:url_links]
|
245
|
+
<<-JS.strip!
|
246
|
+
#{generate_route_name(name, :url)}: Utils.route(#{route_arguments}, true)
|
247
|
+
JS
|
248
|
+
end
|
249
|
+
|
250
|
+
def generate_route_name(name, suffix)
|
251
|
+
route_name = name.join('_')
|
252
|
+
route_name << "_#{ suffix }" if suffix
|
253
|
+
@configuration[:camel_case] ? route_name.camelize(:lower) : route_name
|
254
|
+
end
|
255
|
+
|
256
|
+
def json(string)
|
257
|
+
self.class.json(string)
|
258
|
+
end
|
259
|
+
|
260
|
+
def build_params(required_parts)
|
261
|
+
params = required_parts + [LAST_OPTIONS_KEY]
|
262
|
+
params.join(", ")
|
263
|
+
end
|
264
|
+
|
265
|
+
# This function serializes Journey route into JSON structure
|
266
|
+
# We do not use Hash for human readable serialization
|
267
|
+
# And preffer Array serialization because it is shorter.
|
268
|
+
# Routes.js file will be smaller.
|
269
|
+
def serialize(spec, parent_spec=nil)
|
270
|
+
return nil unless spec
|
271
|
+
return spec.tr(':', '') if spec.is_a?(String)
|
272
|
+
result = serialize_spec(spec, parent_spec)
|
273
|
+
if parent_spec && result[1].is_a?(String)
|
274
|
+
result = [
|
275
|
+
# We encode node symbols as integer
|
276
|
+
# to reduce the routes.js file size
|
277
|
+
NODE_TYPES[:CAT],
|
278
|
+
serialize_spec(parent_spec),
|
279
|
+
result
|
280
|
+
]
|
281
|
+
end
|
282
|
+
result
|
283
|
+
end
|
284
|
+
|
285
|
+
def serialize_spec(spec, parent_spec=nil)
|
286
|
+
[
|
287
|
+
NODE_TYPES[spec.type],
|
288
|
+
serialize(spec.left, parent_spec),
|
289
|
+
spec.respond_to?(:right) && serialize(spec.right)
|
290
|
+
]
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|