volt 0.9.3.pre5 → 0.9.3.pre6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81532c9c3fda9af1ddf09bf56ff2c479f507f279
4
- data.tar.gz: aa68ce3727fa3dda1d21bb86771070b93ce5b595
3
+ metadata.gz: 6b47ed7510baf946cc2e65e7140be76022472b53
4
+ data.tar.gz: cf681b804b8609987671fbf2626aaf399d39bec0
5
5
  SHA512:
6
- metadata.gz: 9079b50cbc1bf729c3c56a11fc30082edf7c846628fccc4cd8b468a5c7184f14c7c25dbcc16dc60ab6ad4a7c51e54ec3154a97c8a38aa8c313bffea7accc071d
7
- data.tar.gz: 88abd17e264363c606421fbffa52621f01bc869147de950b0922b796ad7ff74da5a0f9b04e6a2aedb79adebf957f07da7af9807c329317c12e3f9638cadae1d8
6
+ metadata.gz: ffcd6ef512a9b74b519b1b2ee294b8bef5d00075b8052ab6c4cee1375fac9662d2d2991024a469ccb7b8cf3c0e1d7388999dc6e5ff908f1b48a6705e8cef12df
7
+ data.tar.gz: 811b21c5c24ee684593c7069aba0cd68fad6480b22486c0646f34e67f365b8ab7e0ef36857c0defba4030fc9be243745f27c9a2e8930a7ba6fe58a29afc3dad0
data/CHANGELOG.md CHANGED
@@ -19,6 +19,8 @@
19
19
  - Volt.current_user now works in HttpController's
20
20
  - You can now add your own middleware to the middleware stack. (see docs)
21
21
  - Added a threadpool for Tasks, and options to customize pool size in config/app.rb
22
+ - Volt now handles Syntax errors much better, it will display an error message when your app does not compile, and can reload from that page when things change. (in development)
23
+ - Time objects can now be saved in models.
22
24
 
23
25
  ### Changed
24
26
  - All methods on ArrayModel's under the store collection now return a Promise.
@@ -1,4 +1,3 @@
1
- require 'mongo'
2
1
  require 'volt/models'
3
2
 
4
3
  class StoreTasks < Volt::Task
@@ -57,15 +57,9 @@ class NewGem
57
57
 
58
58
  def copy_options
59
59
  copy('newgem/bin/newgem.tt', "bin/#{@name}") if @options[:bin]
60
- case @options[:test]
61
- when 'rspec'
62
- copy('newgem/rspec.tt', '.rspec')
63
- copy('newgem/spec/spec_helper.rb.tt', 'spec/spec_helper.rb')
64
- copy('newgem/spec/newgem_spec.rb.tt', "spec/#{@namespaced_path}_spec.rb")
65
- when 'minitest'
66
- copy('newgem/test/minitest_helper.rb.tt', 'test/minitest_helper.rb')
67
- copy('newgem/test/test_newgem.rb.tt', "test/test_#{@namespaced_path}.rb")
68
- end
60
+ copy('newgem/rspec.tt', '.rspec')
61
+ copy('newgem/spec/spec_helper.rb.tt', 'spec/spec_helper.rb')
62
+ copy('newgem/spec/newgem_spec.rb.tt', "spec/#{@namespaced_path}_spec.rb")
69
63
  puts "Initializing git repo in #{@target}"
70
64
  Dir.chdir(@target) { `git init`; `git add .` }
71
65
 
@@ -14,7 +14,7 @@ module Volt
14
14
  adaptor_name = root.const_get(adaptor_name)
15
15
  @adaptor = adaptor_name.new
16
16
  else
17
- raise "#{database_name} is not a supported database"
17
+ raise "#{database_name} is not a supported database, you might be missing a volt-#{database_name} gem"
18
18
  end
19
19
 
20
20
  @adaptor
@@ -1,6 +1,6 @@
1
1
  require 'volt/models/persistors/base'
2
2
  require 'volt/utils/local_storage'
3
- require 'json'
3
+ require 'volt/utils/ejson'
4
4
 
5
5
  module Volt
6
6
  module Persistors
@@ -17,7 +17,7 @@ module Volt
17
17
  if @model.path == []
18
18
  json_data = LocalStorage['volt-store']
19
19
  if json_data
20
- root_attributes = JSON.parse(json_data)
20
+ root_attributes = EJSON.parse(json_data)
21
21
 
22
22
  @loading_data = true
23
23
  root_attributes.each_pair do |key, value|
@@ -37,7 +37,7 @@ module Volt
37
37
  def save_all
38
38
  return if @loading_data
39
39
 
40
- json_data = JSON.dump(@model.to_h)
40
+ json_data = EJSON.stringify(@model.to_h)
41
41
 
42
42
  LocalStorage['volt-store'] = json_data
43
43
  end
@@ -1,11 +1,6 @@
1
1
  require 'volt/models/persistors/store'
2
2
  require 'volt/models/persistors/store_state'
3
3
 
4
- if RUBY_PLATFORM == 'opal'
5
- else
6
- require 'mongo'
7
- end
8
-
9
4
  module Volt
10
5
  module Persistors
11
6
  class ModelStore < Store
@@ -8,15 +8,18 @@ module Volt
8
8
  include ReactiveAccessors
9
9
 
10
10
  # TODO: we need to make it so change events only trigger on changes
11
- reactive_accessor :scheme, :host, :port, :path, :query, :params, :fragment
11
+ reactive_accessor :scheme, :host, :port, :path, :query, :fragment
12
12
  attr_accessor :router
13
13
 
14
14
  def initialize(router = nil)
15
15
  @router = router
16
- @params = Model.new({}, persistor: Persistors::Params)
17
16
  @location = Location.new
18
17
  end
19
18
 
19
+ def params
20
+ @params ||= Model.new({}, persistor: Persistors::Params)
21
+ end
22
+
20
23
  # Parse takes in a url and extracts each sections.
21
24
  # It also assigns and changes to the params.
22
25
  def parse(url)
@@ -95,7 +98,7 @@ module Volt
95
98
  end
96
99
 
97
100
  def url_with(params)
98
- url_for(@params.to_h.merge(params))
101
+ url_for(params.to_h.merge(params))
99
102
  end
100
103
 
101
104
  # Called when the state has changed and the url in the
@@ -103,7 +106,7 @@ module Volt
103
106
  # Called when an attribute changes to update the url
104
107
  def update!
105
108
  if Volt.in_browser?
106
- new_url = url_for(@params.to_h)
109
+ new_url = url_for(params.to_h)
107
110
 
108
111
  # Push the new url if pushState is supported
109
112
  # TODO: add fragment fallback
@@ -158,8 +161,9 @@ module Volt
158
161
  query_hash.merge!(new_params)
159
162
 
160
163
  # Loop through the .params we already have assigned.
161
- assign_from_old(@params, query_hash)
162
- assign_new(@params, query_hash)
164
+ lparams = params
165
+ assign_from_old(lparams, query_hash)
166
+ assign_new(lparams, query_hash)
163
167
  end
164
168
 
165
169
  # Loop through the old params, and overwrite any existing values,
@@ -1,6 +1,6 @@
1
1
  # The channel is the connection between the front end and the backend.
2
2
 
3
- require 'json'
3
+ require 'volt/utils/ejson'
4
4
  require 'volt/reactive/reactive_accessors'
5
5
  require 'volt/reactive/eventable'
6
6
 
@@ -94,7 +94,7 @@ module Volt
94
94
  end
95
95
 
96
96
  def message_received(message)
97
- message = JSON.parse(message)
97
+ message = EJSON.parse(message)
98
98
 
99
99
  trigger!('message', *message)
100
100
  end
@@ -104,7 +104,7 @@ module Volt
104
104
  @queue << message
105
105
  else
106
106
  # TODO: Temp: wrap message in an array, so we're sure its valid JSON
107
- message = JSON.dump([message])
107
+ message = EJSON.stringify([message])
108
108
  `
109
109
  this.socket.send(message);
110
110
  `
@@ -1,3 +1,4 @@
1
+ require 'volt/utils/ejson'
1
2
 
2
3
  module Volt
3
4
  class Page
@@ -178,7 +179,7 @@ module Volt
178
179
  `if (page_obj_str) {`
179
180
  `sessionStorage.removeItem('___page');`
180
181
 
181
- JSON.parse(page_obj_str).each_pair do |key, value|
182
+ EJSON.parse(page_obj_str).each_pair do |key, value|
182
183
  page.send(:"_#{key}=", value)
183
184
  end
184
185
  `}`
@@ -1,3 +1,5 @@
1
+ require 'volt/utils/ejson'
2
+
1
3
  module Volt
2
4
  # The tasks class provides an interface to call tasks on
3
5
  # the backend server. This class is setup as page.task (as a singleton)
@@ -63,7 +65,7 @@ module Volt
63
65
 
64
66
  def reload
65
67
  # Stash the current page value
66
- value = JSON.dump($page.page.to_h)
68
+ value = EJSON.stringify($page.page.to_h)
67
69
 
68
70
  # If this browser supports session storage, store the page, so it will
69
71
  # be in the same state when we reload.
@@ -45,7 +45,8 @@ module Volt
45
45
  end
46
46
  end
47
47
 
48
- count
48
+ # return as a promise
49
+ count.then
49
50
  else
50
51
  size
51
52
  end
data/lib/volt/server.rb CHANGED
@@ -17,6 +17,7 @@ require 'volt/page/page'
17
17
  require 'volt/server/websocket/websocket_handler'
18
18
  require 'volt/utils/read_write_lock'
19
19
  require 'volt/server/forking_server'
20
+ require 'volt/server/websocket/rack_server_adaptor'
20
21
 
21
22
 
22
23
  module Volt
@@ -49,6 +50,9 @@ module Volt
49
50
  # killed when code changes and reforked. (This provides simple fast code
50
51
  # reloading)
51
52
  def app
53
+ # Setup the rack server and adaptor
54
+ RackServerAdaptor.load
55
+
52
56
  app = Rack::Builder.new
53
57
 
54
58
  # Handle websocket connections
@@ -4,6 +4,15 @@ require 'drb'
4
4
  require 'stringio'
5
5
  require 'listen'
6
6
 
7
+ class ErrorDispatcher
8
+ def dispatch(channel, message)
9
+ Volt.logger.error("The app failed to start, so the following message can not be run: #{message}")
10
+ end
11
+
12
+ def close_channel(channel)
13
+ end
14
+ end
15
+
7
16
  module Volt
8
17
  class ForkingServer
9
18
  def initialize(server)
@@ -58,11 +67,17 @@ module Volt
58
67
  # Running as child
59
68
  @reader.close
60
69
 
61
- volt_app = @server.boot_volt
62
- @rack_app = volt_app.middleware
70
+ begin
71
+ volt_app = @server.boot_volt
72
+ @rack_app = volt_app.middleware
73
+
74
+ # Set the drb object locally
75
+ @dispatcher = Dispatcher.new(volt_app)
76
+ rescue Exception => error
77
+ boot_error(error)
78
+ end
79
+
63
80
 
64
- # Set the drb object locally
65
- @dispatcher = Dispatcher.new(volt_app)
66
81
  drb_object = DRb.start_service('drbunix:', [self, @dispatcher])
67
82
 
68
83
  @writer.puts(drb_object.uri)
@@ -79,6 +94,28 @@ module Volt
79
94
  end
80
95
  end
81
96
 
97
+ # called from the child when the boot failes. Sets up an error page rack
98
+ # app to show the user the error and handle reloading requests.
99
+ def boot_error(error)
100
+ msg = error.inspect
101
+ if error.respond_to?(:backtrace)
102
+ msg << "\n" + error.backtrace.join("\n")
103
+ end
104
+ Volt.logger.error(msg)
105
+
106
+ # Only require when needed
107
+ require 'cgi'
108
+ @rack_app = Proc.new do
109
+ path = File.join(File.dirname(__FILE__), "forking_server/boot_error.html.erb")
110
+ html = File.read(path)
111
+ error_page = ERB.new(html, nil, '-').result(binding)
112
+
113
+ [500, {"Content-Type" => "text/html"}, error_page]
114
+ end
115
+
116
+ @dispatcher = ErrorDispatcher.new
117
+ end
118
+
82
119
 
83
120
  def stop_child
84
121
  # clear the drb object and kill the child process.
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+
6
+ <script>
7
+ // Simple code to handle reload messages
8
+ if (document.location.protocol == 'https:') {
9
+ var wsProto = 'wss';
10
+ } else {
11
+ var wsProto = 'ws';
12
+ }
13
+
14
+ this.socket = new WebSocket(wsProto + '://' + document.location.host + '/socket');
15
+
16
+ // Log errors
17
+ this.socket.onerror = function (error) {
18
+ document.location.reload();
19
+ };
20
+
21
+ // Log messages from the server
22
+ this.socket.onmessage = function(message) {
23
+ if (message.data == '["reload"]') {
24
+ document.location.reload();
25
+ }
26
+ };
27
+
28
+ this.socket.onclose = function(error) {
29
+ document.location.reload();
30
+ };
31
+ </script>
32
+ </head>
33
+ <body>
34
+ <h2><%= CGI::escapeHTML(error.inspect) %></h2>
35
+
36
+ <% if error.respond_to?(:backtrace) %>
37
+ <pre>
38
+ <%= error.backtrace.map {|l| CGI::escapeHTML(l) }.join("<br />") %>
39
+ </pre>
40
+ <% end %>
41
+ </body>
42
+ </html>
@@ -22,7 +22,7 @@ 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.select {|gem| gem.name =~ /^volt/ }
25
+ Gem.loaded_specs.values.select {|gem| gem.name =~ /^(volt|ejson_ext)/ }
26
26
  .each do |gem|
27
27
  ['app', 'lib'].each do |folder|
28
28
  path = gem.full_gem_path + "/#{folder}"
@@ -1,4 +1,4 @@
1
- require 'json'
1
+ require 'volt/utils/ejson'
2
2
  require File.join(File.dirname(__FILE__), '../../../app/volt/tasks/query_tasks')
3
3
 
4
4
  module Volt
@@ -9,6 +9,14 @@ module Volt
9
9
  # This may be changed as new listeners connect, which is fine.
10
10
  attr_accessor :user_id
11
11
 
12
+
13
+ def initialize(session, *args)
14
+ @session = session
15
+
16
+ @@channels ||= []
17
+ @@channels << self
18
+ end
19
+
12
20
  def self.dispatcher=(val)
13
21
  @@dispatcher = val
14
22
  end
@@ -26,17 +34,10 @@ module Volt
26
34
  end
27
35
  end
28
36
 
29
- def initialize(session, *args)
30
- @session = session
31
-
32
- @@channels ||= []
33
- @@channels << self
34
- end
35
-
36
37
  def process_message(message)
37
38
  # self.class.message_all(message)
38
39
  # Messages are json and wrapped in an array
39
- message = JSON.parse(message).first
40
+ message = EJSON.parse(message).first
40
41
 
41
42
  begin
42
43
  @@dispatcher.dispatch(self, message)
@@ -51,9 +52,17 @@ module Volt
51
52
  end
52
53
 
53
54
  def send_message(*args)
54
- str = JSON.dump([*args])
55
+ str = EJSON.stringify([*args])
55
56
 
56
57
  @session.send(str)
58
+
59
+ if RUNNING_SERVER == 'thin'
60
+ # This might seem strange, but it prevents a delay with outgoing
61
+ # messages.
62
+ # TODO: Figure out the cause of the issue and submit a fix upstream.
63
+ EM.next_tick {}
64
+ end
65
+
57
66
  end
58
67
 
59
68
  def closed
@@ -1,13 +1,10 @@
1
1
  require 'faye/websocket'
2
2
  require 'volt/server/socket_connection_handler'
3
- require 'volt/server/websocket/rack_server_adaptor'
3
+
4
4
 
5
5
  module Volt
6
6
  class WebsocketHandler
7
7
  def initialize(app)
8
- # Setup the rack server and adaptor
9
- RackServerAdaptor.load
10
-
11
8
  @app = app
12
9
  end
13
10
 
@@ -49,7 +49,7 @@ module Volt
49
49
 
50
50
  # Setup the spec collection accessors
51
51
  # RSpec.shared_context "volt collections", {} do
52
- RSpec.shared_examples_for 'volt collections', {} do
52
+ RSpec.shared_context 'volt collections', {} do
53
53
  # Page conflicts with capybara's page method, so we call it the_page for now.
54
54
  # TODO: we need a better solution for page
55
55
 
@@ -60,7 +60,15 @@ module Volt
60
60
  $page.store
61
61
  end
62
62
  let(:volt_app) { volt_app }
63
+ let(:params) { volt_app.page.params }
63
64
 
65
+ after do
66
+ # Clear params if used
67
+ url = volt_app.page.url
68
+ if url.instance_variable_get('@params')
69
+ url.instance_variable_set('@params', nil)
70
+ end
71
+ end
64
72
 
65
73
  if RUBY_PLATFORM != 'opal'
66
74
  after do |example|
@@ -2,6 +2,7 @@
2
2
  require 'volt/utils/logging/task_logger'
3
3
  require 'drb'
4
4
  require 'concurrent'
5
+ require 'timeout'
5
6
 
6
7
  module Volt
7
8
  # The task dispatcher is responsible for taking incoming messages
@@ -95,11 +96,13 @@ module Volt
95
96
  # Init and send the method
96
97
  promise = promise.then do
97
98
  result = nil
98
- Concurrent.timeout(klass.__timeout || @worker_timeout) do
99
+ Timeout.timeout(klass.__timeout || @worker_timeout) do
99
100
  Thread.current['meta'] = meta_data
100
- result = klass.new(@volt_app, channel, self).send(method_name, *args)
101
-
102
- Thread.current['meta'] = nil
101
+ begin
102
+ result = klass.new(@volt_app, channel, self).send(method_name, *args)
103
+ ensure
104
+ Thread.current['meta'] = nil
105
+ end
103
106
  end
104
107
 
105
108
  result
@@ -112,8 +115,9 @@ module Volt
112
115
 
113
116
  # Called after task runs or fails
114
117
  finish = proc do |error|
115
- if error.is_a?(Concurrent::TimeoutError)
116
- error = Concurrent::TimeoutError.new("Task Timed Out after #{@worker_timeout} seconds: #{message}")
118
+ if error.is_a?(Timeout::Error)
119
+ # re-raise with a message
120
+ error = Timeout::Error.new("Task Timed Out after #{@worker_timeout} seconds: #{message}")
117
121
  end
118
122
 
119
123
  run_time = ((Time.now.to_f - start_time) * 1000).round(3)
@@ -1,11 +1,60 @@
1
+ require 'json'
2
+
1
3
  module Volt
2
- class EJson
3
- def self.dump_as(obj)
4
- obj
4
+ class EJSON
5
+ def self.stringify(obj)
6
+ encode(obj).to_json
7
+ end
8
+
9
+ def self.parse(str)
10
+ decode(JSON.parse(str))
11
+ end
12
+
13
+ private
14
+
15
+ def self.decode(obj)
16
+ if Array === obj
17
+ obj.map {|v| decode(v) }
18
+ elsif Hash === obj
19
+ if obj.size == 1 && (escape = obj['$escape'])
20
+ return escape.map do |key, value|
21
+ [key, decode(value)]
22
+ end.to_h
23
+ elsif obj.size == 1 && (time = obj['$date'])
24
+ if time.is_a?(Fixnum)
25
+ return Time.at(time / 1000.0)
26
+ end
27
+ end
28
+
29
+ obj.map do |key, value|
30
+ [key, decode(value)]
31
+ end.to_h
32
+ else
33
+ obj
34
+ end
5
35
  end
6
36
 
7
- def self.dump(obj)
8
- JSON.dump(dump_as(obj))
37
+ def self.encode(obj)
38
+ if Array === obj
39
+ obj.map {|v| encode(v) }
40
+ elsif Hash === obj
41
+ obj.map do |key, value|
42
+ if key == '$date'
43
+ key = '$escape'
44
+ value = {'$date' => encode(value)}
45
+ else
46
+ value = encode(value)
47
+ end
48
+
49
+ [key, value]
50
+ end.to_h
51
+ else
52
+ if obj.is_a?(Time)
53
+ {'$date' => obj.to_i * 1_000}
54
+ else
55
+ obj
56
+ end
57
+ end
9
58
  end
10
59
  end
11
- end
60
+ end
@@ -4,16 +4,25 @@ require 'volt/utils/promise'
4
4
  # A temp patch for promises until https://github.com/opal/opal/pull/725 is released.
5
5
  class Promise
6
6
 
7
- def method_missing(method_name, *args, &block)
8
- promise = self.then do |value|
9
- value.send(method_name, *args, &block)
10
- end
11
-
12
- promise
7
+ # We made a choice not to support comparitors and << and >> on method_missing
8
+ # on Promises. This makes it easier to understand what promise proxying does
9
+ # and how it works. It also prevents confusing situations where you try to
10
+ # == compare two Promises for example. The cost though is more code to do
11
+ # comparisons, but we feel it is worth it.
12
+ def respond_to_missing?(method_name, include_private = false)
13
+ !!(method_name =~ /[a-z_]\w*[?!=]?/)
13
14
  end
14
15
 
15
- def respond_to_missing(method_name, include_private = false)
16
- true
16
+ def method_missing(method_name, *args, &block)
17
+ if respond_to_missing?(method_name)
18
+ promise = self.then do |value|
19
+ value.send(method_name, *args, &block)
20
+ end
21
+
22
+ promise
23
+ else
24
+ super
25
+ end
17
26
  end
18
27
 
19
28
  # Allow .each to be called directly on promises
@@ -56,6 +65,20 @@ class Promise
56
65
  @value || @error
57
66
  end
58
67
 
68
+ # When testing with rspec, add in a custom exception! method that doesn't
69
+ # swallow ExpectationNotMetError's.
70
+ if defined?(RSpec::Expectations::ExpectationNotMetError)
71
+ def exception!(error)
72
+ if error.is_a?(RSpec::Expectations::ExpectationNotMetError)
73
+ raise error
74
+ end
75
+ @exception = true
76
+
77
+ reject!(error)
78
+ end
79
+ end
80
+
81
+
59
82
 
60
83
  # Waits for the promise to resolve (assuming it is blocking on
61
84
  # the server) and returns the result.
data/lib/volt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Version
3
- STRING = '0.9.3.pre5'
3
+ STRING = '0.9.3.pre6'
4
4
  end
5
5
  end
@@ -48,7 +48,8 @@ module Volt
48
48
 
49
49
  # This config needs to run earlier than others
50
50
  def run_config
51
- require("#{Volt.root}/config/app.rb")
51
+ path = "#{Volt.root}/config/app.rb"
52
+ require(path) if File.exists?(path)
52
53
  end
53
54
 
54
55
  # Load in all .rb files in the initializers folders and the config/app.rb
@@ -232,18 +232,20 @@ describe Volt::Model do
232
232
  expect(count).to eq(1)
233
233
  end
234
234
 
235
- it 'should track changes through an expansion' do
236
- a = Volt::Model.new
235
+ unless RUBY_PLATFORM == 'opal'
236
+ it 'should track changes through an expansion' do
237
+ a = Volt::Model.new
237
238
 
238
- last_count = 0
239
- -> { last_count = a._todos.count(&:_checked) }.watch!
239
+ last_count = 0
240
+ -> { last_count = a._todos.count(&:_checked).sync }.watch!
240
241
 
241
- expect(last_count).to eq(0)
242
+ expect(last_count).to eq(0)
242
243
 
243
- a._todos! << { checked: true }
244
- Volt::Computation.flush!
244
+ a._todos! << { checked: true }
245
+ Volt::Computation.flush!
245
246
 
246
- expect(last_count).to eq(1)
247
+ expect(last_count).to eq(1)
248
+ end
247
249
  end
248
250
 
249
251
  it 'should call changed when a the reference to a submodel is assigned to another value' do
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Volt::LengthValidator do
4
- subject { Volt::LengthValidator.validate(*params) }
5
- let(:params) { [model, field_name, options] }
4
+ subject { Volt::LengthValidator.validate(*use_params) }
5
+ let(:use_params) { [model, field_name, options] }
6
6
 
7
7
  let(:model) { Volt::Model.new name: name }
8
8
  let(:field_name) { :name }
@@ -15,8 +15,8 @@ describe Volt::PhoneNumberValidator do
15
15
  let(:valid_intl_number) { '+12 123 123 1234' }
16
16
  let(:invalid_number) { '1234-123-123456' }
17
17
 
18
- let(:validate) { described_class.validate(*params) }
19
- let(:params) { [model, field_name, options] }
18
+ let(:validate) { described_class.validate(*use_params) }
19
+ let(:use_params) { [model, field_name, options] }
20
20
  let(:message) { 'must be a phone number with area or country code' }
21
21
 
22
22
  it_behaves_like 'a format validator'
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe Volt::EJSON, '.parse' do
4
+ subject { Volt::EJSON }
5
+ let(:epoch) { 135820576553 }
6
+ let(:ruby_epoch) { epoch / 1000.0 }
7
+
8
+ context 'safe escaping' do
9
+ it 'does not parse date objects with invalid values' do
10
+ parsed = subject.parse '{"a" : {"$escape" : {"$date" : "something"}}}'
11
+
12
+ expect(parsed).to eq('a' => { '$date' => 'something' })
13
+ end
14
+
15
+ it 'only escapes one level down' do
16
+ parsed = subject.parse %({"$escape": {"$date": {"$date": #{epoch}}}})
17
+
18
+ expect(parsed).to eq('$date' => Time.at(ruby_epoch))
19
+ end
20
+ end
21
+
22
+ context 'parsing EJSON fields' do
23
+ context 'date' do
24
+ it 'is not parsed when given a bad value' do
25
+ expect(subject.parse '{"a": {"$date" : "something"}}').
26
+ to eq('a' => { '$date' => 'something' })
27
+ end
28
+
29
+ it 'parses proper $date EJSON fields' do
30
+ parsed = subject.parse '{"a" : {"$date": 135820576553}}'
31
+
32
+ expect(parsed['a']).to eq Time.at(ruby_epoch)
33
+ end
34
+
35
+ it 'parses nested EJSON date fields' do
36
+ parsed = subject.parse '{"a" : {"b" : {"$date": 135820576553}}}'
37
+
38
+ expect(parsed['a']['b']).to eq Time.at(ruby_epoch)
39
+ end
40
+
41
+ it 'parses nested $dates within $escapes' do
42
+ parsed = subject.parse(
43
+ '{"a" : {"$escape": {"$date" : {"date" : {"$date": 135820576553}}}}}'
44
+ )
45
+
46
+ expect(parsed['a']['$date']['date']).to eq Time.at(ruby_epoch)
47
+ end
48
+
49
+ it 'parses multiple EJSON date fields' do
50
+ ejson = begin
51
+ %({"when":{"$date":#{epoch}},"then":{"$date":#{epoch}}})
52
+ end
53
+
54
+ expect(subject.parse ejson).to eq(
55
+ "when" => Time.at(ruby_epoch),
56
+ "then" => Time.at(ruby_epoch)
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ describe Volt::EJSON, '.stringify' do
64
+ subject { Volt::EJSON }
65
+ context 'marshaling dates' do
66
+ let(:now) { Time.now }
67
+ let(:now_js_epoch) { now.to_i * 1_000 }
68
+
69
+ it 'does nothing with regular hashes' do
70
+ stringified = subject.stringify plain: 'jane'
71
+
72
+ expect(stringified).to eq '{"plain":"jane"}'
73
+ end
74
+
75
+ it 'marshals when given a date' do
76
+ stringified = subject.stringify when: now
77
+
78
+ expect(stringified).to eq %({"when":{"$date":#{now_js_epoch}}})
79
+ end
80
+
81
+ it 'marshals nested dates' do
82
+ stringified = subject.stringify how: { when: now }
83
+
84
+ expect(stringified).to eq %({"how":{"when":{"$date":#{now_js_epoch}}}})
85
+ end
86
+
87
+ it 'marshals multiple dates' do
88
+ stringified = subject.stringify when: now, then: now
89
+
90
+ expect(stringified.gsub(' ', '')).to eq(
91
+ %({"when":{"$date":#{now_js_epoch}},"then":{"$date":#{now_js_epoch}}})
92
+ )
93
+ end
94
+
95
+ it 'escapes reserved key when type is incorrect' do
96
+ stringified = subject.stringify '$date' => 'something'
97
+
98
+ expect(stringified).to eq '{"$escape":{"$date":"something"}}'
99
+ end
100
+ end
101
+ end
@@ -39,4 +39,20 @@ describe Promise do
39
39
 
40
40
  expect(count_occurences(b.inspect, 'Promise')).to eq(1)
41
41
  end
42
+
43
+ it 'should not respond to comparitors' do
44
+ [:>, :<].each do |comp|
45
+ a = Promise.new
46
+ expect do
47
+ a.send(comp, 5)
48
+ end.to raise_error(NoMethodError)
49
+ end
50
+ end
51
+
52
+ it 'should proxy methods on promises' do
53
+ a = Promise.new
54
+ expect do
55
+ a.something
56
+ end.not_to raise_error
57
+ end
42
58
  end
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "volt", "~> <%= config[:volt_version_base] %>"
22
+ spec.add_development_dependency 'rspec', '~> 3.2.0'
22
23
  spec.add_development_dependency "rake"
23
24
  <% if config[:test] -%>
24
25
  spec.add_development_dependency "<%=config[:test]%>"
@@ -1,2 +1,14 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
- require '<%= config[:namespaced_path] %>'
1
+ # Volt sets up rspec and capybara for testing.
2
+ require 'volt/spec/setup'
3
+ Volt.spec_setup
4
+
5
+ RSpec.configure do |config|
6
+ config.run_all_when_everything_filtered = true
7
+ config.filter_run :focus
8
+
9
+ # Run specs in random order to surface order dependencies. If you find an
10
+ # order dependency and want to debug it, you can fix the order by providing
11
+ # the seed, which is printed after each run.
12
+ # --seed 1234
13
+ config.order = 'random'
14
+ end
@@ -1,10 +1,9 @@
1
- <%# IMPORTANT: Please read before changing! %>
2
- <%# This file is rendered on the server using ERB, so it does NOT use Volt's %>
3
- <%# normal template system. You can add to it, but keep in mind the template %>
4
- <%# language difference. This file handles auto-loading all JS/Opal and CSS. %>
5
-
6
1
  <!DOCTYPE html>
7
2
  <html>
3
+ <%# IMPORTANT: Please read before changing! %>
4
+ <%# This file is rendered on the server using ERB, so it does NOT use Volt's %>
5
+ <%# normal template system. You can add to it, but keep in mind the template %>
6
+ <%# language difference. This file handles auto-loading all JS/Opal and CSS. %>
8
7
  <head>
9
8
  <meta charset="UTF-8" />
10
9
  <% javascript_files.each do |javascript_file| %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3.pre5
4
+ version: 0.9.3.pre6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Stout
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-07 00:00:00.000000000 Z
11
+ date: 2015-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -526,6 +526,7 @@ files:
526
526
  - lib/volt/server/component_handler.rb
527
527
  - lib/volt/server/component_templates.rb
528
528
  - lib/volt/server/forking_server.rb
529
+ - lib/volt/server/forking_server/boot_error.html.erb
529
530
  - lib/volt/server/html_parser/attribute_scope.rb
530
531
  - lib/volt/server/html_parser/component_view_scope.rb
531
532
  - lib/volt/server/html_parser/each_scope.rb
@@ -720,6 +721,7 @@ files:
720
721
  - spec/tasks/query_tracker_spec.rb
721
722
  - spec/tasks/user_tasks_spec.rb
722
723
  - spec/templates/targets/binding_document/component_node_spec.rb
724
+ - spec/utils/ejson_spec.rb
723
725
  - spec/utils/generic_counting_pool_spec.rb
724
726
  - spec/utils/generic_pool_spec.rb
725
727
  - spec/utils/parsing_spec.rb
@@ -964,6 +966,7 @@ test_files:
964
966
  - spec/tasks/query_tracker_spec.rb
965
967
  - spec/tasks/user_tasks_spec.rb
966
968
  - spec/templates/targets/binding_document/component_node_spec.rb
969
+ - spec/utils/ejson_spec.rb
967
970
  - spec/utils/generic_counting_pool_spec.rb
968
971
  - spec/utils/generic_pool_spec.rb
969
972
  - spec/utils/parsing_spec.rb