volt 0.8.21 → 0.8.22.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -10
- data/CHANGELOG.md +8 -0
- data/Gemfile +7 -33
- data/Readme.md +2 -0
- data/VERSION +1 -1
- data/app/volt/assets/js/{setImmediate.js → volt_js_polyfills.js} +14 -0
- data/app/volt/models/user.rb +21 -26
- data/app/volt/tasks/live_query/data_store.rb +4 -0
- data/app/volt/tasks/store_tasks.rb +8 -11
- data/app/volt/tasks/user_tasks.rb +18 -17
- data/lib/volt.rb +7 -1
- data/lib/volt/cli.rb +2 -0
- data/lib/volt/config.rb +69 -29
- data/lib/volt/extra_core/object.rb +8 -0
- data/lib/volt/models/model_hash_behaviour.rb +26 -6
- data/lib/volt/models/persistors/model_store.rb +1 -2
- data/lib/volt/models/validations.rb +18 -9
- data/lib/volt/models/validators/length_validator.rb +1 -1
- data/lib/volt/models/validators/presence_validator.rb +1 -1
- data/lib/volt/models/validators/unique_validator.rb +1 -1
- data/lib/volt/page/bindings/template_binding.rb +2 -1
- data/lib/volt/page/page.rb +7 -2
- data/lib/volt/page/sub_context.rb +8 -4
- data/lib/volt/page/targets/base_section.rb +1 -1
- data/lib/volt/page/targets/dom_template.rb +1 -1
- data/lib/volt/server.rb +3 -2
- data/lib/volt/server/html_parser/view_scope.rb +1 -0
- data/lib/volt/server/rack/component_code.rb +8 -1
- data/lib/volt/server/rack/component_paths.rb +2 -1
- data/lib/volt/server/rack/quiet_common_logger.rb +34 -0
- data/lib/volt/spec/setup.rb +30 -1
- data/lib/volt/utils/generic_pool.rb +4 -0
- data/lib/volt/volt/users.rb +18 -0
- data/spec/apps/kitchen_sink/Gemfile +3 -0
- data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +6 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +5 -1
- data/spec/apps/kitchen_sink/app/main/models/user.rb +2 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -1
- data/spec/apps/kitchen_sink/{public → config/base}/index.html +0 -0
- data/spec/integration/bindings_spec.rb +1 -1
- data/spec/integration/cookies_spec.rb +1 -1
- data/spec/integration/flash_spec.rb +3 -2
- data/spec/integration/list_spec.rb +1 -1
- data/spec/integration/templates_spec.rb +1 -1
- data/spec/integration/url_spec.rb +1 -1
- data/spec/integration/user_spec.rb +60 -0
- data/spec/models/model_spec.rb +0 -1
- data/spec/page/bindings/template_binding_spec.rb +0 -4
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/templates/component/tasks/.empty_directory +0 -0
- data/templates/newgem/app/newgem/views/main/{main.html → index.html} +0 -0
- data/templates/project/Gemfile.tt +0 -11
- data/templates/project/app/main/config/routes.rb +5 -0
- data/templates/project/app/main/tasks/.empty_directory +0 -0
- data/templates/project/app/main/views/main/main.html.tt +1 -0
- data/templates/project/config/app.rb.tt +8 -3
- data/volt.gemspec +4 -6
- metadata +58 -12
- data/spec/apps/kitchen_sink/app/main/controllers/users_test_controller.rb +0 -27
- data/spec/apps/kitchen_sink/app/main/views/users_test/index.html +0 -22
@@ -21,9 +21,19 @@ module Volt
|
|
21
21
|
@attributes.size
|
22
22
|
end
|
23
23
|
|
24
|
+
# Returns all of the keys, skipping over nil models
|
25
|
+
# TODO: We should store nil-models elsewhere so we don't have
|
26
|
+
# to skip.
|
24
27
|
def keys
|
25
28
|
@size_dep.depend
|
26
|
-
|
29
|
+
|
30
|
+
keys = []
|
31
|
+
|
32
|
+
each_pair do |k,v|
|
33
|
+
keys << k
|
34
|
+
end
|
35
|
+
|
36
|
+
keys
|
27
37
|
end
|
28
38
|
|
29
39
|
def nil?
|
@@ -44,20 +54,30 @@ module Volt
|
|
44
54
|
end
|
45
55
|
|
46
56
|
def clear
|
47
|
-
@attributes.each_pair do |key,
|
48
|
-
|
57
|
+
@attributes.each_pair do |key, _|
|
58
|
+
delete(key)
|
49
59
|
end
|
50
60
|
|
51
|
-
@attributes.clear
|
61
|
+
# @attributes.clear
|
52
62
|
@size_dep.changed!
|
53
|
-
|
54
|
-
@persistor.removed(nil) if @persistor
|
63
|
+
#
|
64
|
+
# @persistor.removed(nil) if @persistor
|
55
65
|
end
|
56
66
|
|
57
67
|
def each_with_object(*args, &block)
|
58
68
|
(@attributes || {}).each_with_object(*args, &block)
|
59
69
|
end
|
60
70
|
|
71
|
+
def each_pair
|
72
|
+
@attributes.each_pair do |k,v|
|
73
|
+
yield(k,v) unless v.is_a?(Model) && v.nil?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def key?(key)
|
78
|
+
@attributes && @attributes.key?(key)
|
79
|
+
end
|
80
|
+
|
61
81
|
# Convert the model to a hash all of the way down.
|
62
82
|
def to_h
|
63
83
|
@size_dep.depend
|
@@ -91,7 +91,6 @@ module Volt
|
|
91
91
|
|
92
92
|
queue_client_save
|
93
93
|
else
|
94
|
-
puts "Save to DB"
|
95
94
|
errors = save_to_db!(self_attributes)
|
96
95
|
if errors.size == 0
|
97
96
|
promise.resolve(nil)
|
@@ -204,7 +203,7 @@ module Volt
|
|
204
203
|
end
|
205
204
|
end
|
206
205
|
|
207
|
-
puts "Update Collection: #{collection.inspect} - #{values.inspect} -- #{Thread.current['in_channel'].inspect}"
|
206
|
+
# puts "Update Collection: #{collection.inspect} - #{values.inspect} -- #{Thread.current['in_channel'].inspect}"
|
208
207
|
QueryTasks.live_query_pool.updated_collection(collection.to_s, Thread.current['in_channel'])
|
209
208
|
return {}
|
210
209
|
end
|
@@ -84,14 +84,22 @@ module Volt
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
# Get the previous model from the buffer
|
88
|
+
save_to = options[:save_to]
|
89
|
+
if save_to && save_to.is_a?(Volt::Model)
|
90
|
+
old_model = save_to
|
91
|
+
else
|
92
|
+
old_model = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
errors = run_validations(errors, merge, marked_only, old_model)
|
88
96
|
|
89
97
|
# See if any server errors are in place and merge them in if they are
|
90
98
|
if Volt.client?
|
91
99
|
errors = merge.call(server_errors.to_h)
|
92
100
|
end
|
93
101
|
|
94
|
-
errors = run_custom_validations(errors, merge)
|
102
|
+
errors = run_custom_validations(errors, merge, old_model)
|
95
103
|
|
96
104
|
errors
|
97
105
|
end
|
@@ -99,7 +107,7 @@ module Volt
|
|
99
107
|
private
|
100
108
|
|
101
109
|
# Runs through each of the normal validations.
|
102
|
-
def run_validations(errors, merge, marked_only)
|
110
|
+
def run_validations(errors, merge, marked_only, old_model)
|
103
111
|
validations = self.class.validations
|
104
112
|
if validations
|
105
113
|
|
@@ -114,7 +122,7 @@ module Volt
|
|
114
122
|
klass = validation_class(validation, args)
|
115
123
|
|
116
124
|
if klass
|
117
|
-
validate_with(merge, klass, field_name, args)
|
125
|
+
validate_with(merge, klass, old_model, field_name, args)
|
118
126
|
else
|
119
127
|
fail "validation type #{validation} is not specified."
|
120
128
|
end
|
@@ -125,13 +133,14 @@ module Volt
|
|
125
133
|
return errors
|
126
134
|
end
|
127
135
|
|
128
|
-
def run_custom_validations(errors, merge)
|
136
|
+
def run_custom_validations(errors, merge, old_model)
|
129
137
|
# Call all of the custom validations
|
130
138
|
custom_validations = self.class.custom_validations
|
131
139
|
if custom_validations
|
132
140
|
custom_validations.each do |custom_validation|
|
133
|
-
# Run the validator in the context of the model
|
134
|
-
|
141
|
+
# Run the validator in the context of the model, passes in
|
142
|
+
# the old_model as an argument
|
143
|
+
result = instance_eval(old_model, &custom_validation)
|
135
144
|
if result
|
136
145
|
errors = merge.call(result)
|
137
146
|
end
|
@@ -143,8 +152,8 @@ module Volt
|
|
143
152
|
|
144
153
|
|
145
154
|
# calls the validate method on the class, passing the right arguments.
|
146
|
-
def validate_with(merge, klass, field_name, args)
|
147
|
-
merge.call(klass.validate(self, field_name, args))
|
155
|
+
def validate_with(merge, klass, old_model, field_name, args)
|
156
|
+
merge.call(klass.validate(self, old_model, field_name, args))
|
148
157
|
end
|
149
158
|
|
150
159
|
def validation_class(validation, args)
|
@@ -161,7 +161,8 @@ module Volt
|
|
161
161
|
|
162
162
|
# The context for templates can be either a controller, or the original context.
|
163
163
|
def render_template(full_path, controller_path)
|
164
|
-
|
164
|
+
# If arguments is nil, then an blank SubContext will be created
|
165
|
+
args = [SubContext.new(@arguments, nil, true)]
|
165
166
|
|
166
167
|
@controller = nil
|
167
168
|
|
data/lib/volt/page/page.rb
CHANGED
@@ -140,8 +140,13 @@ module Volt
|
|
140
140
|
attr_reader :events
|
141
141
|
|
142
142
|
def add_model(model_name)
|
143
|
-
|
144
|
-
|
143
|
+
begin
|
144
|
+
model_name = model_name.camelize.to_sym
|
145
|
+
@model_classes[model_name] = Object.const_get(model_name)
|
146
|
+
rescue NameError => e
|
147
|
+
# Handle if the model is user (Volt's provided user model is scoped under Volt::)
|
148
|
+
raise unless model_name == :User
|
149
|
+
end
|
145
150
|
end
|
146
151
|
|
147
152
|
def add_template(name, template, bindings)
|
@@ -8,19 +8,23 @@ module Volt
|
|
8
8
|
class SubContext
|
9
9
|
attr_reader :locals
|
10
10
|
|
11
|
-
def initialize(locals, context = nil, return_nils = false)
|
12
|
-
@locals = locals.stringify_keys
|
11
|
+
def initialize(locals=nil, context = nil, return_nils = false)
|
12
|
+
@locals = locals.stringify_keys if locals
|
13
13
|
@context = context
|
14
14
|
@return_nils = return_nils
|
15
15
|
end
|
16
16
|
|
17
17
|
def respond_to?(method_name)
|
18
|
-
!!(@locals[method_name.to_s] || (@context && @context.respond_to?(method_name)))
|
18
|
+
!!((@locals && @locals[method_name.to_s]) || (@context && @context.respond_to?(method_name)))
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<SubContext #{@locals.inspect} context:#{@context.inspect}>"
|
19
23
|
end
|
20
24
|
|
21
25
|
def method_missing(method_name, *args, &block)
|
22
26
|
method_name = method_name.to_s
|
23
|
-
if @locals.key?(method_name)
|
27
|
+
if @locals && @locals.key?(method_name)
|
24
28
|
obj = @locals[method_name]
|
25
29
|
|
26
30
|
# TODORW: Might get a normal proc, flag internal procs
|
@@ -29,7 +29,7 @@ module Volt
|
|
29
29
|
html = template['html']
|
30
30
|
bindings = template['bindings']
|
31
31
|
else
|
32
|
-
html = "<div>-- < missing template #{template_name.inspect.
|
32
|
+
html = "<div>-- < missing template #{template_name.inspect.html_inspect}, make sure it's component is included in dependencies.rb > --</div>"
|
33
33
|
bindings = {}
|
34
34
|
end
|
35
35
|
|
@@ -16,7 +16,7 @@ module Volt
|
|
16
16
|
html = template['html']
|
17
17
|
@bindings = template['bindings']
|
18
18
|
else
|
19
|
-
html = "<div>-- < missing template #{template_name.inspect.
|
19
|
+
html = "<div>-- < missing template #{template_name.inspect.html_inspect}, make sure it's component is included in dependencies.rb > --</div>"
|
20
20
|
@bindings = {}
|
21
21
|
end
|
22
22
|
|
data/lib/volt/server.rb
CHANGED
@@ -8,11 +8,11 @@ else
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'rack'
|
11
|
+
require 'sass'
|
11
12
|
if RUBY_PLATFORM != 'java'
|
12
13
|
require 'rack/sockjs'
|
13
14
|
require 'eventmachine'
|
14
15
|
end
|
15
|
-
require 'sass'
|
16
16
|
require 'sprockets-sass'
|
17
17
|
require 'listen'
|
18
18
|
|
@@ -27,6 +27,7 @@ end
|
|
27
27
|
require 'volt/server/rack/component_paths'
|
28
28
|
require 'volt/server/rack/index_files'
|
29
29
|
require 'volt/server/rack/opal_files'
|
30
|
+
require 'volt/server/rack/quiet_common_logger'
|
30
31
|
require 'volt/page/page'
|
31
32
|
|
32
33
|
module Rack
|
@@ -94,7 +95,7 @@ module Volt
|
|
94
95
|
@app.use Rack::ConditionalGet
|
95
96
|
@app.use Rack::ETag
|
96
97
|
|
97
|
-
@app.use
|
98
|
+
@app.use QuietCommonLogger
|
98
99
|
@app.use Rack::ShowExceptions
|
99
100
|
|
100
101
|
component_paths = @component_paths
|
@@ -14,7 +14,8 @@ module Volt
|
|
14
14
|
|
15
15
|
# The client argument is for if this code is being generated for the client
|
16
16
|
def code(client=true)
|
17
|
-
|
17
|
+
# Start with config code
|
18
|
+
code = generate_config_code
|
18
19
|
|
19
20
|
asset_files = AssetFiles.new(@component_name, @component_paths)
|
20
21
|
asset_files.component_paths.each do |component_path, component_name|
|
@@ -24,5 +25,11 @@ module Volt
|
|
24
25
|
|
25
26
|
code
|
26
27
|
end
|
28
|
+
|
29
|
+
|
30
|
+
def generate_config_code
|
31
|
+
"\nVolt.setup_client_config(#{Volt.config.public.to_h.inspect})\n"
|
32
|
+
end
|
33
|
+
|
27
34
|
end
|
28
35
|
end
|
@@ -18,7 +18,7 @@ module Volt
|
|
18
18
|
# TODO: we should probably qualify this a bit more
|
19
19
|
app_folders += Gem.loaded_specs.values.map(&:full_gem_path).reject { |g| g !~ /volt/ }.map { |f| f + '/app' }
|
20
20
|
|
21
|
-
app_folders
|
21
|
+
app_folders.uniq
|
22
22
|
end
|
23
23
|
|
24
24
|
# Yield each app folder and return a flattened array with
|
@@ -71,6 +71,7 @@ module Volt
|
|
71
71
|
# Add models to page
|
72
72
|
Dir["#{app_folder}/*/models/*.rb"].each do |ruby_file|
|
73
73
|
class_name = File.basename(ruby_file).gsub(/[.]rb$/, '')
|
74
|
+
|
74
75
|
$page.add_model(class_name)
|
75
76
|
end
|
76
77
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class QuietCommonLogger < Rack::CommonLogger
|
2
|
+
include Rack
|
3
|
+
|
4
|
+
@@ignore_extensions = %w(png jpg jpeg ico gif woff tff svg eot css js)
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
path = env['REQUEST_PATH']
|
8
|
+
began_at = Time.now
|
9
|
+
status, header, body = @app.call(env)
|
10
|
+
header = Utils::HeaderHash.new(header)
|
11
|
+
base = ::File.basename(path)
|
12
|
+
if base.index('.')
|
13
|
+
ext = base.split('.').last
|
14
|
+
else
|
15
|
+
ext = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
body = BodyProxy.new(body) do
|
19
|
+
|
20
|
+
# Don't log on ignored extensions
|
21
|
+
unless @@ignore_extensions.include?(ext)
|
22
|
+
log(env, status, header, began_at)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Because of web sockets, the initial request doesn't finish, so we
|
27
|
+
# can just trigger it now.
|
28
|
+
if !ext && !path.start_with?('/channel')
|
29
|
+
log(env, status, header, began_at)
|
30
|
+
end
|
31
|
+
|
32
|
+
[status, header, body]
|
33
|
+
end
|
34
|
+
end
|
data/lib/volt/spec/setup.rb
CHANGED
@@ -4,6 +4,7 @@ module Volt
|
|
4
4
|
require 'volt'
|
5
5
|
else
|
6
6
|
ENV['SERVER'] = 'true'
|
7
|
+
ENV['VOLT_ENV'] = 'test'
|
7
8
|
|
8
9
|
if ENV['BROWSER']
|
9
10
|
require 'capybara'
|
@@ -16,7 +17,7 @@ module Volt
|
|
16
17
|
require 'volt/boot'
|
17
18
|
|
18
19
|
# Require in app
|
19
|
-
Volt.boot(
|
20
|
+
Volt.boot(app_path)
|
20
21
|
|
21
22
|
if ENV['BROWSER']
|
22
23
|
require 'volt/server'
|
@@ -53,6 +54,34 @@ module Volt
|
|
53
54
|
Capybara::Selenium::Driver.new(app, browser: :safari)
|
54
55
|
end
|
55
56
|
Capybara.default_driver = :safari
|
57
|
+
elsif ENV['BROWSER'] == 'sauce'
|
58
|
+
require "sauce"
|
59
|
+
require "sauce/capybara"
|
60
|
+
|
61
|
+
Sauce.config do |c|
|
62
|
+
if ENV['OS']
|
63
|
+
# Use a specifc OS, BROWSER, VERSION combo (for travis)
|
64
|
+
c[:browsers] = [
|
65
|
+
[ENV['OS'], ENV['USE_BROWSER'], ENV['VERSION']]
|
66
|
+
]
|
67
|
+
else
|
68
|
+
# Run all
|
69
|
+
c[:browsers] = [
|
70
|
+
# ["Windows 7", "Chrome", "30"],
|
71
|
+
# ["Windows 8", "Firefox", "28"],
|
72
|
+
["Windows 8.1", "Internet Explorer", "11"],
|
73
|
+
["Windows 8.0", "Internet Explorer", "10"],
|
74
|
+
["Windows 7.0", "Internet Explorer", "9"],
|
75
|
+
# ["OSX 10.9", "iPhone", "8.1"],
|
76
|
+
# ["OSX 10.8", "Safari", "6"],
|
77
|
+
# ["Linux", "Chrome", "26"]
|
78
|
+
]
|
79
|
+
end
|
80
|
+
c[:start_local_application] = false
|
81
|
+
end
|
82
|
+
|
83
|
+
Capybara.default_driver = :sauce
|
84
|
+
Capybara.javascript_driver = :sauce
|
56
85
|
end
|
57
86
|
end
|
58
87
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Volt
|
2
|
+
|
3
|
+
# Login the user, return a promise for success
|
4
|
+
def self.login(username, password)
|
5
|
+
UserTasks.login(username, password).then do |result|
|
6
|
+
|
7
|
+
# Assign the user_id cookie for the user
|
8
|
+
$page.cookies._user_id = result
|
9
|
+
|
10
|
+
# Pass nil back
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.logout
|
16
|
+
$page.cookies.delete(:user_id)
|
17
|
+
end
|
18
|
+
end
|