volt 0.9.2 → 0.9.3.pre1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +4 -0
- data/Gemfile +3 -0
- data/app/volt/assets/js/volt_js_polyfills.js +0 -1
- data/app/volt/assets/js/volt_watch.js +217 -0
- data/app/volt/models/user.rb +7 -2
- data/app/volt/tasks/query_tasks.rb +6 -0
- data/lib/volt/boot.rb +1 -1
- data/lib/volt/cli/generate.rb +1 -1
- data/lib/volt/config.rb +12 -2
- data/lib/volt/controllers/model_controller.rb +1 -1
- data/lib/volt/data_stores/base_adaptor_client.rb +34 -0
- data/lib/volt/data_stores/{base.rb → base_adaptor_server.rb} +1 -1
- data/lib/volt/data_stores/data_store.rb +23 -7
- data/lib/volt/models/array_model.rb +3 -2
- data/lib/volt/models/model.rb +29 -91
- data/lib/volt/models/{dirty.rb → model_helpers/dirty.rb} +0 -0
- data/lib/volt/models/{listener_tracker.rb → model_helpers/listener_tracker.rb} +0 -0
- data/lib/volt/models/model_helpers/model_change_helpers.rb +76 -0
- data/lib/volt/models/{model_helpers.rb → model_helpers/model_helpers.rb} +0 -0
- data/lib/volt/models/persistors/array_store.rb +2 -23
- data/lib/volt/models/persistors/query/normalizer.rb +0 -44
- data/{templates/project/lib/.empty_directory → lib/volt/models/validations/errors.rb} +0 -0
- data/lib/volt/models/{validations.rb → validations/validations.rb} +80 -26
- data/lib/volt/page/bindings/attribute_binding.rb +17 -3
- data/lib/volt/page/page.rb +1 -0
- data/lib/volt/reactive/eventable.rb +1 -0
- data/lib/volt/server.rb +2 -1
- data/lib/volt/server/component_templates.rb +66 -16
- data/lib/volt/server/forking_server.rb +16 -14
- data/lib/volt/server/html_parser/sandlebars_parser.rb +2 -0
- data/lib/volt/server/html_parser/view_scope.rb +2 -0
- data/lib/volt/server/rack/component_paths.rb +4 -2
- data/lib/volt/server/rack/opal_files.rb +4 -2
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server/template_handlers/handlers.rb +0 -0
- data/lib/volt/spec/setup.rb +23 -8
- data/lib/volt/tasks/dispatcher.rb +4 -0
- data/lib/volt/utils/promise_patch.rb +3 -0
- data/lib/volt/version.rb +1 -1
- data/spec/apps/kitchen_sink/Gemfile +5 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +10 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +20 -0
- data/spec/apps/kitchen_sink/app/main/views/main/form.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
- data/spec/integration/bindings_spec.rb +33 -0
- data/spec/integration/user_spec.rb +51 -21
- data/spec/models/associations_spec.rb +8 -0
- data/spec/models/model_spec.rb +7 -0
- data/spec/models/user_spec.rb +20 -0
- data/spec/models/validations_spec.rb +2 -1
- data/spec/models/validators/block_validations_spec.rb +53 -0
- data/spec/page/bindings/template_binding/view_lookup_for_path_spec.rb +10 -0
- data/spec/page/path_string_renderer_spec.rb +6 -0
- data/spec/reactive/eventable_spec.rb +24 -6
- data/spec/server/component_templates_spec.rb +21 -0
- data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -13
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +2 -2
- data/spec/server/rack/http_resource_spec.rb +10 -0
- data/spec/tasks/dispatcher_spec.rb +5 -0
- data/spec/tasks/user_tasks_spec.rb +59 -0
- data/spec/utils/task_argument_filtererer_spec.rb +6 -0
- data/templates/newgem/app/newgem/config/initializers/boot.rb +10 -0
- data/templates/newgem/lib/newgem.rb.tt +13 -0
- data/templates/project/Gemfile.tt +3 -0
- data/templates/project/app/main/lib/.empty_directory +0 -0
- data/volt.gemspec +3 -1
- metadata +24 -25
- data/lib/volt/data_stores/mongo_driver.rb +0 -69
@@ -26,14 +26,22 @@ module Volt
|
|
26
26
|
update(result)
|
27
27
|
end
|
28
28
|
|
29
|
+
@is_select = `#{element}.is('select')`
|
30
|
+
@is_hidden = `#{element}.is('[type=hidden]')`
|
29
31
|
@is_radio = `#{element}.is('[type=radio]')`
|
30
32
|
@selected_value = `#{element}.attr('value') || ''` if @is_radio
|
31
33
|
|
32
34
|
# Bind so when this value updates, we update
|
33
35
|
case @attribute_name
|
34
36
|
when 'value'
|
35
|
-
changed_event =
|
36
|
-
|
37
|
+
changed_event = Proc.new { changed }
|
38
|
+
if @is_select
|
39
|
+
`#{element}.on('change.attrbind', #{changed_event})`
|
40
|
+
elsif @is_hidden
|
41
|
+
`#{element}.watch('value', #{changed_event})`
|
42
|
+
else
|
43
|
+
`#{element}.on('input.attrbind', #{changed_event})`
|
44
|
+
end
|
37
45
|
when 'checked'
|
38
46
|
changed_event = proc { |event| changed(event) }
|
39
47
|
`#{element}.on('change.attrbind', #{changed_event})`
|
@@ -120,7 +128,13 @@ module Volt
|
|
120
128
|
# aren't responsible for it being there.
|
121
129
|
case @attribute_name
|
122
130
|
when 'value'
|
123
|
-
|
131
|
+
if @is_select
|
132
|
+
`#{element}.off('change.attrbind')`
|
133
|
+
elsif @is_hidden
|
134
|
+
`#{element}.unwatch('value')`
|
135
|
+
else
|
136
|
+
`#{element}.off('input.attrbind', #{nil})`
|
137
|
+
end
|
124
138
|
when 'checked'
|
125
139
|
`#{element}.off('change.attrbind', #{nil})`
|
126
140
|
end
|
data/lib/volt/page/page.rb
CHANGED
@@ -15,6 +15,7 @@ require 'volt/page/string_template_renderer'
|
|
15
15
|
require 'volt/page/document_events'
|
16
16
|
require 'volt/page/sub_context'
|
17
17
|
require 'volt/page/targets/dom_target'
|
18
|
+
require 'volt/data_stores/base_adaptor_client'
|
18
19
|
|
19
20
|
if RUBY_PLATFORM == 'opal'
|
20
21
|
require 'volt/page/channel'
|
data/lib/volt/server.rb
CHANGED
@@ -7,6 +7,7 @@ require 'sass'
|
|
7
7
|
require 'volt/utils/tilt_patch'
|
8
8
|
require 'sprockets-sass'
|
9
9
|
|
10
|
+
|
10
11
|
require 'volt'
|
11
12
|
require 'volt/tasks/dispatcher'
|
12
13
|
require 'volt/tasks/task_handler'
|
@@ -49,7 +50,7 @@ module Volt
|
|
49
50
|
attr_reader :listener, :app_path
|
50
51
|
|
51
52
|
# You can also optionally pass in a prebooted app
|
52
|
-
def initialize(root_path = nil, app =
|
53
|
+
def initialize(root_path = nil, app = nil)
|
53
54
|
@root_path = root_path || Dir.pwd
|
54
55
|
@volt_app = app
|
55
56
|
|
@@ -4,7 +4,47 @@ require 'volt/tasks/task_handler'
|
|
4
4
|
# Initialize with the path to a component and returns all the front-end
|
5
5
|
# setup code (for controllers, models, views, and routes)
|
6
6
|
module Volt
|
7
|
+
class BasicHandler
|
8
|
+
def call(file_contents)
|
9
|
+
file_contents
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
class ComponentTemplates
|
14
|
+
|
15
|
+
module Handlers #:nodoc:
|
16
|
+
# Setup default handler on extend
|
17
|
+
def self.extended(base)
|
18
|
+
base.register_template_handler :html, BasicHandler.new
|
19
|
+
base.register_template_handler :email, BasicHandler.new
|
20
|
+
end
|
21
|
+
|
22
|
+
@@template_handlers = {}
|
23
|
+
|
24
|
+
def self.extensions
|
25
|
+
@@template_handlers.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
# Register an object that knows how to handle template files with the given
|
29
|
+
# extensions. This can be used to implement new template types.
|
30
|
+
# The handler must respond to +:call+, which will be passed the template
|
31
|
+
# and should return the rendered template as a String.
|
32
|
+
def register_template_handler(extension, handler)
|
33
|
+
@@template_handlers[extension.to_sym] = handler
|
34
|
+
end
|
35
|
+
|
36
|
+
def registered_template_handler(extension)
|
37
|
+
extension && @@template_handlers[extension.to_sym]
|
38
|
+
end
|
39
|
+
|
40
|
+
def handler_for_extension(extension)
|
41
|
+
registered_template_handler(extension)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
extend ComponentTemplates::Handlers
|
46
|
+
|
47
|
+
|
8
48
|
# client is if we are generating for the client or backend
|
9
49
|
def initialize(component_path, component_name, client = true)
|
10
50
|
@component_path = component_path
|
@@ -16,7 +56,8 @@ module Volt
|
|
16
56
|
code = generate_routes_code + generate_view_code
|
17
57
|
if @client
|
18
58
|
# On the backend, we just need the views
|
19
|
-
code << generate_controller_code + generate_model_code +
|
59
|
+
code << generate_controller_code + generate_model_code +
|
60
|
+
generate_tasks_code + generate_initializers_code
|
20
61
|
end
|
21
62
|
|
22
63
|
code
|
@@ -31,39 +72,47 @@ module Volt
|
|
31
72
|
end
|
32
73
|
|
33
74
|
def generate_view_code
|
34
|
-
code
|
75
|
+
code = ''
|
35
76
|
views_path = "#{@component_path}/views/"
|
36
77
|
|
37
|
-
exts =
|
38
|
-
|
39
|
-
# Only load email templates on the server
|
40
|
-
exts << 'email' unless @client
|
78
|
+
exts = Handlers.extensions
|
41
79
|
|
42
80
|
# Load all templates in the folder
|
43
81
|
Dir["#{views_path}*/*.{#{exts.join(',')}}"].sort.each do |view_path|
|
82
|
+
# file extension
|
83
|
+
format = File.extname(view_path).downcase.delete('.').to_sym
|
84
|
+
|
44
85
|
# Get the path for the template, supports templates in folders
|
45
86
|
template_path = view_path[views_path.size..-1].gsub(/[.](#{exts.join('|')})$/, '')
|
46
87
|
template_path = "#{@component_name}/#{template_path}"
|
47
88
|
|
48
|
-
|
89
|
+
file_contents = File.read(view_path)
|
49
90
|
|
50
|
-
|
51
|
-
|
52
|
-
|
91
|
+
# Process template if we have a handler for this file type
|
92
|
+
if handler = ComponentTemplates.handler_for_extension(format)
|
93
|
+
file_contents = handler.call(file_contents)
|
53
94
|
|
54
|
-
|
55
|
-
|
56
|
-
|
95
|
+
all_templates = ViewParser.new(file_contents, template_path)
|
96
|
+
|
97
|
+
binding_initializers = []
|
98
|
+
all_templates.templates.each_pair do |name, template|
|
99
|
+
binding_code = []
|
100
|
+
|
101
|
+
if template['bindings']
|
102
|
+
template['bindings'].each_pair do |key, value|
|
103
|
+
binding_code << "#{key.inspect} => [#{value.join(', ')}]"
|
104
|
+
end
|
57
105
|
end
|
58
|
-
end
|
59
106
|
|
60
|
-
|
107
|
+
binding_code = "{#{binding_code.join(', ')}}"
|
61
108
|
|
62
|
-
|
109
|
+
code << "#{page_reference}.add_template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})\n"
|
110
|
+
end
|
63
111
|
end
|
64
112
|
end
|
65
113
|
|
66
114
|
code
|
115
|
+
|
67
116
|
end
|
68
117
|
|
69
118
|
def generate_controller_code
|
@@ -125,5 +174,6 @@ module Volt
|
|
125
174
|
def generate_initializers_code
|
126
175
|
"\nrequire_tree '#{@component_path}/config/initializers/'\n"
|
127
176
|
end
|
177
|
+
|
128
178
|
end
|
129
179
|
end
|
@@ -79,6 +79,22 @@ module Volt
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
|
83
|
+
def stop_child
|
84
|
+
# clear the drb object and kill the child process.
|
85
|
+
if @drb_object
|
86
|
+
begin
|
87
|
+
@drb_object = nil
|
88
|
+
DRb.stop_service
|
89
|
+
@reader.close
|
90
|
+
stop_change_listener
|
91
|
+
Process.kill(9, @child_id)
|
92
|
+
rescue => e
|
93
|
+
puts "Stop Child Error: #{e.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
82
98
|
# In the even the parent gets killed without at_exit running,
|
83
99
|
# we watch the pipe and close if the pipe gets closed.
|
84
100
|
def watch_for_parent_exit
|
@@ -129,20 +145,6 @@ module Volt
|
|
129
145
|
end
|
130
146
|
end
|
131
147
|
|
132
|
-
def stop_child
|
133
|
-
# clear the drb object and kill the child process.
|
134
|
-
if @drb_object
|
135
|
-
begin
|
136
|
-
@drb_object = nil
|
137
|
-
DRb.stop_service
|
138
|
-
@reader.close
|
139
|
-
stop_change_listener
|
140
|
-
Process.kill(9, @child_id)
|
141
|
-
rescue => e
|
142
|
-
puts "Stop Child Error: #{e.inspect}"
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
148
|
|
147
149
|
def reload(changed_files)
|
148
150
|
# only reload the server code if a non-view file was changed
|
@@ -16,7 +16,9 @@ module Volt
|
|
16
16
|
|
17
17
|
# Gem folders with volt in them
|
18
18
|
# TODO: we should probably qualify this a bit more
|
19
|
-
app_folders += Gem.loaded_specs.values
|
19
|
+
app_folders += Gem.loaded_specs.values
|
20
|
+
.select {|gem| gem.name =~ /volt/ }
|
21
|
+
.map {|gem| "#{gem.full_gem_path}/app" }
|
20
22
|
|
21
23
|
app_folders.uniq
|
22
24
|
end
|
@@ -60,7 +62,7 @@ module Volt
|
|
60
62
|
$LOAD_PATH.unshift(app_folder)
|
61
63
|
|
62
64
|
# Sort so we get consistent load order across platforms
|
63
|
-
Dir["#{app_folder}/*/{
|
65
|
+
Dir["#{app_folder}/*/{controllers,models,tasks}/*.rb"].each do |ruby_file|
|
64
66
|
path = ruby_file.gsub(/^#{app_folder}\//, '')[0..-4]
|
65
67
|
require(path)
|
66
68
|
end
|
@@ -23,9 +23,11 @@ module Volt
|
|
23
23
|
Opal.append_path(Volt.root + '/lib')
|
24
24
|
|
25
25
|
Gem.loaded_specs.values.each do |gem|
|
26
|
-
|
26
|
+
['app', 'lib'].each do |folder|
|
27
|
+
path = gem.full_gem_path + "/#{folder}"
|
27
28
|
|
28
|
-
|
29
|
+
Opal.append_path(path) if Dir.exist?(path)
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
31
33
|
# Don't run arity checks in production
|
@@ -53,7 +53,11 @@ module Volt
|
|
53
53
|
# Remove ourself from the available channels
|
54
54
|
@@channels.delete(self)
|
55
55
|
|
56
|
-
|
56
|
+
begin
|
57
|
+
@@dispatcher.close_channel(self)
|
58
|
+
rescue DRb::DRbConnError => e
|
59
|
+
# ignore drb read of @@dispatcher error if child has closed
|
60
|
+
end
|
57
61
|
else
|
58
62
|
Volt.logger.error("Socket Error: Connection already closed\n#{inspect}")
|
59
63
|
end
|
File without changes
|
data/lib/volt/spec/setup.rb
CHANGED
@@ -29,6 +29,21 @@ module Volt
|
|
29
29
|
RSpec.configuration.filter_run_excluding type: :feature
|
30
30
|
end
|
31
31
|
|
32
|
+
|
33
|
+
|
34
|
+
cleanup_db = -> do
|
35
|
+
Volt::DataStore.fetch.drop_database
|
36
|
+
|
37
|
+
# Clear cached for a reset
|
38
|
+
$page.instance_variable_set('@store', nil)
|
39
|
+
QueryTasks.reset!
|
40
|
+
end
|
41
|
+
|
42
|
+
if RUBY_PLATFORM != 'opal'
|
43
|
+
# Call once during setup to clear if we killed the last run
|
44
|
+
cleanup_db.call
|
45
|
+
end
|
46
|
+
|
32
47
|
# Setup the spec collection accessors
|
33
48
|
# RSpec.shared_context "volt collections", {} do
|
34
49
|
RSpec.shared_examples_for 'volt collections', {} do
|
@@ -42,23 +57,23 @@ module Volt
|
|
42
57
|
$page.store
|
43
58
|
end
|
44
59
|
|
45
|
-
def cleanup_after
|
46
|
-
Volt::DataStore.fetch.drop_database
|
47
|
-
|
48
|
-
$page.instance_variable_set('@store', nil)
|
49
|
-
end
|
50
60
|
|
51
61
|
if RUBY_PLATFORM != 'opal'
|
52
62
|
after do
|
53
63
|
if @__store_accessed
|
54
64
|
# Clear the database after each spec where we use store
|
55
|
-
|
65
|
+
cleanup_db.call
|
56
66
|
end
|
57
67
|
end
|
58
68
|
|
69
|
+
# Assume store is accessed in capyabara specs
|
70
|
+
before(:context, {type: :feature}) do
|
71
|
+
@__store_accessed = true
|
72
|
+
end
|
73
|
+
|
59
74
|
# Cleanup after integration tests also.
|
60
|
-
|
61
|
-
|
75
|
+
before(:example, {type: :feature}) do
|
76
|
+
@__store_accessed = true
|
62
77
|
end
|
63
78
|
end
|
64
79
|
end
|
data/lib/volt/version.rb
CHANGED
@@ -13,8 +13,13 @@ gem 'volt-bootstrap_jumbotron_theme'
|
|
13
13
|
gem 'volt-fields'
|
14
14
|
gem 'volt-user_templates'
|
15
15
|
|
16
|
+
# use mongo for data store while testing
|
17
|
+
gem 'volt-mongo'
|
18
|
+
|
16
19
|
gem 'opal'
|
17
20
|
|
21
|
+
gem 'concurrent-ruby-ext'
|
22
|
+
|
18
23
|
# Server for MRI
|
19
24
|
platform :mri do
|
20
25
|
gem 'thin', '~> 1.6.0'
|
@@ -10,6 +10,12 @@ module Main
|
|
10
10
|
a[{}] = 5
|
11
11
|
end
|
12
12
|
|
13
|
+
def form_ready
|
14
|
+
`$('#title').html('form_ready')`
|
15
|
+
`$('select#location').val('AL').change()` # have to trigger manually as this is not user initiaized action
|
16
|
+
`$('input#name').val('Test')`
|
17
|
+
end
|
18
|
+
|
13
19
|
def flash_notice
|
14
20
|
flash._notices << 'A notice message'
|
15
21
|
end
|
@@ -44,6 +50,10 @@ module Main
|
|
44
50
|
'<button id="examplebutton">Example Button</button>'
|
45
51
|
end
|
46
52
|
|
53
|
+
def set_show(value)
|
54
|
+
page._show = value
|
55
|
+
end
|
56
|
+
|
47
57
|
private
|
48
58
|
|
49
59
|
# the main template contains a #template binding that shows another
|