volt 0.9.1.pre1 → 0.9.1.pre2

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