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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +15 -1
  4. data/Gemfile +30 -3
  5. data/README.md +7 -2
  6. data/Rakefile +17 -0
  7. data/app/volt/models/user.rb +1 -1
  8. data/app/volt/tasks/live_query/live_query.rb +0 -1
  9. data/app/volt/tasks/live_query/live_query_pool.rb +8 -2
  10. data/app/volt/tasks/live_query/query_tracker.rb +2 -2
  11. data/app/volt/tasks/query_tasks.rb +10 -27
  12. data/app/volt/tasks/store_tasks.rb +6 -5
  13. data/app/volt/tasks/user_tasks.rb +2 -2
  14. data/docs/UPGRADE_GUIDE.md +14 -0
  15. data/lib/volt/boot.rb +1 -0
  16. data/lib/volt/cli/asset_compile.rb +25 -7
  17. data/lib/volt/cli/console.rb +6 -5
  18. data/lib/volt/cli/generate.rb +2 -2
  19. data/lib/volt/config.rb +2 -1
  20. data/lib/volt/controllers/http_controller.rb +4 -3
  21. data/lib/volt/controllers/model_controller.rb +41 -19
  22. data/lib/volt/controllers/template_helpers.rb +19 -0
  23. data/lib/volt/extra_core/array.rb +6 -0
  24. data/lib/volt/extra_core/hash.rb +8 -26
  25. data/lib/volt/extra_core/string.rb +1 -1
  26. data/lib/volt/models/array_model.rb +12 -4
  27. data/lib/volt/models/associations.rb +11 -13
  28. data/lib/volt/models/buffer.rb +1 -1
  29. data/lib/volt/models/model.rb +22 -13
  30. data/lib/volt/models/model_helpers/model_change_helpers.rb +0 -1
  31. data/lib/volt/models/model_helpers/model_helpers.rb +11 -0
  32. data/lib/volt/models/permissions.rb +9 -12
  33. data/lib/volt/models/persistors/array_store.rb +7 -7
  34. data/lib/volt/models/persistors/base.rb +9 -0
  35. data/lib/volt/models/persistors/cookies.rb +0 -4
  36. data/lib/volt/models/persistors/flash.rb +0 -4
  37. data/lib/volt/models/persistors/local_store.rb +0 -4
  38. data/lib/volt/models/persistors/model_store.rb +13 -21
  39. data/lib/volt/models/persistors/page.rb +22 -0
  40. data/lib/volt/models/persistors/params.rb +0 -4
  41. data/lib/volt/models/persistors/query/query_listener.rb +3 -2
  42. data/lib/volt/models/url.rb +2 -2
  43. data/lib/volt/models/validators/unique_validator.rb +1 -1
  44. data/lib/volt/models.rb +1 -0
  45. data/lib/volt/page/bindings/attribute_binding.rb +2 -2
  46. data/lib/volt/page/bindings/base_binding.rb +7 -3
  47. data/lib/volt/page/bindings/bindings.rb +9 -0
  48. data/lib/volt/page/bindings/content_binding.rb +2 -2
  49. data/lib/volt/page/bindings/each_binding.rb +16 -12
  50. data/lib/volt/page/bindings/event_binding.rb +4 -4
  51. data/lib/volt/page/bindings/if_binding.rb +3 -3
  52. data/lib/volt/page/bindings/view_binding.rb +4 -4
  53. data/lib/volt/page/bindings/yield_binding.rb +3 -3
  54. data/lib/volt/page/channel.rb +6 -0
  55. data/lib/volt/page/channel_stub.rb +1 -1
  56. data/lib/volt/page/page.rb +20 -54
  57. data/lib/volt/page/path_string_renderer.rb +5 -6
  58. data/lib/volt/page/string_template_renderer.rb +2 -2
  59. data/lib/volt/page/targets/attribute_section.rb +47 -0
  60. data/lib/volt/page/targets/base_section.rb +5 -5
  61. data/lib/volt/page/targets/binding_document/component_node.rb +6 -1
  62. data/lib/volt/page/template_renderer.rb +4 -4
  63. data/lib/volt/reactive/computation.rb +32 -3
  64. data/lib/volt/router/routes.rb +5 -5
  65. data/lib/volt/server/component_templates.rb +30 -2
  66. data/lib/volt/server/forking_server.rb +2 -2
  67. data/lib/volt/server/message_bus/base_message_bus.rb +52 -0
  68. data/lib/volt/server/message_bus/message_encoder.rb +64 -0
  69. data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +186 -0
  70. data/lib/volt/server/message_bus/peer_to_peer/peer_server.rb +78 -0
  71. data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +57 -0
  72. data/lib/volt/server/message_bus/peer_to_peer/socket_with_timeout.rb +27 -0
  73. data/lib/volt/server/message_bus/peer_to_peer.rb +198 -0
  74. data/lib/volt/server/message_bus/redis.rb +1 -0
  75. data/lib/volt/server/rack/asset_files.rb +2 -2
  76. data/lib/volt/server/rack/component_paths.rb +1 -1
  77. data/lib/volt/server/rack/http_resource.rb +3 -2
  78. data/lib/volt/server/rack/opal_files.rb +6 -9
  79. data/lib/volt/server/websocket/websocket_handler.rb +0 -3
  80. data/lib/volt/server.rb +5 -3
  81. data/lib/volt/spec/setup.rb +11 -12
  82. data/lib/volt/tasks/dispatcher.rb +8 -12
  83. data/lib/volt/tasks/task_handler.rb +3 -2
  84. data/lib/volt/utils/csso_patch.rb +24 -0
  85. data/lib/volt/utils/promise_patch.rb +2 -0
  86. data/lib/volt/version.rb +1 -1
  87. data/lib/volt/volt/app.rb +73 -36
  88. data/lib/volt/volt/server_setup/app.rb +81 -0
  89. data/lib/volt.rb +22 -1
  90. data/spec/apps/kitchen_sink/Gemfile +1 -1
  91. data/spec/controllers/http_controller_spec.rb +5 -3
  92. data/spec/controllers/model_controller_spec.rb +2 -2
  93. data/spec/extra_core/hash_spec.rb +9 -0
  94. data/spec/integration/list_spec.rb +3 -3
  95. data/spec/models/associations_spec.rb +10 -2
  96. data/spec/models/dirty_spec.rb +7 -7
  97. data/spec/models/model_spec.rb +10 -2
  98. data/spec/models/permissions_spec.rb +9 -0
  99. data/spec/models/persistors/store_spec.rb +8 -0
  100. data/spec/page/bindings/content_binding_spec.rb +6 -2
  101. data/spec/page/bindings/each_binding_spec.rb +59 -0
  102. data/spec/page/bindings/if_binding_spec.rb +57 -0
  103. data/spec/page/path_string_renderer_spec.rb +5 -5
  104. data/spec/reactive/computation_spec.rb +65 -1
  105. data/spec/router/routes_spec.rb +1 -1
  106. data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -22
  107. data/spec/server/message_bus/message_encoder_spec.rb +49 -0
  108. data/spec/server/message_bus/peer_to_peer/peer_connection_spec.rb +108 -0
  109. data/spec/server/message_bus/peer_to_peer/peer_server_spec.rb +66 -0
  110. data/spec/server/message_bus/peer_to_peer/socket_with_timeout_spec.rb +11 -0
  111. data/spec/server/message_bus/peer_to_peer_spec.rb +11 -0
  112. data/spec/server/rack/asset_files_spec.rb +1 -1
  113. data/spec/server/rack/http_resource_spec.rb +4 -4
  114. data/spec/spec_helper.rb +16 -3
  115. data/spec/tasks/dispatcher_spec.rb +17 -5
  116. data/spec/tasks/live_query_spec.rb +1 -1
  117. data/spec/tasks/query_tracker_spec.rb +34 -34
  118. data/spec/tasks/user_tasks_spec.rb +4 -2
  119. data/templates/project/Gemfile.tt +14 -3
  120. data/templates/project/config/app.rb.tt +27 -2
  121. data/volt.gemspec +3 -8
  122. metadata +32 -101
  123. 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.each do |gem|
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
- require 'ruby-clean-css'
57
- require 'ruby-clean-css/sprockets'
58
- RubyCleanCSS::Sprockets.register(environment)
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
- if !can_fork || Volt.env.production? || Volt.env.test?
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: ['/'],
@@ -10,7 +10,7 @@ module Volt
10
10
 
11
11
  require 'volt/boot'
12
12
 
13
- # Require in app
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
- Volt::DataStore.fetch.drop_database
35
+ volt_app.database.drop_database
36
36
 
37
37
  # Clear cached for a reset
38
- $page.instance_variable_set('@store', nil)
39
- QueryTasks.reset!
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 ||= Page.new
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
@@ -47,6 +47,8 @@ class Promise
47
47
  # Waits for the promise to resolve (assuming it is blocking on
48
48
  # the server) and returns the result.
49
49
  def sync
50
+ raise ".sync can only be used on the client" if Volt.client?
51
+
50
52
  result = nil
51
53
  error = nil
52
54
 
data/lib/volt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Version
3
- STRING = '0.9.3.pre1'
3
+ STRING = '0.9.3.pre2'
4
4
  end
5
5
  end
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
- attr_reader :component_paths, :router, :page
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
- # Run the app config to load all users config files
10
- unless RUBY_PLATFORM == 'opal'
11
- if Volt.server?
12
- @page = Page.new
53
+ setup_page
13
54
 
14
- # Setup a global for now
15
- $page = @page unless defined?($page)
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
- # Require in app and initializers
20
- Volt.run_app_and_initializers unless RUBY_PLATFORM == 'opal'
65
+ load_app_code
21
66
 
22
- # Load component paths
23
- @component_paths = ComponentPaths.new(app_path)
24
- @component_paths.require_in_components(@page || $page)
67
+ reset_query_pool!
25
68
 
26
- unless RUBY_PLATFORM == 'opal'
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
- def require_http_controllers
43
- @component_paths.app_folders do |app_folder|
44
- # Sort so we get consistent load order across platforms
45
- Dir["#{app_folder}/*/controllers/server/*.rb"].each do |ruby_file|
46
- # path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
47
- # require(path)
48
- require(ruby_file)
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
- `!!document && !window.OPAL_SPEC_PHANTOM`
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
@@ -14,7 +14,7 @@ gem 'volt-fields'
14
14
  gem 'volt-user_templates'
15
15
 
16
16
  # use mongo for data store while testing
17
- gem 'volt-mongo'
17
+ gem 'volt-mongo', path: '/Users/ryanstout/Sites/volt/apps/volt-mongo'
18
18
 
19
19
  gem 'opal'
20
20
 
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash do
4
+ it 'should return a hash without the speicified keys' do
5
+ a = {one: 1, two: 2, three: 3}
6
+
7
+ expect(a.without(:one, :three)).to eq({two: 2})
8
+ end
9
+ end
@@ -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._id).to eq(@person._id)
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
@@ -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