volt 0.9.6 → 0.9.7.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +6 -1
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/app/volt/models/active_volt_instance.rb +8 -6
- data/app/volt/models/user.rb +11 -2
- data/app/volt/models/volt_app_property.rb +8 -0
- data/app/volt/tasks/query_tasks.rb +23 -31
- data/app/volt/tasks/store_tasks.rb +2 -2
- data/app/volt/tasks/volt_admin_tasks.rb +24 -0
- data/docs/UPGRADE_GUIDE.md +6 -0
- data/lib/volt.rb +19 -12
- data/lib/volt/boot.rb +1 -0
- data/lib/volt/cli.rb +19 -8
- data/lib/volt/cli/console.rb +0 -1
- data/lib/volt/cli/generators.rb +14 -3
- data/lib/volt/cli/migrate.rb +26 -0
- data/lib/volt/config.rb +17 -4
- data/lib/volt/controllers/http_controller.rb +12 -0
- data/lib/volt/data_stores/base_adaptor_client.rb +2 -2
- data/lib/volt/data_stores/base_adaptor_server.rb +2 -0
- data/lib/volt/data_stores/data_store.rb +20 -14
- data/lib/volt/extra_core/class.rb +28 -14
- data/lib/volt/extra_core/hash.rb +5 -0
- data/lib/volt/extra_core/string.rb +3 -1
- data/lib/volt/helpers/time.rb +9 -43
- data/lib/volt/helpers/time/calculations.rb +204 -0
- data/lib/volt/helpers/time/distance.rb +63 -0
- data/lib/volt/helpers/time/duration.rb +71 -0
- data/lib/volt/helpers/time/local_calculations.rb +49 -0
- data/lib/volt/helpers/time/local_volt_time.rb +23 -0
- data/lib/volt/helpers/time/numeric.rb +59 -0
- data/lib/volt/helpers/time/volt_time.rb +170 -0
- data/lib/volt/models.rb +5 -0
- data/lib/volt/models/array_model.rb +33 -6
- data/lib/volt/models/associations.rb +146 -23
- data/lib/volt/models/buffer.rb +38 -41
- data/lib/volt/models/cursor.rb +15 -0
- data/lib/volt/models/errors.rb +11 -0
- data/lib/volt/models/field_helpers.rb +108 -68
- data/lib/volt/models/helpers/array_model.rb +4 -0
- data/lib/volt/models/helpers/base.rb +8 -1
- data/lib/volt/models/helpers/change_helpers.rb +31 -12
- data/lib/volt/models/helpers/defaults.rb +15 -0
- data/lib/volt/models/location.rb +20 -6
- data/lib/volt/models/migrations/migration.rb +23 -0
- data/lib/volt/models/migrations/migration_runner.rb +146 -0
- data/lib/volt/models/model.rb +38 -1
- data/lib/volt/models/permissions.rb +8 -1
- data/lib/volt/models/persistors/array_store.rb +87 -8
- data/lib/volt/models/persistors/base.rb +19 -0
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/page.rb +4 -1
- data/lib/volt/models/persistors/query/query_identifier.rb +102 -0
- data/lib/volt/models/persistors/query/query_listener.rb +57 -12
- data/lib/volt/models/root_models/root_models.rb +19 -0
- data/lib/volt/models/url.rb +11 -2
- data/lib/volt/models/validations/validations.rb +5 -2
- data/lib/volt/models/validators/type_validator.rb +11 -0
- data/lib/volt/models/validators/unique_validator.rb +2 -2
- data/lib/volt/page/bindings/attribute_binding.rb +23 -1
- data/lib/volt/page/targets/attribute_section.rb +7 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +44 -18
- data/lib/volt/page/targets/binding_document/tag_node.rb +41 -0
- data/lib/volt/page/tasks.rb +16 -8
- data/lib/volt/queries/live_query.rb +109 -0
- data/lib/volt/queries/live_query_pool.rb +58 -0
- data/lib/volt/queries/live_subquery.rb +0 -0
- data/lib/volt/queries/query_association_splitter.rb +31 -0
- data/lib/volt/queries/query_diff.rb +100 -0
- data/lib/volt/queries/query_runner.rb +110 -0
- data/lib/volt/queries/query_subscription.rb +80 -0
- data/lib/volt/queries/query_subscription_pool.rb +37 -0
- data/lib/volt/reactive/eventable.rb +8 -0
- data/lib/volt/reactive/reactive_array.rb +0 -4
- data/lib/volt/router/routes.rb +81 -31
- data/lib/volt/server/message_bus/base_message_bus.rb +9 -3
- data/lib/volt/server/message_bus/peer_to_peer.rb +6 -6
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
- data/lib/volt/server/middleware/default_middleware_stack.rb +12 -8
- data/lib/volt/server/rack/component_paths.rb +31 -4
- data/lib/volt/server/rack/http_content_types.rb +62 -0
- data/lib/volt/server/rack/http_resource.rb +1 -1
- data/lib/volt/server/rack/index_files.rb +8 -1
- data/lib/volt/server/rack/opal_files.rb +16 -1
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +32 -1
- data/lib/volt/server/socket_connection_handler.rb +16 -7
- data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -3
- data/lib/volt/spec/capybara.rb +4 -3
- data/lib/volt/spec/setup.rb +5 -0
- data/lib/volt/tasks/dispatcher.rb +3 -1
- data/lib/volt/utils/data_transformer.rb +4 -4
- data/lib/volt/utils/ejson.rb +19 -6
- data/lib/volt/utils/promise_extensions.rb +1 -1
- data/lib/volt/utils/time_opal_patch.rb +749 -0
- data/lib/volt/utils/time_patch.rb +11 -4
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +19 -11
- data/lib/volt/volt/properties.rb +24 -0
- data/lib/volt/volt/server_setup/app.rb +30 -7
- data/lib/volt/volt/users.rb +15 -3
- data/spec/apps/kitchen_sink/Gemfile +5 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/save_controller.rb +1 -1
- data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +4 -2
- data/spec/apps/kitchen_sink/app/main/models/post.rb +0 -1
- data/spec/apps/kitchen_sink/app/main/models/todo.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/views/mailers/reset_password.html +10 -0
- data/spec/apps/kitchen_sink/app/main/views/todos/index.html +2 -0
- data/spec/apps/kitchen_sink/config/app.rb +2 -0
- data/spec/apps/migrations/config/db/migrations/1445111704_migration1.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445113517_migration2.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445115200_migration3.rb +7 -0
- data/spec/extra_core/class_spec.rb +10 -0
- data/spec/helpers/distance_spec.rb +35 -0
- data/spec/helpers/duration_spec.rb +160 -0
- data/spec/helpers/volt_time_spec.rb +275 -0
- data/spec/integration/callbacks_spec.rb +2 -1
- data/spec/integration/http_endpoints_spec.rb +4 -0
- data/spec/integration/save_spec.rb +1 -1
- data/spec/integration/todos_spec.rb +7 -5
- data/spec/models/array_model_spec.rb +17 -3
- data/spec/models/associations_spec.rb +48 -1
- data/spec/models/field_helpers_spec.rb +7 -3
- data/spec/models/migrations/migration_runner_spec.rb +69 -0
- data/spec/models/model_spec.rb +42 -8
- data/spec/models/permissions_spec.rb +20 -8
- data/spec/models/persistors/array_store_spec.rb +18 -0
- data/spec/models/persistors/page_spec.rb +15 -10
- data/spec/models/persistors/store_spec.rb +13 -3
- data/spec/models/url_spec.rb +4 -3
- data/spec/models/user_spec.rb +6 -3
- data/spec/models/user_validation_spec.rb +3 -3
- data/spec/models/validations_spec.rb +4 -0
- data/spec/models/validators/block_validations_spec.rb +9 -5
- data/spec/models/validators/email_validator_spec.rb +2 -0
- data/spec/models/validators/lifecycle_callbacks_spec.rb +86 -0
- data/spec/models/validators/unique_validator_spec.rb +1 -0
- data/spec/page/path_string_renderer_spec.rb +5 -0
- data/spec/queries/live_query_spec.rb +16 -0
- data/spec/queries/query_association_splitter_spec.rb +14 -0
- data/spec/queries/query_diff_spec.rb +132 -0
- data/spec/queries/query_identifier_spec.rb +98 -0
- data/spec/queries/query_runner_spec.rb +63 -0
- data/spec/queries/query_tracker_spec.rb +141 -0
- data/spec/router/routes_spec.rb +52 -21
- data/spec/server/middleware/rack_content_types_spec.rb +78 -0
- data/spec/server/rack/asset_files_spec.rb +38 -30
- data/spec/spec_helper.rb +8 -0
- data/spec/utils/ejson_spec.rb +9 -8
- data/spec/utils/ejson_volt_time_spec.rb +65 -0
- data/templates/migration/migration.rb.tt +9 -0
- data/templates/newgem/gitignore.tt +1 -0
- data/templates/project/Gemfile.tt +19 -2
- data/templates/project/README.md.tt +6 -1
- data/templates/project/app/main/config/dependencies.rb +6 -0
- data/templates/project/config/app.rb.tt +18 -4
- data/volt.gemspec +2 -2
- metadata +73 -16
- data/app/volt/tasks/live_query/live_query_pool.rb +0 -48
- data/app/volt/tasks/live_query/query_tracker.rb +0 -92
- data/spec/tasks/live_query_spec.rb +0 -18
- data/spec/tasks/query_tasks.rb +0 -7
- data/spec/tasks/query_tracker_spec.rb +0 -145
data/lib/volt/router/routes.rb
CHANGED
@@ -5,38 +5,38 @@ module Volt
|
|
5
5
|
# a url to params, and params to url.
|
6
6
|
# routes do
|
7
7
|
# client "/about", _view: 'about'
|
8
|
-
# client "/blog/{id}/edit",
|
9
|
-
# client "/blog/{id}",
|
10
|
-
# client "/blog",
|
11
|
-
# client "/blog/new",
|
12
|
-
# client "/cool/{
|
8
|
+
# client "/blog/{{ id }}/edit", view: 'blog/edit', action: 'edit'
|
9
|
+
# client "/blog/{{ id }}", view: 'blog/show', action: 'show'
|
10
|
+
# client "/blog", view: 'blog'
|
11
|
+
# client "/blog/new", view: 'blog/new', action: 'new'
|
12
|
+
# client "/cool/{{ name }}", view: 'cool'
|
13
13
|
# end
|
14
14
|
#
|
15
15
|
# Using the routes above, we would generate the following:
|
16
16
|
#
|
17
17
|
# @direct_routes = {
|
18
|
-
# '/about' => {
|
19
|
-
# '/blog' => {
|
20
|
-
# '/blog/new' => {
|
18
|
+
# '/about' => {view: 'about'},
|
19
|
+
# '/blog' => {view: 'blog'}
|
20
|
+
# '/blog/new' => {view: 'blog/new', action: 'new'}
|
21
21
|
# }
|
22
22
|
#
|
23
23
|
# -- nil represents a terminal
|
24
|
-
# -- * represents any match
|
24
|
+
# -- * represents any match (in that section (between / and /))
|
25
25
|
# -- a number for a parameter means use the value in that number section
|
26
26
|
#
|
27
27
|
# @indirect_routes = {
|
28
28
|
# '*' => {
|
29
29
|
# 'edit' => {
|
30
|
-
# nil => {id: 1,
|
30
|
+
# nil => {id: 1, view: 'blog/edit', action: 'edit'}
|
31
31
|
# }
|
32
|
-
# nil => {id: 1,
|
32
|
+
# nil => {id: 1, view: 'blog/show', action: 'show'}
|
33
33
|
# }
|
34
34
|
# }
|
35
35
|
# }
|
36
36
|
#
|
37
37
|
# Match for params
|
38
38
|
# @param_matches = [
|
39
|
-
# {id: nil,
|
39
|
+
# {id: nil, view: 'blog/edit', action: 'edit'} => Proc.new {|params| "/blog/#{params.id}/edit", params.reject {|k,v| k == :id }}
|
40
40
|
# ]
|
41
41
|
class Routes
|
42
42
|
def initialize
|
@@ -68,7 +68,7 @@ module Volt
|
|
68
68
|
end
|
69
69
|
|
70
70
|
# Add server side routes
|
71
|
-
|
71
|
+
|
72
72
|
def get(path, params)
|
73
73
|
create_route(:get, path, params)
|
74
74
|
end
|
@@ -190,17 +190,29 @@ module Volt
|
|
190
190
|
if part.nil?
|
191
191
|
if node[part]
|
192
192
|
# We found a match, replace the bindings and return
|
193
|
-
# TODO:
|
193
|
+
# TODO: Handle nested
|
194
194
|
setup_bindings_in_params(original_parts, node[part])
|
195
195
|
else
|
196
196
|
false
|
197
197
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
198
|
+
else
|
199
|
+
if (new_node = node[part])
|
200
|
+
# Direct match for section, continue
|
201
|
+
result = match_path(original_parts, parts, new_node)
|
202
|
+
return result if result
|
203
|
+
end
|
204
|
+
if (new_node = node['*'])
|
205
|
+
# Match on binding single section
|
206
|
+
result = match_path(original_parts, parts, new_node)
|
207
|
+
return result if result
|
208
|
+
end
|
209
|
+
if ((params = node['**']) && params && (params = params[nil]))
|
210
|
+
# Match on binding multiple sections
|
211
|
+
result = setup_bindings_in_params(original_parts, params)
|
212
|
+
return result if result
|
213
|
+
end
|
214
|
+
|
215
|
+
return false
|
204
216
|
end
|
205
217
|
end
|
206
218
|
|
@@ -214,6 +226,10 @@ module Volt
|
|
214
226
|
if value.is_a?(Fixnum)
|
215
227
|
# Lookup the param's value in the original url parts
|
216
228
|
params[key] = original_parts[value]
|
229
|
+
elsif value.is_a?(Range)
|
230
|
+
# When doing multiple section bindings, we lookup the parts as a range
|
231
|
+
# then join them with /
|
232
|
+
params[key] = original_parts[value].join('/')
|
217
233
|
end
|
218
234
|
end
|
219
235
|
|
@@ -231,10 +247,24 @@ module Volt
|
|
231
247
|
parts.each_with_index do |part, index|
|
232
248
|
if has_binding?(part)
|
233
249
|
# Strip off {{ and }}
|
234
|
-
|
250
|
+
section_matcher, multipart = binding_extract(part)
|
251
|
+
|
252
|
+
if multipart
|
253
|
+
# Match anything for the rest of the url (multiple sections)
|
254
|
+
|
255
|
+
# check that the splat is at the end of the url
|
256
|
+
if index != (parts.size-1)
|
257
|
+
raise "The splat (*) operator can only be used at the end of a url"
|
258
|
+
end
|
259
|
+
|
260
|
+
part = '**'
|
261
|
+
index = (index..-1)
|
262
|
+
else
|
263
|
+
# Match anything in a single section, set the part to be '*'
|
264
|
+
part = '*'
|
265
|
+
end
|
235
266
|
|
236
|
-
|
237
|
-
part = '*'
|
267
|
+
params[section_matcher] = index
|
238
268
|
end
|
239
269
|
|
240
270
|
node = (node[part] ||= {})
|
@@ -251,7 +281,8 @@ module Volt
|
|
251
281
|
if has_binding?(part)
|
252
282
|
# Setup a nil param that can match anything, but gets
|
253
283
|
# assigned into the url
|
254
|
-
|
284
|
+
section_matcher, _ = binding_extract(part)
|
285
|
+
params[section_matcher] = nil
|
255
286
|
end
|
256
287
|
end
|
257
288
|
|
@@ -269,12 +300,12 @@ module Volt
|
|
269
300
|
|
270
301
|
url = parts.map do |part|
|
271
302
|
val = if has_binding?(part)
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
303
|
+
# Get the
|
304
|
+
binding, _ = binding_extract(part)
|
305
|
+
input_params.delete(binding)
|
306
|
+
else
|
307
|
+
part
|
308
|
+
end
|
278
309
|
|
279
310
|
val
|
280
311
|
end.join('/')
|
@@ -319,7 +350,9 @@ module Volt
|
|
319
350
|
end
|
320
351
|
|
321
352
|
def url_parts(path)
|
322
|
-
|
353
|
+
# Remove start and end / and then split, keeping any blanks between
|
354
|
+
# sections (so // would get rejoined with the double)
|
355
|
+
path.gsub(/^\//, '').chomp('/').split('/', -1)
|
323
356
|
end
|
324
357
|
|
325
358
|
# Check if a string has a binding in it
|
@@ -331,5 +364,22 @@ module Volt
|
|
331
364
|
def path_with_id(base_path)
|
332
365
|
base_path + '/{{ id }}'
|
333
366
|
end
|
367
|
+
|
368
|
+
# @param: The binding part of the url eg: "{{ binding }} or {{ *binding }}"
|
369
|
+
# @return: a symbol for the binding variable eg: :binding, and a boolean for
|
370
|
+
# if the binding matches multiple sections.
|
371
|
+
def binding_extract(part)
|
372
|
+
section_matcher = part[2...-2].strip
|
373
|
+
|
374
|
+
multipart_matcher = false
|
375
|
+
|
376
|
+
if section_matcher[0] == '*'
|
377
|
+
# Match anything for the rest of the url (multiple sections)
|
378
|
+
section_matcher = section_matcher[1..-1]
|
379
|
+
multipart_matcher = true
|
380
|
+
end
|
381
|
+
|
382
|
+
return section_matcher.to_sym, multipart_matcher
|
383
|
+
end
|
334
384
|
end
|
335
385
|
end
|
@@ -5,13 +5,15 @@
|
|
5
5
|
#
|
6
6
|
# MessageBus instances inherit from MessageBus::BaseMessageBus and provide
|
7
7
|
# two methods 'publish' and 'subscribe'. They should be inside of
|
8
|
-
# Volt::MessageBus.
|
8
|
+
# Volt::MessageBus. Be sure to
|
9
|
+
# ```require 'volt/server/message_bus/base_message_bus'```
|
9
10
|
#
|
10
11
|
# publish should take a channel name and a message and deliver the message to
|
11
12
|
# any subscried listeners.
|
12
13
|
#
|
13
14
|
# subscribe should take a channel name and a block. It should yield a message
|
14
|
-
# to the block if a message is published to the channel.
|
15
|
+
# to the block if a message is published to the channel. It should return an
|
16
|
+
# object with a ```remove``` method that will remove the subscription.
|
15
17
|
#
|
16
18
|
# The implementation details of the pub/sub connection are left to the
|
17
19
|
# implemntation. If the user needs to configure server addresses, Volt.config
|
@@ -29,9 +31,13 @@
|
|
29
31
|
# NOTE: in the future, we plan to add support for round robbin message receiving
|
30
32
|
# and other patterns.
|
31
33
|
|
34
|
+
require 'volt/reactive/eventable'
|
35
|
+
|
32
36
|
module Volt
|
33
37
|
module MessageBus
|
34
38
|
class BaseMessageBus
|
39
|
+
include Eventable
|
40
|
+
|
35
41
|
# MessagesBus's should take an instance of a Volt::App
|
36
42
|
def initialize(volt_app)
|
37
43
|
raise "Not implemented"
|
@@ -54,4 +60,4 @@ module Volt
|
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
57
|
-
end
|
63
|
+
end
|
@@ -40,6 +40,7 @@ require 'volt/server/message_bus/peer_to_peer/server_tracker'
|
|
40
40
|
require 'volt/server/message_bus/peer_to_peer/peer_server'
|
41
41
|
require 'volt/server/message_bus/peer_to_peer/peer_connection'
|
42
42
|
require 'volt/server/message_bus/base_message_bus'
|
43
|
+
require_relative '../../../../app/volt/models/active_volt_instance'
|
43
44
|
|
44
45
|
# TODO: Right now the message bus uses threads, we should switch it to use a
|
45
46
|
# single thread and some form of select:
|
@@ -50,7 +51,6 @@ module Volt
|
|
50
51
|
class PeerToPeer < BaseMessageBus
|
51
52
|
# How long without an update before we mark an instance as dead (in seconds)
|
52
53
|
DEAD_TIME = 20
|
53
|
-
include Eventable
|
54
54
|
|
55
55
|
# Use subscribe instead of on provided in Eventable
|
56
56
|
alias_method :subscribe, :on
|
@@ -60,7 +60,7 @@ module Volt
|
|
60
60
|
def initialize(volt_app)
|
61
61
|
@volt_app = volt_app
|
62
62
|
|
63
|
-
if Volt::DataStore.fetch.connected?
|
63
|
+
if Volt::DataStore.fetch(volt_app).connected?
|
64
64
|
# Generate a guid
|
65
65
|
@server_id = SecureRandom.uuid
|
66
66
|
# The PeerConnection's to peers
|
@@ -112,9 +112,9 @@ module Volt
|
|
112
112
|
|
113
113
|
# Return an array of peer records.
|
114
114
|
def peers
|
115
|
-
instances = @volt_app.store.
|
115
|
+
instances = @volt_app.store.active_volt_instances
|
116
116
|
|
117
|
-
instances.where
|
117
|
+
instances.where {|svr| svr.server_id !~ @server_id }.all.sync
|
118
118
|
end
|
119
119
|
|
120
120
|
def connect_to_peers
|
@@ -186,7 +186,7 @@ module Volt
|
|
186
186
|
if peer
|
187
187
|
# Found the peer, retry if it has reported in in the last 2
|
188
188
|
# minutes.
|
189
|
-
if peer._time > (Time.now.to_i - DEAD_TIME)
|
189
|
+
if peer._time.to_i > (Time.now.to_i - DEAD_TIME)
|
190
190
|
# Peer reported in less than 2 minutes ago
|
191
191
|
return true
|
192
192
|
else
|
@@ -200,4 +200,4 @@ module Volt
|
|
200
200
|
|
201
201
|
end
|
202
202
|
end
|
203
|
-
end
|
203
|
+
end
|
@@ -36,7 +36,7 @@ module Volt
|
|
36
36
|
instances = @volt_app.store.active_volt_instances
|
37
37
|
instances.where(server_id: @server_id).first.then do |item|
|
38
38
|
ips = local_ips.join(',')
|
39
|
-
time = Time.now
|
39
|
+
time = Time.now
|
40
40
|
if item
|
41
41
|
item.assign_attributes(ips: ips, time: time, port: @port)
|
42
42
|
else
|
@@ -7,6 +7,7 @@ require 'volt/server/rack/opal_files'
|
|
7
7
|
require 'volt/server/rack/index_files'
|
8
8
|
require 'volt/server/rack/http_resource'
|
9
9
|
require 'volt/server/rack/sprockets_helpers_setup'
|
10
|
+
require 'volt/server/rack/http_content_types'
|
10
11
|
|
11
12
|
|
12
13
|
|
@@ -14,16 +15,17 @@ module Volt
|
|
14
15
|
class DefaultMiddlewareStack
|
15
16
|
# Setup on the middleware we can setup before booting components
|
16
17
|
def self.preboot_setup(volt_app, rack_app)
|
18
|
+
rack_app.use Rack::Chunked
|
17
19
|
# Should only be used in production
|
18
20
|
if Volt.config.deflate
|
19
21
|
rack_app.use Rack::Deflater
|
20
|
-
rack_app.use Rack::Chunked
|
21
22
|
end
|
22
23
|
|
23
24
|
rack_app.use Rack::ContentLength
|
24
25
|
rack_app.use Rack::KeepAlive
|
25
26
|
rack_app.use Rack::ConditionalGet
|
26
27
|
rack_app.use Rack::ETag
|
28
|
+
rack_app.use Rack::HttpContentTypes
|
27
29
|
|
28
30
|
rack_app.use Rack::Session::Cookie, {
|
29
31
|
key: 'rack.session',
|
@@ -42,6 +44,7 @@ module Volt
|
|
42
44
|
# Setup the middleware that we need to wait for components to boot before we
|
43
45
|
# can set them up.
|
44
46
|
def self.postboot_setup(volt_app, rack_app)
|
47
|
+
|
45
48
|
# Serve the opal files
|
46
49
|
opal_files = OpalFiles.new(rack_app, volt_app.app_url, volt_app.app_path, volt_app.component_paths)
|
47
50
|
volt_app.opal_files = opal_files
|
@@ -55,16 +58,17 @@ module Volt
|
|
55
58
|
|
56
59
|
rack_app.use HttpResource, volt_app, volt_app.router
|
57
60
|
|
61
|
+
|
58
62
|
# serve assets from public
|
59
63
|
rack_app.use Rack::Static,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
urls: [''],
|
65
|
+
root: ['public'],
|
66
|
+
index: 'index.html',
|
67
|
+
header_rules: [
|
68
|
+
[:all, { 'Cache-Control' => 'public, max-age=86400' }]
|
69
|
+
]
|
66
70
|
|
67
71
|
rack_app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }
|
68
72
|
end
|
69
73
|
end
|
70
|
-
end
|
74
|
+
end
|
@@ -12,13 +12,16 @@ module Volt
|
|
12
12
|
# Find all app folders
|
13
13
|
@app_folders ||= begin
|
14
14
|
volt_app = File.expand_path(File.join(File.dirname(__FILE__), '../../../../app'))
|
15
|
-
app_folders = [volt_app, "#{@root}/app", "#{@root}/vendor/app"].map { |f| File.expand_path(f) }
|
16
15
|
|
17
16
|
# Gem folders with volt in them
|
18
17
|
# TODO: we should probably qualify this a bit more
|
19
|
-
app_folders
|
20
|
-
|
21
|
-
|
18
|
+
app_folders = [volt_app]
|
19
|
+
app_folders += Gem.loaded_specs.values.
|
20
|
+
select {|gem| gem.name =~ /^volt/ }.
|
21
|
+
sort { |a, b| dependent_sort(a, b) }.
|
22
|
+
map {|gem| "#{gem.full_gem_path}/app" }
|
23
|
+
|
24
|
+
app_folders += ["#{@root}/app", "#{@root}/vendor/app"].map { |f| File.expand_path(f) }
|
22
25
|
|
23
26
|
app_folders.uniq
|
24
27
|
end
|
@@ -77,6 +80,7 @@ module Volt
|
|
77
80
|
|
78
81
|
# Delay the loading of views
|
79
82
|
volt_app.templates.template_loader = -> { load_views_and_routes(volt_app) }
|
83
|
+
volt_app.url.routes_loader = -> { load_views_and_routes(volt_app) }
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
@@ -95,6 +99,10 @@ module Volt
|
|
95
99
|
# Evaluate returned code, the ```volt_app``` variable is set for access.
|
96
100
|
eval(code)
|
97
101
|
end
|
102
|
+
|
103
|
+
# Clear template loader
|
104
|
+
volt_app.templates.template_loader = nil
|
105
|
+
volt_app.url.routes_loader = nil
|
98
106
|
end
|
99
107
|
|
100
108
|
# Returns all paths for a specific component
|
@@ -119,5 +127,24 @@ module Volt
|
|
119
127
|
|
120
128
|
folders.flatten
|
121
129
|
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# Determine if Gem::Specification b dependes on a
|
134
|
+
def dependent?(a, b)
|
135
|
+
name = a.name
|
136
|
+
b.dependencies.any? {|dep| dep.type == :runtime && dep.name == a.name }
|
137
|
+
end
|
138
|
+
|
139
|
+
def dependent_sort(a, b)
|
140
|
+
if dependent?(a, b)
|
141
|
+
-1
|
142
|
+
elsif dependent?(b, a)
|
143
|
+
1
|
144
|
+
else
|
145
|
+
0
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
122
149
|
end
|
123
150
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Middleware for parsing params of different restful content in HTTP endpoints.
|
2
|
+
# Content-type: application/json - is supported out of the box
|
3
|
+
#
|
4
|
+
# Much thanks to @achiu (https://github.com/achiu) for original version of this middleware
|
5
|
+
#
|
6
|
+
# New Parsers can be added in your app's config:
|
7
|
+
# Volt.setup do |config|
|
8
|
+
# config.http_content_types = {
|
9
|
+
# parsers: {
|
10
|
+
# 'application/roll' => proc { |body| {'rick_says' => 'never gonna give you up'}}
|
11
|
+
# }
|
12
|
+
# }
|
13
|
+
# end
|
14
|
+
|
15
|
+
module Rack
|
16
|
+
class HttpContentTypes
|
17
|
+
|
18
|
+
POST_BODY = 'rack.input'.freeze
|
19
|
+
FORM_INPUT = 'rack.request.form_input'.freeze
|
20
|
+
FORM_HASH = 'rack.request.form_hash'.freeze
|
21
|
+
|
22
|
+
JSON_PARSER = proc { |data| JSON.parse data }
|
23
|
+
ERROR_HANDLER = proc { |err, type| [400, {}, ['']] }
|
24
|
+
|
25
|
+
attr_reader :parsers, :handlers, :logger
|
26
|
+
|
27
|
+
def initialize(app, options = {})
|
28
|
+
@app = app
|
29
|
+
@options = Volt.config.http_content_types ? Volt.config.http_content_types.dup : {}
|
30
|
+
@options.merge!(options)
|
31
|
+
@parsers = @options[:parsers] || {}
|
32
|
+
@handlers = @options[:handlers] || {}
|
33
|
+
unless parsers.detect { |content_type, _| 'json'.match(content_type) }
|
34
|
+
@parsers.merge!({ %r{json} => JSON_PARSER })
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(env)
|
39
|
+
type = Rack::Request.new(env).media_type
|
40
|
+
parser = parsers.detect { |content_type, _| type.match(content_type) } if type
|
41
|
+
return @app.call(env) unless parser
|
42
|
+
body = env[POST_BODY].read ; env[POST_BODY].rewind
|
43
|
+
return @app.call(env) unless body && !body.empty?
|
44
|
+
begin
|
45
|
+
parsed = parser.last.call body
|
46
|
+
env.update FORM_HASH => parsed, FORM_INPUT => env[POST_BODY]
|
47
|
+
rescue StandardError => e
|
48
|
+
warn! e, type
|
49
|
+
handler = handlers.detect { |content_type, _| type.match(content_type) }
|
50
|
+
handler ||= ['default', ERROR_HANDLER]
|
51
|
+
return handler.last.call(e, type)
|
52
|
+
end
|
53
|
+
@app.call env
|
54
|
+
end
|
55
|
+
|
56
|
+
def warn!(error, content_type)
|
57
|
+
return unless Volt.logger
|
58
|
+
message = "[Rack::HttpContentType] Error on %s : %s" % [content_type, error.to_s]
|
59
|
+
Volt.logger.warn message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|