volt 0.9.3.pre1 → 0.9.3.pre2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|