volt 0.9.3.pre3 → 0.9.3.pre4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -2
- data/app/volt/models/user.rb +5 -0
- data/app/volt/tasks/store_tasks.rb +2 -2
- data/app/volt/tasks/user_tasks.rb +1 -1
- data/lib/volt/cli/asset_compile.rb +0 -2
- data/lib/volt/cli/generate.rb +8 -3
- data/lib/volt/cli.rb +3 -9
- data/lib/volt/controllers/actions.rb +6 -1
- data/lib/volt/controllers/http_controller.rb +16 -7
- data/lib/volt/controllers/model_controller.rb +21 -0
- data/lib/volt/models/array_model.rb +26 -3
- data/lib/volt/models/model.rb +18 -19
- data/lib/volt/models/persistors/array_store.rb +2 -10
- data/lib/volt/models/root_models/store_root.rb +17 -4
- data/lib/volt/models/validations/validations.rb +1 -1
- data/lib/volt/models/validators/unique_validator.rb +1 -1
- data/lib/volt/models.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +5 -4
- data/lib/volt/page/bindings/base_binding.rb +17 -0
- data/lib/volt/page/bindings/content_binding.rb +7 -5
- data/lib/volt/page/bindings/each_binding.rb +62 -51
- data/lib/volt/page/bindings/event_binding.rb +14 -0
- data/lib/volt/page/bindings/view_binding.rb +1 -1
- data/lib/volt/reactive/computation.rb +22 -13
- data/lib/volt/reactive/dependency.rb +0 -24
- data/lib/volt/router/routes.rb +35 -0
- data/lib/volt/server/forking_server.rb +26 -3
- data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +1 -1
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
- data/lib/volt/server/message_bus/peer_to_peer.rb +28 -21
- data/lib/volt/server/middleware/default_middleware_stack.rb +67 -0
- data/lib/volt/server/middleware/middleware_stack.rb +58 -0
- data/lib/volt/server/rack/http_request.rb +1 -1
- data/lib/volt/server/rack/http_resource.rb +7 -0
- data/lib/volt/server/rack/keep_alive.rb +20 -0
- data/lib/volt/server/socket_connection_handler.rb +10 -1
- data/lib/volt/server.rb +6 -76
- data/lib/volt/utils/promise_extensions.rb +5 -1
- data/lib/volt/utils/set_patch.rb +25 -0
- data/lib/volt/utils/timers.rb +12 -0
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +13 -1
- data/lib/volt/volt/server_setup/app.rb +19 -1
- data/lib/volt/volt/users.rb +11 -22
- data/lib/volt.rb +1 -0
- data/spec/apps/kitchen_sink/Gemfile +1 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -1
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +22 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +10 -0
- data/spec/apps/kitchen_sink/app/main/views/main/store_demo.html +9 -0
- data/spec/controllers/http_controller_spec.rb +27 -0
- data/spec/integration/bindings_spec.rb +29 -0
- data/spec/integration/store_spec.rb +7 -7
- data/spec/models/associations_spec.rb +1 -1
- data/spec/models/model_spec.rb +10 -0
- data/spec/models/permissions_spec.rb +7 -4
- data/spec/reactive/computation_spec.rb +33 -5
- data/spec/router/routes_spec.rb +69 -0
- data/spec/server/middleware/middleware_handler.rb +24 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/tasks/user_tasks_spec.rb +3 -2
- data/templates/project/Gemfile.tt +2 -2
- data/templates/project/config/base/index.html +5 -1
- metadata +10 -5
- data/spec/apps/kitchen_sink/app/main/views/main/store.html +0 -9
- data/templates/project/app/main/models/.empty_directory +0 -0
@@ -19,13 +19,20 @@ module Volt
|
|
19
19
|
value = @context.instance_eval(&@getter)
|
20
20
|
rescue => e
|
21
21
|
Volt.logger.error("EachBinding Error: #{e.inspect}")
|
22
|
+
if RUBY_PLATFORM == 'opal'
|
23
|
+
Volt.logger.error(`#{@getter}`)
|
24
|
+
else
|
25
|
+
Volt.logger.error(e.backtrace.join("\n"))
|
26
|
+
end
|
27
|
+
|
22
28
|
value = []
|
23
29
|
end
|
24
30
|
|
25
31
|
value
|
26
|
-
end.watch_and_resolve!
|
27
|
-
update
|
28
|
-
|
32
|
+
end.watch_and_resolve!(
|
33
|
+
method(:update),
|
34
|
+
method(:getter_fail)
|
35
|
+
)
|
29
36
|
end
|
30
37
|
|
31
38
|
# When a changed event happens, we update to the new size.
|
@@ -64,69 +71,73 @@ module Volt
|
|
64
71
|
end
|
65
72
|
|
66
73
|
def item_removed(position)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove
|
74
|
+
# Remove dependency
|
75
|
+
@templates[position].context.locals[:_index_dependency].remove
|
76
|
+
@templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
78
|
+
@templates[position].remove_anchors
|
79
|
+
@templates[position].remove
|
80
|
+
@templates.delete_at(position)
|
75
81
|
|
76
|
-
|
77
|
-
|
78
|
-
end
|
82
|
+
# Removed at the position, update context for every item after this position
|
83
|
+
update_indexes_after(position)
|
79
84
|
end
|
80
85
|
|
81
86
|
def item_added(position)
|
82
|
-
|
83
|
-
binding_name = @@binding_number
|
84
|
-
@@binding_number += 1
|
85
|
-
|
86
|
-
if position >= @templates.size
|
87
|
-
# Setup new bindings in the spot we want to insert the item
|
88
|
-
dom_section.insert_anchor_before_end(binding_name)
|
89
|
-
else
|
90
|
-
# Insert the item before an existing item
|
91
|
-
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
|
92
|
-
end
|
87
|
+
item_context = nil
|
93
88
|
|
94
|
-
|
95
|
-
|
96
|
-
item_context.locals[@item_name.to_sym] = proc { @value[item_context.locals[:_index_value]] }
|
89
|
+
binding_name = @@binding_number
|
90
|
+
@@binding_number += 1
|
97
91
|
|
98
|
-
|
99
|
-
|
92
|
+
if position >= @templates.size
|
93
|
+
# Setup new bindings in the spot we want to insert the item
|
94
|
+
dom_section.insert_anchor_before_end(binding_name)
|
95
|
+
else
|
96
|
+
# Insert the item before an existing item
|
97
|
+
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
|
98
|
+
end
|
100
99
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
100
|
+
# TODORW: :parent => @value may change
|
101
|
+
item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
|
102
|
+
item_context.locals[@item_name.to_sym] = proc do
|
103
|
+
# Fetch only whats there currently, no promises.
|
104
|
+
Volt.run_in_mode(:no_model_promises) do
|
105
|
+
# puts "GET AT: #{item_context.locals[:_index_value]}"
|
106
|
+
@value[item_context.locals[:_index_value]]
|
105
107
|
end
|
108
|
+
end
|
106
109
|
|
107
|
-
|
108
|
-
|
109
|
-
item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency
|
110
|
+
position_dependency = Dependency.new
|
111
|
+
item_context.locals[:_index_dependency] = position_dependency
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
113
|
+
# Get and set index
|
114
|
+
item_context.locals[:_index=] = proc do |val|
|
115
|
+
position_dependency.changed!
|
116
|
+
item_context.locals[:_index_value] = val
|
117
|
+
end
|
115
118
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
item_context.locals[@index_name.to_sym] = proc do
|
120
|
-
position_dependency.depend
|
121
|
-
item_context.locals[:_index_value]
|
122
|
-
end
|
123
|
-
end
|
119
|
+
# Get and set value
|
120
|
+
value_dependency = Dependency.new
|
121
|
+
item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency
|
124
122
|
|
125
|
-
|
126
|
-
|
123
|
+
item_context.locals["#{@item_name}=".to_sym] = proc do |val|
|
124
|
+
value_dependency.changed!
|
125
|
+
@value[item_context.locals[:_index_value]] = val
|
126
|
+
end
|
127
127
|
|
128
|
-
|
128
|
+
# If the user provides an each_with_index, we can assign the lookup for the index
|
129
|
+
# variable here.
|
130
|
+
if @index_name
|
131
|
+
item_context.locals[@index_name.to_sym] = proc do
|
132
|
+
position_dependency.depend
|
133
|
+
item_context.locals[:_index_value]
|
134
|
+
end
|
129
135
|
end
|
136
|
+
|
137
|
+
item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
|
138
|
+
@templates.insert(position, item_template)
|
139
|
+
|
140
|
+
update_indexes_after(position)
|
130
141
|
end
|
131
142
|
|
132
143
|
# When items are added or removed in the middle of the list, we need
|
@@ -40,6 +40,20 @@ module Volt
|
|
40
40
|
# Call the proc the user setup for the event in context,
|
41
41
|
# pass in the wrapper for the JS event
|
42
42
|
result = @context.instance_exec(event, &call_proc)
|
43
|
+
|
44
|
+
# The following doesn't work due to the promise already chained issue.
|
45
|
+
# # Ignore native objects.
|
46
|
+
# result = nil unless BasicObject === result
|
47
|
+
|
48
|
+
# # if the result is a promise, log an exception if it failed and wasn't
|
49
|
+
# # handled
|
50
|
+
# if result.is_a?(Promise) && !result.next
|
51
|
+
# result.fail do |err|
|
52
|
+
# Volt.logger.error("EventBinding Error: promise returned from event binding #{@event_name} was rejected")
|
53
|
+
# Volt.logger.error(err)
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
|
43
57
|
end
|
44
58
|
|
45
59
|
@listener = page.events.add(event_name, self, handler)
|
@@ -225,7 +225,7 @@ module Volt
|
|
225
225
|
def call_ready
|
226
226
|
if @controller
|
227
227
|
# Set the current section on the controller if it wants so it can manipulate
|
228
|
-
# the dom if needed
|
228
|
+
# the dom if needed.
|
229
229
|
# Only assign sections for action's, so we don't get AttributeSections bound
|
230
230
|
# also.
|
231
231
|
if @controller.respond_to?(:section=)
|
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Volt
|
2
4
|
class Computation
|
3
5
|
@@current = nil
|
4
|
-
@@flush_queue =
|
6
|
+
@@flush_queue = Set.new
|
5
7
|
|
6
8
|
def self.current=(val)
|
7
9
|
@@current = val
|
@@ -111,7 +113,7 @@ module Volt
|
|
111
113
|
@@timer = nil
|
112
114
|
|
113
115
|
computations = @@flush_queue
|
114
|
-
@@flush_queue =
|
116
|
+
@@flush_queue = Set.new
|
115
117
|
|
116
118
|
computations.each(&:compute!)
|
117
119
|
|
@@ -171,16 +173,12 @@ class Proc
|
|
171
173
|
end
|
172
174
|
|
173
175
|
# Does an watch and if the result is a promise, resolves the promise.
|
174
|
-
# #watch_and_resolve! takes
|
175
|
-
#
|
176
|
+
# #watch_and_resolve! takes two procs, one for the promise resolution (then), and
|
177
|
+
# one for promise rejection (fail).
|
176
178
|
#
|
177
179
|
# Example:
|
178
180
|
# -> { }
|
179
|
-
def watch_and_resolve!(yield_nil_for_unresolved_promise=false)
|
180
|
-
unless block_given?
|
181
|
-
fail 'watch_and_resolve! requires a block to call when the value is resolved or another value other than a promise is returned in the watch.'
|
182
|
-
end
|
183
|
-
|
181
|
+
def watch_and_resolve!(success, failure=nil, yield_nil_for_unresolved_promise=false)
|
184
182
|
# Keep results between runs
|
185
183
|
result = nil
|
186
184
|
|
@@ -194,24 +192,35 @@ class Proc
|
|
194
192
|
# Often you want a to be alerted that an unresolved promise is waiting
|
195
193
|
# to be resolved.
|
196
194
|
if yield_nil_for_unresolved_promise && !result.resolved?
|
197
|
-
|
195
|
+
success.call(nil)
|
198
196
|
end
|
199
197
|
|
200
|
-
|
198
|
+
# The handler gets called once the promise resolves or is rejected.
|
199
|
+
handler = lambda do |&after_handle|
|
201
200
|
# Check to make sure that a new value didn't get reactively pushed
|
202
201
|
# before the promise resolved.
|
203
202
|
if last_promise.is_a?(Promise) && last_promise == result
|
204
203
|
# Don't resolve if the computation was stopped
|
205
204
|
unless comp.stopped?
|
206
|
-
|
205
|
+
# Call the passed in proc
|
206
|
+
after_handle.call
|
207
207
|
end
|
208
208
|
|
209
209
|
# Clear result for GC
|
210
210
|
result = nil
|
211
211
|
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
result.then do |final|
|
216
|
+
# Call the success proc passing in the resolved value
|
217
|
+
handler.call { success.call(final) }
|
218
|
+
end.fail do |err|
|
219
|
+
# call the fail callback, passing in the error
|
220
|
+
handler.call { failure.call(err) if failure }
|
212
221
|
end
|
213
222
|
else
|
214
|
-
|
223
|
+
success.call(result)
|
215
224
|
|
216
225
|
# Clear result for GC
|
217
226
|
result = nil
|
@@ -1,29 +1,5 @@
|
|
1
|
-
# Temp until https://github.com/opal/opal/pull/596
|
2
1
|
require 'set'
|
3
2
|
|
4
|
-
class Set
|
5
|
-
def delete(o)
|
6
|
-
if include?(o)
|
7
|
-
@hash.delete(o)
|
8
|
-
true
|
9
|
-
else
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def delete_if
|
15
|
-
block_given? or return enum_for(__method__)
|
16
|
-
# @hash.delete_if should be faster, but using it breaks the order
|
17
|
-
# of enumeration in subclasses.
|
18
|
-
select { |o| yield o }.each { |o| @hash.delete(o) }
|
19
|
-
self
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_a
|
23
|
-
@hash.keys
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
3
|
module Volt
|
28
4
|
# Dependencies are used to track the current computation so it can be re-run
|
29
5
|
# at a later point if this dependency changes.
|
data/lib/volt/router/routes.rb
CHANGED
@@ -68,6 +68,7 @@ module Volt
|
|
68
68
|
end
|
69
69
|
|
70
70
|
# Add server side routes
|
71
|
+
|
71
72
|
def get(path, params)
|
72
73
|
create_route(:get, path, params)
|
73
74
|
end
|
@@ -88,6 +89,35 @@ module Volt
|
|
88
89
|
create_route(:delete, path, params)
|
89
90
|
end
|
90
91
|
|
92
|
+
#Create rest endpoints
|
93
|
+
def rest(path, params)
|
94
|
+
endpoints = (params.delete(:only) || [:index, :show, :create, :update, :destroy]).to_a
|
95
|
+
endpoints = endpoints - params.delete(:except).to_a
|
96
|
+
endpoints.each do |endpoint|
|
97
|
+
self.send(('restful_' + endpoint.to_s).to_sym, path, params)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def restful_index(base_path, params)
|
102
|
+
get(base_path, params.merge(action: 'index'))
|
103
|
+
end
|
104
|
+
|
105
|
+
def restful_create(base_path, params)
|
106
|
+
post(base_path, params.merge(action: 'create'))
|
107
|
+
end
|
108
|
+
|
109
|
+
def restful_show(base_path, params)
|
110
|
+
get(path_with_id(base_path), params.merge(action: 'show'))
|
111
|
+
end
|
112
|
+
|
113
|
+
def restful_update(base_path, params)
|
114
|
+
put(path_with_id(base_path), params.merge(action: 'update'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def restful_destroy(base_path, params)
|
118
|
+
delete(path_with_id(base_path), params.merge(action: 'destroy'))
|
119
|
+
end
|
120
|
+
|
91
121
|
# Takes in params and generates a path and the remaining params
|
92
122
|
# that should be shown in the url. The extra "unused" params
|
93
123
|
# will be tacked onto the end of the url ?param1=value1, etc...
|
@@ -296,5 +326,10 @@ module Volt
|
|
296
326
|
def has_binding?(string)
|
297
327
|
string.index('{{') && string.index('}}')
|
298
328
|
end
|
329
|
+
|
330
|
+
#Append an id to a given path
|
331
|
+
def path_with_id(base_path)
|
332
|
+
base_path + '/{{ id }}'
|
333
|
+
end
|
299
334
|
end
|
300
335
|
end
|
@@ -59,7 +59,7 @@ module Volt
|
|
59
59
|
@reader.close
|
60
60
|
|
61
61
|
volt_app = @server.boot_volt
|
62
|
-
@rack_app =
|
62
|
+
@rack_app = volt_app.middleware
|
63
63
|
|
64
64
|
# Set the drb object locally
|
65
65
|
@dispatcher = Dispatcher.new(volt_app)
|
@@ -110,7 +110,19 @@ module Volt
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
-
|
113
|
+
# When passing an object, Drb will not marshal it if any of its subobjects
|
114
|
+
# are not marshalable. So we split the marshable and not marshalbe objects
|
115
|
+
# then re-merge them so we get real copies of most values (which are
|
116
|
+
# needed in some cases) Then we merge them back into a new hash.
|
117
|
+
def call_on_child(env_base, env_other)
|
118
|
+
env = env_base
|
119
|
+
|
120
|
+
# TODO: this requires quite a few trips, there's probably a faster way
|
121
|
+
# to handle this.
|
122
|
+
env_other.each_pair do |key, value|
|
123
|
+
env[key] = value
|
124
|
+
end
|
125
|
+
|
114
126
|
status, headers, body = @rack_app.call(env)
|
115
127
|
|
116
128
|
# Extract the body to pass as a string. We need to do this
|
@@ -138,7 +150,18 @@ module Volt
|
|
138
150
|
if @exiting
|
139
151
|
[500, {}, 'Server Exiting']
|
140
152
|
else
|
141
|
-
|
153
|
+
env_base = {}
|
154
|
+
env_other = {}
|
155
|
+
|
156
|
+
env.each_pair do |key, value|
|
157
|
+
if [String, TrueClass, FalseClass, Array].include?(value.class)
|
158
|
+
env_base.merge!(key => value)
|
159
|
+
else
|
160
|
+
env_other.merge!(key => value)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
status, headers, body_str = @server_proxy.call_on_child(env_base, env_other)
|
142
165
|
|
143
166
|
[status, headers, StringIO.new(body_str)]
|
144
167
|
end
|
@@ -91,7 +91,7 @@ module Volt
|
|
91
91
|
begin
|
92
92
|
@message_encoder.send_message(@socket, message)
|
93
93
|
# 'Error: closed stream' comes in sometimes
|
94
|
-
rescue Errno::ECONNREFUSED, Errno::EPIPE,
|
94
|
+
rescue Errno::ECONNREFUSED, Errno::EPIPE, IOError => e # was also rescuing Error
|
95
95
|
if reconnect!
|
96
96
|
retry
|
97
97
|
else
|
@@ -34,7 +34,7 @@ module Volt
|
|
34
34
|
# Register this server as active with the database
|
35
35
|
def register
|
36
36
|
instances = @page.store._active_volt_instances
|
37
|
-
instances.where(server_id: @server_id).
|
37
|
+
instances.where(server_id: @server_id).first.then do |item|
|
38
38
|
ips = local_ips.join(',')
|
39
39
|
time = Time.now.to_i
|
40
40
|
if item
|
@@ -58,22 +58,26 @@ module Volt
|
|
58
58
|
attr_reader :server_id, :page
|
59
59
|
|
60
60
|
def initialize(volt_app)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
if Volt::DataStore.fetch.connected?
|
62
|
+
# Generate a guid
|
63
|
+
@server_id = SecureRandom.uuid
|
64
|
+
# The PeerConnection's to peers
|
65
|
+
@peer_connections = {}
|
66
|
+
# The server id's for each peer we're connected to
|
67
|
+
@peer_server_ids = {}
|
67
68
|
|
68
|
-
|
69
|
+
@page = volt_app.page
|
69
70
|
|
70
|
-
|
71
|
-
|
71
|
+
setup_peer_server
|
72
|
+
start_tracker
|
72
73
|
|
73
|
-
|
74
|
-
|
74
|
+
Thread.new do
|
75
|
+
sleep 1
|
75
76
|
|
76
|
-
|
77
|
+
connect_to_peers
|
78
|
+
end
|
79
|
+
else
|
80
|
+
Volt.logger.error('Unable to connect to the database. Volt will still run, but the message bus requires a database connection to setup connections between nodes, so the message bus has been disabled. This means updates will not be propigated between instances (server, console, runners, etc...)')
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
@@ -110,7 +114,7 @@ module Volt
|
|
110
114
|
def peers
|
111
115
|
instances = @page.store._active_volt_instances
|
112
116
|
|
113
|
-
instances.where(server_id: {'$ne' => @server_id}).
|
117
|
+
instances.where(server_id: {'$ne' => @server_id}).all.sync
|
114
118
|
end
|
115
119
|
|
116
120
|
def connect_to_peers
|
@@ -118,13 +122,16 @@ module Volt
|
|
118
122
|
# Start connecting to all at the same time. Since most will connect or
|
119
123
|
# timeout, this is the desired behaviour.
|
120
124
|
Thread.new do
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
125
|
+
# sometimes we get nil peers for some reason
|
126
|
+
if peer
|
127
|
+
peer_connection = PeerConnection.connect_to(self, peer._ips, peer._port)
|
128
|
+
|
129
|
+
if peer_connection
|
130
|
+
add_peer_connection(peer_connection)
|
131
|
+
else
|
132
|
+
# remove if not alive anymore.
|
133
|
+
still_alive?(peer._server_id)
|
134
|
+
end
|
128
135
|
end
|
129
136
|
end
|
130
137
|
end
|
@@ -177,7 +184,7 @@ module Volt
|
|
177
184
|
# Unable to write to the socket, retry until the instance is no
|
178
185
|
# longer marking its self as active in the database
|
179
186
|
peer_table = @page.store._active_volt_instances
|
180
|
-
peer = peer_table.where(server_id: peer_server_id).
|
187
|
+
peer = peer_table.where(server_id: peer_server_id).first.sync
|
181
188
|
if peer
|
182
189
|
# Found the peer, retry if it has reported in in the last 2
|
183
190
|
# minutes.
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Responsible for setting up all "out of the box" middleware on a Volt app.
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
require 'volt/server/rack/keep_alive'
|
5
|
+
require 'volt/server/rack/quiet_common_logger'
|
6
|
+
require 'volt/server/rack/opal_files'
|
7
|
+
require 'volt/server/rack/index_files'
|
8
|
+
require 'volt/server/rack/http_resource'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
module Volt
|
13
|
+
class DefaultMiddlewareStack
|
14
|
+
# Setup on the middleware we can setup before booting components
|
15
|
+
def self.preboot_setup(volt_app, rack_app)
|
16
|
+
# Should only be used in production
|
17
|
+
if Volt.config.deflate
|
18
|
+
rack_app.use Rack::Deflater
|
19
|
+
rack_app.use Rack::Chunked
|
20
|
+
end
|
21
|
+
|
22
|
+
rack_app.use Rack::ContentLength
|
23
|
+
rack_app.use Rack::KeepAlive
|
24
|
+
rack_app.use Rack::ConditionalGet
|
25
|
+
rack_app.use Rack::ETag
|
26
|
+
|
27
|
+
rack_app.use Rack::Session::Cookie, {
|
28
|
+
:key => 'rack.session',
|
29
|
+
# :domain => 'localhost.com',
|
30
|
+
:path => '/',
|
31
|
+
:expire_after => 2592000,
|
32
|
+
:secret => Volt.config.app_secret
|
33
|
+
}
|
34
|
+
|
35
|
+
rack_app.use QuietCommonLogger
|
36
|
+
rack_app.use Rack::ShowExceptions
|
37
|
+
end
|
38
|
+
|
39
|
+
# Setup the middleware that we need to wait for components to boot before we
|
40
|
+
# can set them up.
|
41
|
+
def self.postboot_setup(volt_app, rack_app)
|
42
|
+
component_paths = volt_app.component_paths
|
43
|
+
rack_app.map '/components' do
|
44
|
+
run ComponentHandler.new(component_paths)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Serve the opal files
|
48
|
+
opal_files = OpalFiles.new(rack_app, volt_app.app_path, volt_app.component_paths)
|
49
|
+
|
50
|
+
# Serve the main html files from public, also figure out
|
51
|
+
# which JS/CSS files to serve.
|
52
|
+
rack_app.use IndexFiles, volt_app, volt_app.component_paths, opal_files
|
53
|
+
|
54
|
+
rack_app.use HttpResource, volt_app, volt_app.router
|
55
|
+
|
56
|
+
rack_app.use Rack::Static,
|
57
|
+
urls: ['/'],
|
58
|
+
root: 'config/base',
|
59
|
+
index: '',
|
60
|
+
header_rules: [
|
61
|
+
[:all, { 'Cache-Control' => 'public, max-age=86400' }]
|
62
|
+
]
|
63
|
+
|
64
|
+
rack_app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Volt::MiddlewareStack provides an interface where app code can add custom
|
2
|
+
# rack middleware. Volt.current_app.middleware returns an instance of
|
3
|
+
# Volt::MiddlewareStack, and apps can call #use to add in more middleware.
|
4
|
+
|
5
|
+
module Volt
|
6
|
+
class MiddlewareStack
|
7
|
+
attr_reader :middlewares
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# Setup the next app
|
11
|
+
@middlewares = []
|
12
|
+
@maps = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Set the app that gets called after the middleware runs
|
16
|
+
# def set_app(app)
|
17
|
+
# @app = app
|
18
|
+
# end
|
19
|
+
|
20
|
+
def use(*args, &block)
|
21
|
+
@middlewares << [args, block]
|
22
|
+
|
23
|
+
# invalidate builder, so it gets built again
|
24
|
+
@builder = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def map(path, &block)
|
28
|
+
@maps << [path, block]
|
29
|
+
end
|
30
|
+
|
31
|
+
def run(app)
|
32
|
+
@app = app
|
33
|
+
end
|
34
|
+
|
35
|
+
# Builds a new Rack::Builder with the middleware and the app
|
36
|
+
def build
|
37
|
+
@builder = Rack::Builder.new
|
38
|
+
|
39
|
+
@maps.each do |path, block|
|
40
|
+
@builder.map(path, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
@middlewares.each do |middleware|
|
44
|
+
@builder.use(*middleware[0], &middleware[1])
|
45
|
+
end
|
46
|
+
|
47
|
+
@builder.run(@app)
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(env)
|
51
|
+
unless @builder
|
52
|
+
build
|
53
|
+
end
|
54
|
+
|
55
|
+
@builder.call(env)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -5,7 +5,7 @@ module Volt
|
|
5
5
|
# A request object for a HttpController. See Rack::Request for more details
|
6
6
|
class HttpRequest < Rack::Request
|
7
7
|
# Returns the request format
|
8
|
-
# /
|
8
|
+
# /acticles/index.html => html
|
9
9
|
# Defaults to the media_type of the request
|
10
10
|
def format
|
11
11
|
path_format || media_type
|
@@ -40,6 +40,13 @@ module Volt
|
|
40
40
|
namespace_module = Object.const_get(namespace.camelize.to_sym)
|
41
41
|
klass = namespace_module.const_get(controller_name.camelize.to_sym)
|
42
42
|
controller = klass.new(@volt_app, params, request)
|
43
|
+
|
44
|
+
# Use the 'meta' thread local to set the user_id for Volt.current_user
|
45
|
+
meta_data = {}
|
46
|
+
user_id = request.cookies['user_id']
|
47
|
+
meta_data['user_id'] = user_id if user_id
|
48
|
+
Thread.current['meta'] = meta_data
|
49
|
+
|
43
50
|
controller.perform(action)
|
44
51
|
end
|
45
52
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rack
|
2
|
+
# TODO: For some reason in Rack (or maybe thin), 304 headers close
|
3
|
+
# the http connection. We might need to make this check if keep
|
4
|
+
# alive was in the request.
|
5
|
+
class KeepAlive
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
status, headers, body = @app.call(env)
|
12
|
+
|
13
|
+
if status == 304 && env['HTTP_CONNECTION'] && env['HTTP_CONNECTION'].downcase == 'keep-alive'
|
14
|
+
headers['Connection'] = 'keep-alive'
|
15
|
+
end
|
16
|
+
|
17
|
+
[status, headers, body]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|