volt 0.9.3.pre1 → 0.9.3.pre2
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 +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +15 -1
- data/Gemfile +30 -3
- data/README.md +7 -2
- data/Rakefile +17 -0
- data/app/volt/models/user.rb +1 -1
- data/app/volt/tasks/live_query/live_query.rb +0 -1
- data/app/volt/tasks/live_query/live_query_pool.rb +8 -2
- data/app/volt/tasks/live_query/query_tracker.rb +2 -2
- data/app/volt/tasks/query_tasks.rb +10 -27
- data/app/volt/tasks/store_tasks.rb +6 -5
- data/app/volt/tasks/user_tasks.rb +2 -2
- data/docs/UPGRADE_GUIDE.md +14 -0
- data/lib/volt/boot.rb +1 -0
- data/lib/volt/cli/asset_compile.rb +25 -7
- data/lib/volt/cli/console.rb +6 -5
- data/lib/volt/cli/generate.rb +2 -2
- data/lib/volt/config.rb +2 -1
- data/lib/volt/controllers/http_controller.rb +4 -3
- data/lib/volt/controllers/model_controller.rb +41 -19
- data/lib/volt/controllers/template_helpers.rb +19 -0
- data/lib/volt/extra_core/array.rb +6 -0
- data/lib/volt/extra_core/hash.rb +8 -26
- data/lib/volt/extra_core/string.rb +1 -1
- data/lib/volt/models/array_model.rb +12 -4
- data/lib/volt/models/associations.rb +11 -13
- data/lib/volt/models/buffer.rb +1 -1
- data/lib/volt/models/model.rb +22 -13
- data/lib/volt/models/model_helpers/model_change_helpers.rb +0 -1
- data/lib/volt/models/model_helpers/model_helpers.rb +11 -0
- data/lib/volt/models/permissions.rb +9 -12
- data/lib/volt/models/persistors/array_store.rb +7 -7
- data/lib/volt/models/persistors/base.rb +9 -0
- data/lib/volt/models/persistors/cookies.rb +0 -4
- data/lib/volt/models/persistors/flash.rb +0 -4
- data/lib/volt/models/persistors/local_store.rb +0 -4
- data/lib/volt/models/persistors/model_store.rb +13 -21
- data/lib/volt/models/persistors/page.rb +22 -0
- data/lib/volt/models/persistors/params.rb +0 -4
- data/lib/volt/models/persistors/query/query_listener.rb +3 -2
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validators/unique_validator.rb +1 -1
- data/lib/volt/models.rb +1 -0
- data/lib/volt/page/bindings/attribute_binding.rb +2 -2
- data/lib/volt/page/bindings/base_binding.rb +7 -3
- data/lib/volt/page/bindings/bindings.rb +9 -0
- data/lib/volt/page/bindings/content_binding.rb +2 -2
- data/lib/volt/page/bindings/each_binding.rb +16 -12
- data/lib/volt/page/bindings/event_binding.rb +4 -4
- data/lib/volt/page/bindings/if_binding.rb +3 -3
- data/lib/volt/page/bindings/view_binding.rb +4 -4
- data/lib/volt/page/bindings/yield_binding.rb +3 -3
- data/lib/volt/page/channel.rb +6 -0
- data/lib/volt/page/channel_stub.rb +1 -1
- data/lib/volt/page/page.rb +20 -54
- data/lib/volt/page/path_string_renderer.rb +5 -6
- data/lib/volt/page/string_template_renderer.rb +2 -2
- data/lib/volt/page/targets/attribute_section.rb +47 -0
- data/lib/volt/page/targets/base_section.rb +5 -5
- data/lib/volt/page/targets/binding_document/component_node.rb +6 -1
- data/lib/volt/page/template_renderer.rb +4 -4
- data/lib/volt/reactive/computation.rb +32 -3
- data/lib/volt/router/routes.rb +5 -5
- data/lib/volt/server/component_templates.rb +30 -2
- data/lib/volt/server/forking_server.rb +2 -2
- data/lib/volt/server/message_bus/base_message_bus.rb +52 -0
- data/lib/volt/server/message_bus/message_encoder.rb +64 -0
- data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +186 -0
- data/lib/volt/server/message_bus/peer_to_peer/peer_server.rb +78 -0
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +57 -0
- data/lib/volt/server/message_bus/peer_to_peer/socket_with_timeout.rb +27 -0
- data/lib/volt/server/message_bus/peer_to_peer.rb +198 -0
- data/lib/volt/server/message_bus/redis.rb +1 -0
- data/lib/volt/server/rack/asset_files.rb +2 -2
- data/lib/volt/server/rack/component_paths.rb +1 -1
- data/lib/volt/server/rack/http_resource.rb +3 -2
- data/lib/volt/server/rack/opal_files.rb +6 -9
- data/lib/volt/server/websocket/websocket_handler.rb +0 -3
- data/lib/volt/server.rb +5 -3
- data/lib/volt/spec/setup.rb +11 -12
- data/lib/volt/tasks/dispatcher.rb +8 -12
- data/lib/volt/tasks/task_handler.rb +3 -2
- data/lib/volt/utils/csso_patch.rb +24 -0
- data/lib/volt/utils/promise_patch.rb +2 -0
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +73 -36
- data/lib/volt/volt/server_setup/app.rb +81 -0
- data/lib/volt.rb +22 -1
- data/spec/apps/kitchen_sink/Gemfile +1 -1
- data/spec/controllers/http_controller_spec.rb +5 -3
- data/spec/controllers/model_controller_spec.rb +2 -2
- data/spec/extra_core/hash_spec.rb +9 -0
- data/spec/integration/list_spec.rb +3 -3
- data/spec/models/associations_spec.rb +10 -2
- data/spec/models/dirty_spec.rb +7 -7
- data/spec/models/model_spec.rb +10 -2
- data/spec/models/permissions_spec.rb +9 -0
- data/spec/models/persistors/store_spec.rb +8 -0
- data/spec/page/bindings/content_binding_spec.rb +6 -2
- data/spec/page/bindings/each_binding_spec.rb +59 -0
- data/spec/page/bindings/if_binding_spec.rb +57 -0
- data/spec/page/path_string_renderer_spec.rb +5 -5
- data/spec/reactive/computation_spec.rb +65 -1
- data/spec/router/routes_spec.rb +1 -1
- data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -22
- data/spec/server/message_bus/message_encoder_spec.rb +49 -0
- data/spec/server/message_bus/peer_to_peer/peer_connection_spec.rb +108 -0
- data/spec/server/message_bus/peer_to_peer/peer_server_spec.rb +66 -0
- data/spec/server/message_bus/peer_to_peer/socket_with_timeout_spec.rb +11 -0
- data/spec/server/message_bus/peer_to_peer_spec.rb +11 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/server/rack/http_resource_spec.rb +4 -4
- data/spec/spec_helper.rb +16 -3
- data/spec/tasks/dispatcher_spec.rb +17 -5
- data/spec/tasks/live_query_spec.rb +1 -1
- data/spec/tasks/query_tracker_spec.rb +34 -34
- data/spec/tasks/user_tasks_spec.rb +4 -2
- data/templates/project/Gemfile.tt +14 -3
- data/templates/project/config/app.rb.tt +27 -2
- data/volt.gemspec +3 -8
- metadata +32 -101
- data/docs/FAQ.md +0 -7
|
@@ -6,8 +6,9 @@ require 'volt/server/rack/http_request'
|
|
|
6
6
|
module Volt
|
|
7
7
|
# Rack middleware for HttpController
|
|
8
8
|
class HttpResource
|
|
9
|
-
def initialize(app, router)
|
|
9
|
+
def initialize(app, volt_app, router)
|
|
10
10
|
@app = app
|
|
11
|
+
@volt_app = volt_app
|
|
11
12
|
@router = router
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -38,7 +39,7 @@ module Volt
|
|
|
38
39
|
|
|
39
40
|
namespace_module = Object.const_get(namespace.camelize.to_sym)
|
|
40
41
|
klass = namespace_module.const_get(controller_name.camelize.to_sym)
|
|
41
|
-
controller = klass.new(params, request)
|
|
42
|
+
controller = klass.new(@volt_app, params, request)
|
|
42
43
|
controller.perform(action)
|
|
43
44
|
end
|
|
44
45
|
end
|
|
@@ -22,7 +22,8 @@ module Volt
|
|
|
22
22
|
Opal.append_path(Volt.root + '/app')
|
|
23
23
|
Opal.append_path(Volt.root + '/lib')
|
|
24
24
|
|
|
25
|
-
Gem.loaded_specs.values.
|
|
25
|
+
Gem.loaded_specs.values.select {|gem| gem.name =~ /^volt/ }
|
|
26
|
+
.each do |gem|
|
|
26
27
|
['app', 'lib'].each do |folder|
|
|
27
28
|
path = gem.full_gem_path + "/#{folder}"
|
|
28
29
|
|
|
@@ -53,9 +54,10 @@ module Volt
|
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
if Volt.config.compress_css
|
|
56
|
-
|
|
57
|
-
require '
|
|
58
|
-
|
|
57
|
+
# Use csso for css compression by default.
|
|
58
|
+
require 'volt/utils/csso_patch'
|
|
59
|
+
require 'csso'
|
|
60
|
+
Csso.install(environment)
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
server.append_path(app_path)
|
|
@@ -65,11 +67,6 @@ module Volt
|
|
|
65
67
|
|
|
66
68
|
add_asset_folders(server)
|
|
67
69
|
|
|
68
|
-
# Add the opal load paths
|
|
69
|
-
Opal.paths.each do |path|
|
|
70
|
-
server.append_path(path)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
70
|
builder.map '/assets' do
|
|
74
71
|
run server
|
|
75
72
|
end
|
|
@@ -3,9 +3,6 @@ require 'volt/server/socket_connection_handler'
|
|
|
3
3
|
require 'volt/server/websocket/rack_server_adaptor'
|
|
4
4
|
|
|
5
5
|
module Volt
|
|
6
|
-
# Setup the dispatcher for the socket connection handler.
|
|
7
|
-
# SocketConnectionHandler.dispatcher = Dispatcher.new
|
|
8
|
-
|
|
9
6
|
class WebsocketHandler
|
|
10
7
|
def initialize(app)
|
|
11
8
|
# Setup the rack server and adaptor
|
data/lib/volt/server.rb
CHANGED
|
@@ -84,14 +84,16 @@ module Volt
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Only run ForkingServer if fork is supported in this env.
|
|
87
|
-
|
|
87
|
+
# NO_FORKING can be used to specify that you don't want to use the forking
|
|
88
|
+
# server.
|
|
89
|
+
if !can_fork || Volt.env.production? || Volt.env.test? || ENV['NO_FORKING']
|
|
88
90
|
# In production/test, we boot the app and run the server
|
|
89
91
|
#
|
|
90
92
|
# Sometimes the app is already booted, so we can skip if it is
|
|
91
93
|
boot_volt unless @volt_app
|
|
92
94
|
|
|
93
95
|
# Setup the dispatcher (it stays this class during its run)
|
|
94
|
-
SocketConnectionHandler.dispatcher = Dispatcher.new
|
|
96
|
+
SocketConnectionHandler.dispatcher = Dispatcher.new(@volt_app)
|
|
95
97
|
app.run(new_server)
|
|
96
98
|
else
|
|
97
99
|
# In developer
|
|
@@ -133,7 +135,7 @@ module Volt
|
|
|
133
135
|
# which JS/CSS files to serve.
|
|
134
136
|
@rack_app.use IndexFiles, @volt_app.component_paths, opal_files
|
|
135
137
|
|
|
136
|
-
@rack_app.use HttpResource, @volt_app.router
|
|
138
|
+
@rack_app.use HttpResource, @volt_app, @volt_app.router
|
|
137
139
|
|
|
138
140
|
@rack_app.use Rack::Static,
|
|
139
141
|
urls: ['/'],
|
data/lib/volt/spec/setup.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Volt
|
|
|
10
10
|
|
|
11
11
|
require 'volt/boot'
|
|
12
12
|
|
|
13
|
-
#
|
|
13
|
+
# Create a main volt app for tests
|
|
14
14
|
volt_app = Volt.boot(app_path)
|
|
15
15
|
|
|
16
16
|
unless RUBY_PLATFORM == 'opal'
|
|
@@ -32,11 +32,11 @@ module Volt
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
cleanup_db = -> do
|
|
35
|
-
|
|
35
|
+
volt_app.database.drop_database
|
|
36
36
|
|
|
37
37
|
# Clear cached for a reset
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
volt_app.page.instance_variable_set('@store', nil)
|
|
39
|
+
volt_app.reset_query_pool!
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
if RUBY_PLATFORM != 'opal'
|
|
@@ -44,6 +44,9 @@ module Volt
|
|
|
44
44
|
cleanup_db.call
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# Run everything in the context of this app
|
|
48
|
+
Thread.current['volt_app'] = volt_app
|
|
49
|
+
|
|
47
50
|
# Setup the spec collection accessors
|
|
48
51
|
# RSpec.shared_context "volt collections", {} do
|
|
49
52
|
RSpec.shared_examples_for 'volt collections', {} do
|
|
@@ -53,24 +56,20 @@ module Volt
|
|
|
53
56
|
let(:the_page) { Model.new }
|
|
54
57
|
let(:store) do
|
|
55
58
|
@__store_accessed = true
|
|
56
|
-
$page ||=
|
|
59
|
+
$page ||= volt_app.page
|
|
57
60
|
$page.store
|
|
58
61
|
end
|
|
62
|
+
let(:volt_app) { volt_app }
|
|
59
63
|
|
|
60
64
|
|
|
61
65
|
if RUBY_PLATFORM != 'opal'
|
|
62
|
-
after do
|
|
63
|
-
if @__store_accessed
|
|
66
|
+
after do |example|
|
|
67
|
+
if @__store_accessed || example.metadata[:type] == :feature
|
|
64
68
|
# Clear the database after each spec where we use store
|
|
65
69
|
cleanup_db.call
|
|
66
70
|
end
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
# Assume store is accessed in capyabara specs
|
|
70
|
-
before(:context, {type: :feature}) do
|
|
71
|
-
@__store_accessed = true
|
|
72
|
-
end
|
|
73
|
-
|
|
74
73
|
# Cleanup after integration tests also.
|
|
75
74
|
before(:example, {type: :feature}) do
|
|
76
75
|
@__store_accessed = true
|
|
@@ -9,6 +9,12 @@ module Volt
|
|
|
9
9
|
# When we pass the dispatcher over DRb, don't send a copy, just proxy.
|
|
10
10
|
include DRb::DRbUndumped
|
|
11
11
|
|
|
12
|
+
attr_reader :volt_app
|
|
13
|
+
|
|
14
|
+
def initialize(volt_app)
|
|
15
|
+
@volt_app = volt_app
|
|
16
|
+
end
|
|
17
|
+
|
|
12
18
|
# Dispatch takes an incoming Task from the client and runs it on the
|
|
13
19
|
# server, returning the result to the client.
|
|
14
20
|
# Tasks returning a promise will wait to return.
|
|
@@ -31,17 +37,7 @@ module Volt
|
|
|
31
37
|
# Init and send the method
|
|
32
38
|
promise = promise.then do
|
|
33
39
|
Thread.current['meta'] = meta_data
|
|
34
|
-
|
|
35
|
-
# # Profile the code
|
|
36
|
-
# RubyProf.start
|
|
37
|
-
|
|
38
|
-
result = klass.new(channel, self).send(method_name, *args)
|
|
39
|
-
|
|
40
|
-
# res = RubyProf.stop
|
|
41
|
-
#
|
|
42
|
-
# # Print a flat profile to text
|
|
43
|
-
# printer = RubyProf::FlatPrinter.new(res)
|
|
44
|
-
# printer.print(STDOUT)
|
|
40
|
+
result = klass.new(@volt_app, channel, self).send(method_name, *args)
|
|
45
41
|
|
|
46
42
|
Thread.current['meta'] = nil
|
|
47
43
|
|
|
@@ -91,7 +87,7 @@ module Volt
|
|
|
91
87
|
end
|
|
92
88
|
|
|
93
89
|
def close_channel(channel)
|
|
94
|
-
QueryTasks.new(channel).close!
|
|
90
|
+
QueryTasks.new(@volt_app, channel).close!
|
|
95
91
|
end
|
|
96
92
|
end
|
|
97
93
|
end
|
|
@@ -14,7 +14,8 @@ module Volt
|
|
|
14
14
|
$page.tasks.call(self.name, name, meta_data, *args, &block)
|
|
15
15
|
end
|
|
16
16
|
else
|
|
17
|
-
def initialize(channel = nil, dispatcher = nil)
|
|
17
|
+
def initialize(volt_app, channel = nil, dispatcher = nil)
|
|
18
|
+
@volt_app = volt_app
|
|
18
19
|
@channel = channel
|
|
19
20
|
@dispatcher = dispatcher
|
|
20
21
|
end
|
|
@@ -35,7 +36,7 @@ module Volt
|
|
|
35
36
|
# TODO: optimize: this could run the inside first to see if it
|
|
36
37
|
# returns a promise, so we don't have to wrap it.
|
|
37
38
|
Promise.new.then do
|
|
38
|
-
new(nil, nil).send(name, *args, &block)
|
|
39
|
+
new(Volt.current_app, nil, nil).send(name, *args, &block)
|
|
39
40
|
end.resolve(nil)
|
|
40
41
|
end
|
|
41
42
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# CSSO complains when using node as an execJS runtime, but we currently have
|
|
2
|
+
# to due to a bug in therubyracer (or maybe execJS?)
|
|
3
|
+
|
|
4
|
+
require 'execjs'
|
|
5
|
+
|
|
6
|
+
module Csso
|
|
7
|
+
class JsLib
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
spec = Gem::Specification.find_by_name("csso-rails")
|
|
11
|
+
path = spec.gem_dir
|
|
12
|
+
|
|
13
|
+
lib = File.read(File.expand_path(path + "/" + CSSO_JS_LIB, File.dirname(__FILE__)))
|
|
14
|
+
unless @csso = ExecJS.runtime.compile(lib)
|
|
15
|
+
raise 'cannot compile or what?'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def compress css, structural_optimization=true
|
|
20
|
+
@csso.call("do_compression", css, !structural_optimization)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/volt/version.rb
CHANGED
data/lib/volt/volt/app.rb
CHANGED
|
@@ -1,54 +1,91 @@
|
|
|
1
|
+
require 'opal'
|
|
2
|
+
|
|
3
|
+
# on the client, we want to include the main volt.rb file
|
|
4
|
+
require 'volt'
|
|
5
|
+
require 'volt/models'
|
|
6
|
+
require 'volt/controllers/model_controller'
|
|
7
|
+
require 'volt/tasks/task_handler'
|
|
8
|
+
require 'volt/page/bindings/bindings'
|
|
9
|
+
require 'volt/page/template_renderer'
|
|
10
|
+
require 'volt/page/string_template_renderer'
|
|
11
|
+
require 'volt/page/document_events'
|
|
12
|
+
require 'volt/page/sub_context'
|
|
13
|
+
require 'volt/page/targets/dom_target'
|
|
14
|
+
require 'volt/data_stores/base_adaptor_client'
|
|
15
|
+
|
|
16
|
+
if RUBY_PLATFORM == 'opal'
|
|
17
|
+
require 'volt/page/channel'
|
|
18
|
+
else
|
|
19
|
+
require 'volt/page/channel_stub'
|
|
20
|
+
end
|
|
21
|
+
require 'volt/router/routes'
|
|
22
|
+
require 'volt/models/url'
|
|
23
|
+
require 'volt/page/url_tracker'
|
|
24
|
+
require 'volt/benchmark/benchmark'
|
|
25
|
+
require 'volt/page/tasks'
|
|
26
|
+
require 'volt/page/page'
|
|
27
|
+
|
|
28
|
+
unless RUBY_PLATFORM == 'opal'
|
|
29
|
+
require 'volt/volt/server_setup/app'
|
|
30
|
+
end
|
|
31
|
+
|
|
1
32
|
module Volt
|
|
2
33
|
class App
|
|
3
|
-
|
|
34
|
+
if RUBY_PLATFORM != 'opal'
|
|
35
|
+
# Include server app setup
|
|
36
|
+
include Volt::ServerSetup::App
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_reader :component_paths, :router, :page, :live_query_pool,
|
|
40
|
+
:channel_live_queries, :app_path, :database, :message_bus
|
|
41
|
+
|
|
42
|
+
def initialize(app_path=nil)
|
|
43
|
+
if Volt.server? && !app_path
|
|
44
|
+
raise "Volt::App.new requires an app path to boot"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@app_path = app_path
|
|
48
|
+
$volt_app = self
|
|
4
49
|
|
|
5
|
-
def initialize(app_path)
|
|
6
50
|
# Setup root path
|
|
7
51
|
Volt.root = app_path
|
|
8
52
|
|
|
9
|
-
|
|
10
|
-
unless RUBY_PLATFORM == 'opal'
|
|
11
|
-
if Volt.server?
|
|
12
|
-
@page = Page.new
|
|
53
|
+
setup_page
|
|
13
54
|
|
|
14
|
-
|
|
15
|
-
|
|
55
|
+
if RUBY_PLATFORM != 'opal'
|
|
56
|
+
# Require in app and initializers
|
|
57
|
+
Volt.run_app_and_initializers unless RUBY_PLATFORM == 'opal'
|
|
58
|
+
|
|
59
|
+
# abort_on_exception is a useful debugging tool, and in my opinion something
|
|
60
|
+
# you probbaly want on. That said you can disable it if you need.
|
|
61
|
+
unless RUBY_PLATFORM == 'opal'
|
|
62
|
+
Thread.abort_on_exception = Volt.config.abort_on_exception
|
|
16
63
|
end
|
|
17
|
-
end
|
|
18
64
|
|
|
19
|
-
|
|
20
|
-
Volt.run_app_and_initializers unless RUBY_PLATFORM == 'opal'
|
|
65
|
+
load_app_code
|
|
21
66
|
|
|
22
|
-
|
|
23
|
-
@component_paths = ComponentPaths.new(app_path)
|
|
24
|
-
@component_paths.require_in_components(@page || $page)
|
|
67
|
+
reset_query_pool!
|
|
25
68
|
|
|
26
|
-
|
|
27
|
-
setup_router
|
|
28
|
-
require_http_controllers
|
|
69
|
+
start_message_bus
|
|
29
70
|
end
|
|
30
71
|
end
|
|
31
72
|
|
|
32
|
-
unless RUBY_PLATFORM == 'opal'
|
|
33
|
-
def setup_router
|
|
34
|
-
# Find the route file
|
|
35
|
-
home_path = @component_paths.component_paths('main').first
|
|
36
|
-
routes = File.read("#{home_path}/config/routes.rb")
|
|
37
|
-
@router = Routes.new.define do
|
|
38
|
-
eval(routes)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
73
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
74
|
+
# Setup a Page instance.
|
|
75
|
+
def setup_page
|
|
76
|
+
# Run the app config to load all users config files
|
|
77
|
+
@page = Page.new(self)
|
|
78
|
+
|
|
79
|
+
# Setup a global for now
|
|
80
|
+
$page = @page unless defined?($page)
|
|
52
81
|
end
|
|
53
82
|
end
|
|
54
83
|
end
|
|
84
|
+
|
|
85
|
+
if Volt.client?
|
|
86
|
+
$volt_app = Volt::App.new
|
|
87
|
+
|
|
88
|
+
`$(document).ready(function() {`
|
|
89
|
+
$volt_app.page.start
|
|
90
|
+
`});`
|
|
91
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# The following setup handles setting up the app on the server.
|
|
2
|
+
unless RUBY_PLATFORM == 'opal'
|
|
3
|
+
require 'volt/server/message_bus/peer_to_peer'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
module Volt
|
|
7
|
+
module ServerSetup
|
|
8
|
+
module App
|
|
9
|
+
def load_app_code
|
|
10
|
+
# Load component paths
|
|
11
|
+
@component_paths = ComponentPaths.new(@app_path)
|
|
12
|
+
@component_paths.require_in_components(@page || $page)
|
|
13
|
+
|
|
14
|
+
setup_router
|
|
15
|
+
require_http_controllers
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def setup_router
|
|
19
|
+
# Find the route file
|
|
20
|
+
home_path = @component_paths.component_paths('main').first
|
|
21
|
+
routes = File.read("#{home_path}/config/routes.rb")
|
|
22
|
+
@router = Routes.new.define do
|
|
23
|
+
eval(routes)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def require_http_controllers
|
|
28
|
+
@component_paths.app_folders do |app_folder|
|
|
29
|
+
# Sort so we get consistent load order across platforms
|
|
30
|
+
Dir["#{app_folder}/*/controllers/server/*.rb"].each do |ruby_file|
|
|
31
|
+
# path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
|
|
32
|
+
# require(path)
|
|
33
|
+
require(ruby_file)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def reset_query_pool!
|
|
39
|
+
if RUBY_PLATFORM != 'opal'
|
|
40
|
+
# The load path isn't setup at the top of app.rb, so we wait to require
|
|
41
|
+
require 'volt/tasks/live_query/live_query_pool'
|
|
42
|
+
|
|
43
|
+
# Setup LiveQueryPool for the app
|
|
44
|
+
@database = Volt::DataStore.fetch
|
|
45
|
+
@live_query_pool = LiveQueryPool.new(@database, self)
|
|
46
|
+
@channel_live_queries = {}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def start_message_bus
|
|
51
|
+
unless RUBY_PLATFORM == 'opal'
|
|
52
|
+
|
|
53
|
+
# Don't run in test env, since you probably only have one set of tests
|
|
54
|
+
# running at a time, and even if you have multiple, they shouldn't be
|
|
55
|
+
# updating each other.
|
|
56
|
+
unless Volt.env.test?
|
|
57
|
+
# Start the message bus
|
|
58
|
+
bus_name = Volt.config.message_bus.try(:bus_name) || 'peer_to_peer'
|
|
59
|
+
begin
|
|
60
|
+
message_bus_class = MessageBus.const_get(bus_name.camelize)
|
|
61
|
+
rescue NameError => e
|
|
62
|
+
raise "message bus name #{bus_name} was not found, be sure its "
|
|
63
|
+
+ "gem is included in the gemfile."
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@message_bus = message_bus_class.new(self)
|
|
67
|
+
|
|
68
|
+
Thread.new do
|
|
69
|
+
# Handle incoming messages in a new thread
|
|
70
|
+
@message_bus.subscribe('volt_collection_update') do |collection_name|
|
|
71
|
+
# update a collection, don't resend since we're coming from
|
|
72
|
+
# the message bus.
|
|
73
|
+
live_query_pool.updated_collection(collection_name, nil, true)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
data/lib/volt.rb
CHANGED
|
@@ -12,7 +12,9 @@ require 'volt/volt/users'
|
|
|
12
12
|
|
|
13
13
|
module Volt
|
|
14
14
|
@in_browser = if RUBY_PLATFORM == 'opal'
|
|
15
|
-
|
|
15
|
+
# When testing with opal-rspec, it technically is in a browser
|
|
16
|
+
# but its not setup with our own app code.
|
|
17
|
+
`!!document && !window.OPAL_SPEC_PHANTOM && window.$`
|
|
16
18
|
else
|
|
17
19
|
false
|
|
18
20
|
end
|
|
@@ -52,5 +54,24 @@ module Volt
|
|
|
52
54
|
def in_browser?
|
|
53
55
|
@in_browser
|
|
54
56
|
end
|
|
57
|
+
|
|
58
|
+
# When we use something like a Task, we don't specify an app, so we use
|
|
59
|
+
# a thread local or global to lookup the current app. This lets us run
|
|
60
|
+
# more than one app at once, giving deference to a global app.
|
|
61
|
+
def current_app
|
|
62
|
+
Thread.current['volt_app'] || $volt_app
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Runs code in the context of this app.
|
|
66
|
+
def in_app
|
|
67
|
+
previous_app = Thread.current['volt_app']
|
|
68
|
+
Thread.current['volt_app'] = self
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
yield
|
|
72
|
+
ensure
|
|
73
|
+
Thread.current['volt_app'] = previous_app
|
|
74
|
+
end
|
|
75
|
+
end
|
|
55
76
|
end
|
|
56
77
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
1
3
|
if RUBY_PLATFORM != 'opal'
|
|
2
4
|
require 'volt/controllers/http_controller'
|
|
3
5
|
require 'volt/server/rack/http_request'
|
|
@@ -53,13 +55,13 @@ if RUBY_PLATFORM != 'opal'
|
|
|
53
55
|
'CONTENT_TYPE' => 'text/plain;charset=utf-8'))
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
let(:controller) { TestHttpController.new({}, request) }
|
|
58
|
+
let(:controller) { TestHttpController.new(volt_app, {}, request) }
|
|
57
59
|
|
|
58
60
|
it 'should merge the request params and the url params' do
|
|
59
61
|
request = Volt::HttpRequest.new(
|
|
60
62
|
Rack::MockRequest.env_for('http://example.com/test.html?this=is_a&test=param'))
|
|
61
63
|
controller = TestHttpController.new(
|
|
62
|
-
{ another: 'params', 'and_a' => 'string' }, request)
|
|
64
|
+
volt_app, { another: 'params', 'and_a' => 'string' }, request)
|
|
63
65
|
expect(controller.params.size).to eq(4)
|
|
64
66
|
expect(controller.params._and_a).to eq('string')
|
|
65
67
|
expect(controller.params._this).to eq('is_a')
|
|
@@ -117,7 +119,7 @@ if RUBY_PLATFORM != 'opal'
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
it 'should have access to the body' do
|
|
120
|
-
http_app = Volt::HttpResource.new(app, nil)
|
|
122
|
+
http_app = Volt::HttpResource.new(app, volt_app, nil)
|
|
121
123
|
allow(http_app).to receive(:routes_match?)
|
|
122
124
|
.and_return(controller: 'test_http',
|
|
123
125
|
action: 'access_body')
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
if RUBY_PLATFORM != 'opal'
|
|
4
4
|
describe Volt::ModelController do
|
|
5
5
|
it 'should accept a promise as a model and resolve it' do
|
|
6
|
-
controller = Volt::ModelController.new
|
|
6
|
+
controller = Volt::ModelController.new(volt_app)
|
|
7
7
|
|
|
8
8
|
promise = Promise.new
|
|
9
9
|
|
|
@@ -17,7 +17,7 @@ if RUBY_PLATFORM != 'opal'
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
it 'should not return true from loaded until the promise is resolved' do
|
|
20
|
-
controller = Volt::ModelController.new
|
|
20
|
+
controller = Volt::ModelController.new(volt_app)
|
|
21
21
|
|
|
22
22
|
promise = Promise.new
|
|
23
23
|
controller.model = promise
|
|
@@ -12,7 +12,7 @@ if ENV['BROWSER'] == 'firefox'
|
|
|
12
12
|
fill_in('newtodo', with: "Item 3\n")
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
it 'should add items to the list' do
|
|
15
|
+
it 'should add items to the list', type: :feature do
|
|
16
16
|
expect(find('#todos-table')).to have_content('Item 1')
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -28,7 +28,7 @@ if ENV['BROWSER'] == 'firefox'
|
|
|
28
28
|
expect(find('#todos-table')).to_not have_css('td.name.complete')
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
it 'should delete items' do
|
|
31
|
+
it 'should delete items', type: :feature do
|
|
32
32
|
expect(find('#todos-table')).to have_content('Item 1')
|
|
33
33
|
expect(find('#todos-table')).to have_content('Item 2')
|
|
34
34
|
expect(find('#todos-table')).to have_content('Item 3')
|
|
@@ -41,7 +41,7 @@ if ENV['BROWSER'] == 'firefox'
|
|
|
41
41
|
expect(find('#todos-table')).to have_content('Item 3')
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
it 'should track the number of todos and the numbers that are complete' do
|
|
44
|
+
it 'should track the number of todos and the numbers that are complete', type: :feature do
|
|
45
45
|
count = find('#count')
|
|
46
46
|
|
|
47
47
|
expect(count).to have_content('0 of 3')
|
|
@@ -20,7 +20,7 @@ describe Volt::Associations do
|
|
|
20
20
|
it 'should associate via belongs_to' do
|
|
21
21
|
address = store._addresses!.fetch_first.sync
|
|
22
22
|
|
|
23
|
-
expect(address.person.sync.
|
|
23
|
+
expect(address.person.sync.id).to eq(@person.id)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it 'should associate via has_many' do
|
|
@@ -35,8 +35,16 @@ describe Volt::Associations do
|
|
|
35
35
|
store = Volt::Model.new({}, persistor: Volt::Persistors::Flash)
|
|
36
36
|
expect do
|
|
37
37
|
store.send(:association_with_root_model, :blah)
|
|
38
|
-
end.to raise_error("blah currently only works on the store collection "\
|
|
38
|
+
end.to raise_error("blah currently only works on the store and page collection "\
|
|
39
39
|
"(support for other collections coming soon)")
|
|
40
40
|
end
|
|
41
|
+
|
|
42
|
+
# it 'should assign the reference_id for has_many' do
|
|
43
|
+
# bob = Person.new
|
|
44
|
+
# bob.addresses << {:street => '1234 awesome street'}
|
|
45
|
+
# puts "Bob: #{bob.inspect} - #{bob.addresses.size}"
|
|
46
|
+
# expect(bob.addresses[0].person_id).to eq(bob.id)
|
|
47
|
+
# expect(bob.id).to_not eq(nil)
|
|
48
|
+
# end
|
|
41
49
|
end
|
|
42
50
|
end
|
data/spec/models/dirty_spec.rb
CHANGED
|
@@ -79,27 +79,27 @@ describe 'Volt::Dirty' do
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
it 'should revert changes' do
|
|
82
|
-
expect(model.attributes).to eq({})
|
|
82
|
+
expect(model.attributes.without(:id)).to eq({})
|
|
83
83
|
model.attributes = { first: 'Bob', last: 'Smith' }
|
|
84
|
-
expect(model.attributes).to eq(first: 'Bob', last: 'Smith')
|
|
84
|
+
expect(model.attributes.without(:id)).to eq(first: 'Bob', last: 'Smith')
|
|
85
85
|
|
|
86
86
|
model.revert_changes!
|
|
87
|
-
expect(model.attributes).to eq(first: nil, last: nil)
|
|
87
|
+
expect(model.attributes.without(:id)).to eq(first: nil, last: nil)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
it 'should revert changes after a clear_tracked_changed!' do
|
|
91
|
-
expect(model.attributes).to eq({})
|
|
91
|
+
expect(model.attributes.without(:id)).to eq({})
|
|
92
92
|
model.attributes = { first: 'Bob', last: 'Smith' }
|
|
93
|
-
expect(model.attributes).to eq(first: 'Bob', last: 'Smith')
|
|
93
|
+
expect(model.attributes.without(:id)).to eq(first: 'Bob', last: 'Smith')
|
|
94
94
|
|
|
95
95
|
model.clear_tracked_changes!
|
|
96
96
|
expect(model.changed_attributes).to eq({})
|
|
97
97
|
|
|
98
98
|
model._first = 'Jimmy'
|
|
99
99
|
model._last = 'Dean'
|
|
100
|
-
expect(model.attributes).to eq(first: 'Jimmy', last: 'Dean')
|
|
100
|
+
expect(model.attributes.without(:id)).to eq(first: 'Jimmy', last: 'Dean')
|
|
101
101
|
|
|
102
102
|
model.revert_changes!
|
|
103
|
-
expect(model.attributes).to eq(first: 'Bob', last: 'Smith')
|
|
103
|
+
expect(model.attributes.without(:id)).to eq(first: 'Bob', last: 'Smith')
|
|
104
104
|
end
|
|
105
105
|
end
|