volt 0.9.1.pre1 → 0.9.1.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +8 -0
  4. data/VERSION +1 -1
  5. data/app/volt/tasks/live_query/live_query.rb +8 -2
  6. data/app/volt/tasks/query_tasks.rb +2 -1
  7. data/lib/volt/boot.rb +6 -6
  8. data/lib/volt/cli.rb +22 -17
  9. data/lib/volt/cli/asset_compile.rb +8 -4
  10. data/lib/volt/cli/console.rb +1 -0
  11. data/lib/volt/controllers/model_controller.rb +1 -1
  12. data/lib/volt/extra_core/logger.rb +4 -0
  13. data/lib/volt/models/validators/unique_validator.rb +8 -6
  14. data/lib/volt/page/bindings/attribute_binding.rb +1 -1
  15. data/lib/volt/page/channel.rb +10 -4
  16. data/lib/volt/page/page.rb +1 -1
  17. data/lib/volt/page/string_template_renderer.rb +3 -3
  18. data/lib/volt/server.rb +55 -81
  19. data/lib/volt/server/component_handler.rb +16 -8
  20. data/lib/volt/server/forking_server.rb +176 -0
  21. data/lib/volt/server/html_parser/attribute_scope.rb +1 -1
  22. data/lib/volt/server/html_parser/sandlebars_parser.rb +5 -8
  23. data/lib/volt/server/rack/http_request.rb +3 -1
  24. data/lib/volt/server/rack/http_resource.rb +3 -1
  25. data/lib/volt/server/rack/opal_files.rb +14 -0
  26. data/lib/volt/server/socket_connection_handler.rb +12 -16
  27. data/lib/volt/server/websocket/rack_server_adaptor.rb +19 -0
  28. data/lib/volt/server/websocket/websocket_handler.rb +42 -0
  29. data/lib/volt/spec/capybara.rb +18 -7
  30. data/lib/volt/spec/setup.rb +7 -2
  31. data/lib/volt/tasks/dispatcher.rb +4 -0
  32. data/lib/volt/utils/generic_pool.rb +6 -0
  33. data/lib/volt/utils/read_write_lock.rb +173 -0
  34. data/lib/volt/volt/app.rb +46 -0
  35. data/lib/volt/volt/core.rb +3 -0
  36. data/spec/apps/kitchen_sink/Gemfile +0 -4
  37. data/spec/integration/flash_spec.rb +1 -0
  38. data/spec/integration/user_spec.rb +0 -2
  39. data/spec/server/html_parser/view_parser_spec.rb +1 -1
  40. data/spec/server/rack/asset_files_spec.rb +1 -1
  41. data/spec/spec_helper.rb +12 -0
  42. data/templates/project/Gemfile.tt +2 -0
  43. data/templates/project/config/app.rb.tt +2 -2
  44. data/templates/project/config/base/index.html +1 -0
  45. data/volt.gemspec +5 -1
  46. metadata +53 -7
  47. data/app/volt/assets/js/sockjs-0.3.4.min.js +0 -27
  48. data/lib/volt/server/rack/component_html_renderer.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eaebdf0ceeb600875ea244b0aa8e1bf36fadc998
4
- data.tar.gz: 0111f18c3191d2f2f81528d9b7ffbbe8e6187c5f
3
+ metadata.gz: ad2f798ba0d81a985f87b8ff96e53b84797644c8
4
+ data.tar.gz: 9b71d94cfc22e7ac63e51dfa0375d33759f5ecfd
5
5
  SHA512:
6
- metadata.gz: f06a879922cac14ae698c20e7a6ec3966c74aeae78bc175b11c9c6588c93ed20032fd678342137b2ec3f1c8682fa36241ac21aff6df97b820fb8da2968775c2b
7
- data.tar.gz: cc56f0cae364f252f7ff11ff6ce09081192c9aedd14f73a665a0ae8a366e6dac20220eaa19d8a6ac99acd340243eb1bdb7f10a2d01ab1ee78ba1e393a9d13a9a
6
+ metadata.gz: 0ac6eadbc636ff75bf155cb6babf498499c70c81526b758a6028d8b2d315262e190bf7ddcd27d524c5bfa2f4b0d1205d7cfb85413436fa94862f40a33c7bdc99
7
+ data.tar.gz: d8cebc99bc26b65ecd9df5f5826f8d2d4e696f00e859f294db53510bbc4936436866f1a0b9d12f5f62dabf6adaa8a1da43691a80f54f18b7a96bf632b678e977
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.9.1
4
+ ### Changed
5
+ - Corrected the name of StringTemplateRender to StringTemplateRenderer
6
+ - Volt now uses faye-websocket for socket connections. This means we can run on any rack-hijack server supported by faye-websocket. Currently Volt is tested with thin and puma. (Note: Thin will probably have better performance since it is evented, which means it doesn't need a thread per connection) More servers coming soon.
7
+ - fixed issue with the unique validation.
8
+ - made it so <:SectionName> can be accessed by <:section_name /> tag
9
+ - fixed issue with if bindings not resolving some promises.
10
+
3
11
  ## 0.9.0
4
12
  ### Added
5
13
  - the permissions api has been added!
data/Gemfile CHANGED
@@ -22,4 +22,12 @@ end
22
22
 
23
23
  group :development, :test do
24
24
  gem 'bson_ext'
25
+
26
+ # For running tests
27
+ gem 'thin'
28
+ end
29
+
30
+ platform :mri do
31
+ # The implementation of ReadWriteLock in Volt uses concurrent ruby and ext helps performance.
32
+ gem 'concurrent-ruby-ext'
25
33
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1.pre1
1
+ 0.9.1.pre2
@@ -85,10 +85,16 @@ class LiveQuery
85
85
  end
86
86
 
87
87
  def remove_channel(channel)
88
- @channels.delete(channel)
88
+ deleted = @channels.delete(channel)
89
89
 
90
90
  # remove this query, no one is listening anymore
91
- @pool.remove(@collection, @query) if @channels.empty?
91
+ if @channels.empty?
92
+ begin
93
+ @pool.remove(@collection, @query)
94
+ rescue Volt::GenericPoolDeleteException => e
95
+ # ignore
96
+ end
97
+ end
92
98
  end
93
99
 
94
100
  def notify!(skip_channel = nil, only_channel = nil)
@@ -25,8 +25,9 @@ class QueryTasks < Volt::Task
25
25
  # they simply return to :dirty once the query is issued.
26
26
  @channel.user_id = Volt.current_user_id
27
27
 
28
- live_query.add_channel(@channel)
28
+ # live_query.add_channel(@channel)
29
29
  end
30
+ live_query.add_channel(@channel)
30
31
 
31
32
  errors = {}
32
33
 
data/lib/volt/boot.rb CHANGED
@@ -1,25 +1,25 @@
1
1
  require 'volt/models'
2
2
  require 'volt/server/rack/component_paths'
3
+
3
4
  if RUBY_PLATFORM == 'opal'
4
5
  require 'volt'
5
6
  else
6
7
  require 'volt/page/page'
7
8
  end
9
+ require 'volt/volt/app'
8
10
 
9
11
  module Volt
10
12
  def self.boot(app_path)
11
13
  # Run the app config to load all users config files
12
14
  unless RUBY_PLATFORM == 'opal'
13
- Volt.run_files_in_config_folder
14
-
15
15
  if Volt.server?
16
16
  $page = Page.new
17
17
  end
18
18
  end
19
19
 
20
- component_paths = ComponentPaths.new(app_path)
21
- component_paths.require_in_components
22
-
23
- component_paths
20
+ # Boot the app
21
+ App.new(app_path)
24
22
  end
23
+
24
+
25
25
  end
data/lib/volt/cli.rb CHANGED
@@ -36,13 +36,8 @@ module Volt
36
36
  method_option :bind, type: :string, aliases: '-b', banner: 'the ip the server should bind to'
37
37
 
38
38
  def server
39
- if RUBY_PLATFORM == 'java'
40
- require 'volt/server'
41
- else
42
- require 'thin'
43
- end
44
-
45
39
  require 'fileutils'
40
+ require 'volt/server'
46
41
 
47
42
  # If we're in a Volt project, clear the temp directory
48
43
  # TODO: this is a work around for a bug when switching between
@@ -55,19 +50,29 @@ module Volt
55
50
  return
56
51
  end
57
52
 
58
- if RUBY_PLATFORM == 'java'
59
- server = Server.new.app
60
- Rack::Handler::Jubilee.run(server)
61
- Thread.stop
62
- else
63
- ENV['SERVER'] = 'true'
64
- args = ['start', '--threaded', '--max-persistent-conns', '300']
65
- args += ['--max-conns', '400'] unless Gem.win_platform?
66
- args += ['-p', options[:port].to_s] if options[:port]
67
- args += ['-a', options[:bind]] if options[:bind]
53
+ ENV['SERVER'] = 'true'
68
54
 
69
- Thin::Runner.new(args).run!
55
+ app = Volt::Server.new.app
56
+
57
+ server = Rack::Handler.get(RUNNING_SERVER)
58
+
59
+ opts = {}
60
+ opts[:Port] = options[:port] || 3000
61
+ opts[:Host] = options[:bind] if options[:bind]
62
+
63
+ server.run(app, opts) do |server|
64
+ case RUNNING_SERVER
65
+ when 'thin'
66
+ server.maximum_persistent_connections = 300
67
+ server.maximum_connections = 500 unless Gem.win_platform?
68
+ server.threaded = true
69
+
70
+ # We need to disable the timeout on thin, otherwise it will keep
71
+ # disconnecting the websockets.
72
+ server.timeout = 0
73
+ end
70
74
  end
75
+
71
76
  end
72
77
 
73
78
  desc 'runner FILEPATH', 'Runs a ruby file at FILEPATH in the volt app'
@@ -14,10 +14,15 @@ module Volt
14
14
  ENV['SERVER'] = 'true'
15
15
 
16
16
  require 'opal'
17
+ require 'rack'
17
18
  require 'volt'
18
19
  require 'volt/boot'
19
20
 
20
- Volt.boot(Dir.pwd)
21
+
22
+ @root_path ||= Dir.pwd
23
+ Volt.root = @root_path
24
+
25
+ Volt.boot(@root_path)
21
26
 
22
27
  require 'volt/server/rack/component_paths'
23
28
  require 'volt/server/rack/component_code'
@@ -25,8 +30,6 @@ module Volt
25
30
  require 'volt/server/rack/index_files'
26
31
  require 'volt/server/component_handler'
27
32
 
28
- @root_path ||= Dir.pwd
29
- Volt.root = @root_path
30
33
 
31
34
  @app_path = File.expand_path(File.join(@root_path, 'app'))
32
35
 
@@ -49,7 +52,7 @@ module Volt
49
52
  @opal_files.environment.each_logical_path do |logical_path|
50
53
  logical_path = logical_path.to_s
51
54
  # Only include files that aren't compiled elsewhere, like fonts
52
- unless logical_path[/[.](y|css|js|html|erb)$/]
55
+ if !logical_path[/[.](y|css|js|html|erb)$/] && File.extname(logical_path) != ''
53
56
  write_sprocket_file(logical_path)
54
57
  end
55
58
  end
@@ -68,6 +71,7 @@ module Volt
68
71
  path = "#{@root_path}/public/assets/#{logical_path}"
69
72
 
70
73
  begin
74
+ puts "LP: #{logical_path.inspect}"
71
75
  content = @opal_files.environment[logical_path].to_s
72
76
  write_file(path, content)
73
77
  rescue Sprockets::FileNotFound, SyntaxError => e
@@ -37,6 +37,7 @@ module Volt
37
37
 
38
38
  require 'volt'
39
39
  require 'volt/boot'
40
+ require 'volt/volt/core'
40
41
  require 'volt/server/socket_connection_handler_stub'
41
42
 
42
43
  SocketConnectionHandlerStub.dispatcher = Dispatcher.new
@@ -30,7 +30,7 @@ module Volt
30
30
  def yield_html
31
31
  if (template_path = attrs.content_template_path)
32
32
  # TODO: Don't use $page global
33
- @yield_renderer ||= StringTemplateRender.new($page, self, template_path)
33
+ @yield_renderer ||= StringTemplateRenderer.new($page, self, template_path)
34
34
  @yield_renderer.html
35
35
  else
36
36
  # no template, empty string
@@ -66,6 +66,10 @@ else
66
66
  colorize(@current[:run_time].to_s + 'ms', :green)
67
67
  end
68
68
 
69
+ def log_with_color(msg, color)
70
+ Volt.logger.info(colorize(msg, color))
71
+ end
72
+
69
73
 
70
74
  private
71
75
 
@@ -1,8 +1,6 @@
1
1
  module Volt
2
2
  class UniqueValidator
3
3
  def self.validate(model, field_name, args)
4
- errors = {}
5
-
6
4
  if RUBY_PLATFORM != 'opal'
7
5
  if args
8
6
  value = model.get(field_name)
@@ -14,15 +12,19 @@ module Volt
14
12
 
15
13
  # Check if the value is taken
16
14
  # TODO: need a way to handle scope for unique
17
- if $page.store.send(:"_#{model.path[-2]}").find(query).size > 0
18
- message = (args.is_a?(Hash) && args[:message]) || 'is already taken'
15
+ return $page.store.get(model.path[-2]).where(query).fetch_first do |item|
16
+ if item
17
+ message = (args.is_a?(Hash) && args[:message]) || 'is already taken'
19
18
 
20
- errors[field_name] = [message]
19
+ # return the error
20
+ next {field_name: [message]}
21
+ end
21
22
  end
22
23
  end
23
24
  end
24
25
 
25
- errors
26
+ # no errors
27
+ {}
26
28
  end
27
29
  end
28
30
  end
@@ -72,7 +72,7 @@ module Volt
72
72
  @string_template_renderer_computation.stop if @string_template_renderer_computation
73
73
  @string_template_renderer.remove if @string_template_renderer
74
74
 
75
- if new_value.is_a?(StringTemplateRender)
75
+ if new_value.is_a?(StringTemplateRenderer)
76
76
  # We don't need to refetch the whole reactive template to
77
77
  # update, we can just depend on it and update directly.
78
78
  @string_template_renderer = new_value
@@ -27,13 +27,19 @@ module Volt
27
27
  end
28
28
 
29
29
  def connect!
30
- `
31
- this.socket = new SockJS('/channel');
30
+ %x{
31
+ this.socket = new WebSocket('ws://' + document.location.host + '/socket');
32
32
 
33
- this.socket.onopen = function() {
33
+ this.socket.onopen = function () {
34
34
  self.$opened();
35
35
  };
36
36
 
37
+ // Log errors
38
+ this.socket.onerror = function (error) {
39
+ console.log('WebSocket Error ', error);
40
+ };
41
+
42
+ // Log messages from the server
37
43
  this.socket.onmessage = function(message) {
38
44
  self['$message_received'](message.data);
39
45
  };
@@ -41,7 +47,7 @@ module Volt
41
47
  this.socket.onclose = function(error) {
42
48
  self.$closed(error);
43
49
  };
44
- `
50
+ }
45
51
  end
46
52
 
47
53
  def opened
@@ -173,7 +173,7 @@ module Volt
173
173
  TemplateRenderer.new(self, DomTarget.new, main_controller, 'CONTENT', 'main/main/main/body')
174
174
 
175
175
  # Setup title reactive template
176
- @title_template = StringTemplateRender.new(self, main_controller, 'main/main/main/title')
176
+ @title_template = StringTemplateRenderer.new(self, main_controller, 'main/main/main/title')
177
177
 
178
178
  # Watch for changes to the title template
179
179
  proc do
@@ -1,10 +1,10 @@
1
1
  module Volt
2
- # StringTemplateRender are used to render a template to a string. Call .html
2
+ # StringTemplateRenderer are used to render a template to a string. Call .html
3
3
  # to get the string. Be sure to call .remove when complete.
4
4
  #
5
- # StringTemplateRender will intellegently update the string in the same way
5
+ # StringTemplateRenderer will intellegently update the string in the same way
6
6
  # a normal bindings will update the dom.
7
- class StringTemplateRender
7
+ class StringTemplateRenderer
8
8
  def initialize(page, context, template_path)
9
9
  @dependency = Dependency.new
10
10
 
data/lib/volt/server.rb CHANGED
@@ -1,30 +1,16 @@
1
1
  ENV['SERVER'] = 'true'
2
2
 
3
3
  require 'opal'
4
- if RUBY_PLATFORM == 'java'
5
- require 'jubilee'
6
- else
7
- require 'thin'
8
- end
9
4
 
10
5
  require 'rack'
11
6
  require 'sass'
12
7
  require 'volt/utils/tilt_patch'
13
- if RUBY_PLATFORM != 'java'
14
- require 'rack/sockjs'
15
- require 'eventmachine'
16
- end
17
8
  require 'sprockets-sass'
18
- require 'listen'
19
9
 
20
10
  require 'volt'
21
- require 'volt/boot'
22
11
  require 'volt/tasks/dispatcher'
23
12
  require 'volt/tasks/task_handler'
24
13
  require 'volt/server/component_handler'
25
- if RUBY_PLATFORM != 'java'
26
- require 'volt/server/socket_connection_handler'
27
- end
28
14
  require 'volt/server/rack/component_paths'
29
15
  require 'volt/server/rack/index_files'
30
16
  require 'volt/server/rack/http_resource'
@@ -32,8 +18,10 @@ require 'volt/server/rack/opal_files'
32
18
  require 'volt/server/rack/quiet_common_logger'
33
19
  require 'volt/page/page'
34
20
 
35
- require 'volt/server/rack/http_request'
36
- require 'volt/controllers/http_controller'
21
+ require 'volt/volt/core'
22
+ require 'volt/server/websocket/websocket_handler'
23
+ require 'volt/utils/read_write_lock'
24
+ require 'volt/server/forking_server'
37
25
 
38
26
  module Rack
39
27
  # TODO: For some reason in Rack (or maybe thin), 304 headers close
@@ -58,19 +46,14 @@ end
58
46
 
59
47
  module Volt
60
48
  class Server
49
+ attr_reader :listener, :app_path
61
50
 
62
- def initialize(root_path = nil)
63
- root_path ||= Dir.pwd
64
- Volt.root = root_path
65
-
66
- @app_path = File.expand_path(File.join(root_path, 'app'))
67
-
68
- # Boot the volt app
69
- @component_paths = Volt.boot(root_path)
51
+ # You can also optionally pass in a prebooted app
52
+ def initialize(root_path = nil, app = false)
53
+ @root_path = root_path || Dir.pwd
54
+ @volt_app = app
70
55
 
71
- setup_router
72
- require_http_controllers
73
- setup_change_listener
56
+ @app_path = File.expand_path(File.join(@root_path, 'app'))
74
57
 
75
58
  display_welcome
76
59
  end
@@ -79,81 +62,72 @@ module Volt
79
62
  puts File.read(File.join(File.dirname(__FILE__), 'server/banner.txt'))
80
63
  end
81
64
 
82
- def setup_router
83
- # Find the route file
84
- home_path = @component_paths.component_paths('main').first
85
- routes = File.read("#{home_path}/config/routes.rb")
86
- @router = Routes.new.define do
87
- eval(routes)
88
- end
89
- end
65
+ def boot_volt
66
+ # Boot the volt app
67
+ require 'volt/boot'
90
68
 
91
- def require_http_controllers
92
- @component_paths.app_folders do |app_folder|
93
- # Sort so we get consistent load order across platforms
94
- Dir["#{app_folder}/*/controllers/server/*.rb"].each do |ruby_file|
95
- #path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
96
- #require(path)
97
- load ruby_file
98
- end
99
- end
69
+ @volt_app ||= Volt.boot(@root_path)
100
70
  end
101
71
 
102
- def setup_change_listener
103
- # Setup the listeners for file changes
104
- listener = Listen.to("#{@app_path}/") do |modified, added, removed|
105
- puts 'file changed, sending reload'
106
- setup_router
107
- require_http_controllers
108
- SocketConnectionHandler.send_message_all(nil, 'reload')
72
+ # App returns the main rack app. In development it will fork a
73
+ def app
74
+ app = Rack::Builder.new
75
+
76
+ # Handle websocket connections
77
+ app.use WebsocketHandler
78
+
79
+ if Volt.env.production? || Volt.env.test?
80
+ # In production/test, we boot the app and run the server
81
+ #
82
+ # Sometimes the app is already booted, so we can skip if it is
83
+ boot_volt unless @volt_app
84
+
85
+ # Setup the dispatcher (it stays this class during its run)
86
+ SocketConnectionHandler.dispatcher = Dispatcher.new
87
+ app.run(new_server)
88
+ else
89
+ # In developer
90
+ app.run ForkingServer.new(self)
109
91
  end
110
- listener.start
92
+
93
+ app
111
94
  end
112
95
 
113
- def app
114
- @app = Rack::Builder.new
96
+ # new_server returns the core of the Rack app.
97
+ # Volt.boot should be called before generating the new server
98
+ def new_server
99
+ @rack_app = Rack::Builder.new
115
100
 
116
101
  # Should only be used in production
117
102
  if Volt.config.deflate
118
- @app.use Rack::Deflater
119
- @app.use Rack::Chunked
103
+ @rack_app.use Rack::Deflater
104
+ @rack_app.use Rack::Chunked
120
105
  end
121
106
 
122
- @app.use Rack::ContentLength
107
+ @rack_app.use Rack::ContentLength
123
108
 
124
- @app.use Rack::KeepAlive
125
- @app.use Rack::ConditionalGet
126
- @app.use Rack::ETag
109
+ @rack_app.use Rack::KeepAlive
110
+ @rack_app.use Rack::ConditionalGet
111
+ @rack_app.use Rack::ETag
127
112
 
128
- @app.use QuietCommonLogger
129
- @app.use Rack::ShowExceptions
113
+ @rack_app.use QuietCommonLogger
114
+ @rack_app.use Rack::ShowExceptions
130
115
 
131
- component_paths = @component_paths
132
- @app.map '/components' do
116
+ component_paths = @volt_app.component_paths
117
+ @rack_app.map '/components' do
133
118
  run ComponentHandler.new(component_paths)
134
119
  end
135
120
 
136
121
  # Serve the opal files
137
- opal_files = OpalFiles.new(@app, @app_path, @component_paths)
122
+ opal_files = OpalFiles.new(@rack_app, @app_path, @volt_app.component_paths)
138
123
 
139
124
  # Serve the main html files from public, also figure out
140
125
  # which JS/CSS files to serve.
141
- @app.use IndexFiles, @component_paths, opal_files
126
+ @rack_app.use IndexFiles, @volt_app.component_paths, opal_files
142
127
 
143
- @app.use HttpResource, @router
144
-
145
- component_paths.require_in_components
146
-
147
- # Handle socks js connection
148
- if RUBY_PLATFORM != 'java'
149
- SocketConnectionHandler.dispatcher = Dispatcher.new
150
-
151
- @app.map '/channel' do
152
- run Rack::SockJS.new(SocketConnectionHandler) # , :websocket => false
153
- end
154
- end
128
+ @rack_app.use HttpResource, @volt_app.router
155
129
 
156
- @app.use Rack::Static,
130
+ @rack_app.use Rack::Static,
157
131
  urls: ['/'],
158
132
  root: 'config/base',
159
133
  index: '',
@@ -161,9 +135,9 @@ module Volt
161
135
  [:all, { 'Cache-Control' => 'public, max-age=86400' }]
162
136
  ]
163
137
 
164
- @app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }
138
+ @rack_app.run lambda { |env| [404, { 'Content-Type' => 'text/html; charset=utf-8' }, ['404 - page not found']] }
165
139
 
166
- @app
140
+ @rack_app
167
141
  end
168
142
  end
169
143
  end