volt 0.9.5.pre4 → 0.9.5.pre5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +13 -5
- data/app/volt/assets/css/{notices.css.scss → notices.scss} +0 -0
- data/app/volt/models/active_volt_instance.rb +1 -1
- data/app/volt/tasks/live_query/live_query.rb +11 -3
- data/app/volt/tasks/store_tasks.rb +14 -17
- data/lib/volt/cli.rb +22 -0
- data/lib/volt/cli/asset_compile.rb +63 -63
- data/lib/volt/cli/base_index_renderer.rb +26 -0
- data/lib/volt/cli/generate.rb +1 -1
- data/lib/volt/config.rb +1 -0
- data/lib/volt/controllers/model_controller.rb +37 -1
- data/lib/volt/extra_core/array.rb +22 -0
- data/lib/volt/models/array_model.rb +7 -1
- data/lib/volt/models/errors.rb +1 -1
- data/lib/volt/models/field_helpers.rb +36 -21
- data/lib/volt/models/model.rb +16 -0
- data/lib/volt/models/validations/validations.rb +21 -6
- data/lib/volt/models/validators/type_validator.rb +35 -3
- data/lib/volt/page/bindings/content_binding.rb +1 -1
- data/lib/volt/page/bindings/event_binding.rb +40 -16
- data/lib/volt/page/document_events.rb +8 -6
- data/lib/volt/reactive/reactive_array.rb +18 -1
- data/lib/volt/server/forking_server.rb +7 -1
- data/lib/volt/server/html_parser/attribute_scope.rb +26 -0
- data/lib/volt/server/html_parser/component_view_scope.rb +30 -22
- data/lib/volt/server/middleware/default_middleware_stack.rb +6 -1
- data/lib/volt/server/rack/asset_files.rb +5 -3
- data/lib/volt/server/rack/opal_files.rb +35 -23
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +71 -0
- data/lib/volt/server/template_handlers/view_processor.rb +1 -2
- data/lib/volt/utils/promise_extensions.rb +1 -1
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +0 -2
- data/lib/volt/volt/client_setup/browser.rb +11 -0
- data/spec/apps/kitchen_sink/Gemfile +37 -14
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
- data/spec/apps/kitchen_sink/app/main/controllers/events_controller.rb +26 -0
- data/spec/apps/kitchen_sink/app/main/views/events/index.html +30 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +3 -0
- data/spec/apps/kitchen_sink/app/main/views/main/yield.html +1 -6
- data/spec/apps/kitchen_sink/app/main/views/{yield-component → yield_component}/index.html +0 -0
- data/spec/extra_core/array_spec.rb +26 -0
- data/spec/integration/bindings_spec.rb +9 -0
- data/spec/integration/event_spec.rb +19 -0
- data/spec/models/array_model_spec.rb +13 -0
- data/spec/models/field_helpers_spec.rb +2 -2
- data/spec/models/validations_spec.rb +31 -0
- data/spec/models/validators/type_validator_spec.rb +47 -1
- data/spec/reactive/reactive_array_spec.rb +46 -0
- data/spec/server/forking_server_spec.rb +27 -0
- data/spec/server/html_parser/view_scope_spec.rb +44 -0
- data/spec/server/rack/asset_files_spec.rb +2 -2
- data/templates/project/Gemfile.tt +8 -0
- data/templates/project/config/app.rb.tt +2 -1
- data/volt.gemspec +1 -1
- metadata +31 -5
@@ -1,6 +1,30 @@
|
|
1
1
|
module Volt
|
2
2
|
# Included into ViewScope to provide processing for attributes
|
3
3
|
module AttributeScope
|
4
|
+
module ClassMethods
|
5
|
+
def methodize_string(str)
|
6
|
+
# Convert the string passed in to the binding so it returns a ruby Method
|
7
|
+
# instance
|
8
|
+
parts = str.split('.')
|
9
|
+
|
10
|
+
end_call = parts.last.strip
|
11
|
+
|
12
|
+
# If no method(args) is passed, we assume they want to convert the method
|
13
|
+
# to a Method, to be called with *args (from any trigger's), then event.
|
14
|
+
if str !~ /[\[\]\$\@\=]/ && end_call =~ /[_a-z0-9!?]+$/
|
15
|
+
parts[-1] = "method(:#{end_call})"
|
16
|
+
|
17
|
+
str = parts.join('.')
|
18
|
+
end
|
19
|
+
|
20
|
+
str
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(base)
|
25
|
+
base.send :extend, ClassMethods
|
26
|
+
end
|
27
|
+
|
4
28
|
# Take the attributes and create any bindings
|
5
29
|
def process_attributes(tag_name, attributes)
|
6
30
|
new_attributes = attributes.dup
|
@@ -29,6 +53,8 @@ module Volt
|
|
29
53
|
# Remove the e- attribute
|
30
54
|
attributes.delete(name)
|
31
55
|
|
56
|
+
value = self.class.methodize_string(value)
|
57
|
+
|
32
58
|
save_binding(id, "lambda { |__p, __t, __c, __id| Volt::EventBinding.new(__p, __t, __c, __id, #{event.inspect}, Proc.new {|event| #{value} })}")
|
33
59
|
end
|
34
60
|
|
@@ -7,38 +7,46 @@ module Volt
|
|
7
7
|
|
8
8
|
@binding_in_path = path
|
9
9
|
|
10
|
-
component_name = tag_name[1..-1].tr(':', '/')
|
10
|
+
component_name = tag_name[1..-1].tr('-', '_').tr(':', '/')
|
11
11
|
|
12
12
|
data_hash = []
|
13
13
|
attributes.each_pair do |name, value|
|
14
14
|
name = name.tr('-', '_')
|
15
|
-
parts, binding_count = binding_parts_and_count(value)
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
if name[0..1] == 'e_'
|
17
|
+
# Event binding
|
18
|
+
value = ViewScope.methodize_string(value.strip)
|
19
|
+
data_hash << "#{name.inspect} => Proc.new {|event| #{value} }"
|
20
|
+
else
|
21
|
+
parts, binding_count = binding_parts_and_count(value)
|
22
|
+
|
23
|
+
# if this attribute has bindings
|
24
|
+
if binding_count > 0
|
25
|
+
if binding_count > 1
|
26
|
+
# Multiple bindings
|
27
|
+
elsif parts.size == 1 && binding_count == 1
|
28
|
+
# A single binding
|
29
|
+
getter = value[2...-2].strip
|
25
30
|
|
26
|
-
|
27
|
-
data_hash << "#{(name + '=').inspect} => Proc.new { |val| #{setter} }"
|
31
|
+
data_hash << "#{name.inspect} => Proc.new { #{getter} }"
|
28
32
|
|
29
|
-
|
30
|
-
|
33
|
+
setter = getter_to_setter(getter)
|
34
|
+
data_hash << "#{(name + '=').inspect} => Proc.new { |val| #{setter} }"
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
data_hash << "#{(name + '_parent').inspect} => Proc.new { #{parent} }"
|
36
|
+
# Add an _parent fetcher. Useful for things like volt-fields to get the parent model.
|
37
|
+
parent = parent_fetcher(getter)
|
35
38
|
|
36
|
-
|
37
|
-
|
39
|
+
# TODO: This adds some overhead, perhaps there is a way to compute this dynamically on the
|
40
|
+
# front-end.
|
41
|
+
data_hash << "#{(name + '_parent').inspect} => Proc.new { #{parent} }"
|
42
|
+
|
43
|
+
# Add a _last_method property. This is useful
|
44
|
+
data_hash << "#{(name + '_last_method').inspect} => #{last_method_name(getter).inspect}"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# String
|
48
|
+
data_hash << "#{name.inspect} => #{value.inspect}"
|
38
49
|
end
|
39
|
-
else
|
40
|
-
# String
|
41
|
-
data_hash << "#{name.inspect} => #{value.inspect}"
|
42
50
|
end
|
43
51
|
end
|
44
52
|
|
@@ -6,6 +6,7 @@ require 'volt/server/rack/quiet_common_logger'
|
|
6
6
|
require 'volt/server/rack/opal_files'
|
7
7
|
require 'volt/server/rack/index_files'
|
8
8
|
require 'volt/server/rack/http_resource'
|
9
|
+
require 'volt/server/rack/sprockets_helpers_setup'
|
9
10
|
|
10
11
|
|
11
12
|
|
@@ -33,7 +34,9 @@ module Volt
|
|
33
34
|
}
|
34
35
|
|
35
36
|
rack_app.use QuietCommonLogger
|
36
|
-
|
37
|
+
if Volt.env.development? || Volt.env.test?
|
38
|
+
rack_app.use Rack::ShowExceptions
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
# Setup the middleware that we need to wait for components to boot before we
|
@@ -44,6 +47,8 @@ module Volt
|
|
44
47
|
volt_app.opal_files = opal_files
|
45
48
|
volt_app.sprockets = opal_files.environment
|
46
49
|
|
50
|
+
Volt::SprocketsHelpersSetup.new(volt_app.sprockets)
|
51
|
+
|
47
52
|
# Serve the main html files from public, also figure out
|
48
53
|
# which JS/CSS files to serve.
|
49
54
|
rack_app.use IndexFiles, volt_app, volt_app.component_paths, opal_files
|
@@ -172,7 +172,9 @@ module Volt
|
|
172
172
|
# aren't imported by default:
|
173
173
|
# http://sass-lang.com/guide
|
174
174
|
css_files += Dir["#{path}/**/[^_]*.{css,scss}"].sort.map do |folder|
|
175
|
-
'/assets' + folder[path.size..-1].gsub(/[.]scss$/, '')
|
175
|
+
css_path = '/assets' + folder[path.size..-1].gsub(/[.]scss$/, '')
|
176
|
+
css_path += '.css' unless css_path =~ /[.]css$/
|
177
|
+
css_path
|
176
178
|
end
|
177
179
|
when :css_file
|
178
180
|
css_files << path
|
@@ -185,10 +187,10 @@ module Volt
|
|
185
187
|
# #javascript is only used on the server
|
186
188
|
unless RUBY_PLATFORM == 'opal'
|
187
189
|
# Parses the javascript tags to reutrn the following:
|
188
|
-
# [[:
|
190
|
+
# [[:src, '/somefile.js'], [:body, 'var inlinejs = true;']]
|
189
191
|
def javascript(volt_app)
|
190
192
|
javascript_tags(volt_app)
|
191
|
-
.scan(/[<]script([^>]*)[>](.*?)[<]\/script[^>]*[>]/)
|
193
|
+
.scan(/[<]script([^>]*)[>](.*?)[<]\/script[^>]*[>]/m)
|
192
194
|
.map do |attrs, body|
|
193
195
|
src = attrs.match(/[\s|$]src\s*[=]\s*["']([^"']+?)["']/)
|
194
196
|
|
@@ -41,10 +41,6 @@ module Volt
|
|
41
41
|
# @environment = Opal::Environment.new
|
42
42
|
@environment = @server.sprockets
|
43
43
|
|
44
|
-
# Since the scope changes in builder blocks, we need to capture
|
45
|
-
# environment in closure
|
46
|
-
environment = @environment
|
47
|
-
|
48
44
|
environment.cache = Sprockets::Cache::FileStore.new('./tmp')
|
49
45
|
|
50
46
|
# Compress in production
|
@@ -60,6 +56,10 @@ module Volt
|
|
60
56
|
Csso.install(environment)
|
61
57
|
end
|
62
58
|
|
59
|
+
if Volt.config.compress_images
|
60
|
+
add_image_compression
|
61
|
+
end
|
62
|
+
|
63
63
|
@server.append_path(app_path)
|
64
64
|
|
65
65
|
volt_gem_lib_path = File.expand_path(File.join(File.dirname(__FILE__), '../../..'))
|
@@ -67,7 +67,17 @@ module Volt
|
|
67
67
|
|
68
68
|
add_asset_folders(@server)
|
69
69
|
|
70
|
-
|
70
|
+
|
71
|
+
# Setup ViewProcessor to parse views
|
72
|
+
Volt::ViewProcessor.setup(@environment)
|
73
|
+
|
74
|
+
# Use the cached env in production so it doesn't have to stat the FS
|
75
|
+
@environment = @environment.cached if Volt.env.production?
|
76
|
+
|
77
|
+
# Since the scope changes in builder blocks, we need to capture
|
78
|
+
# environment in closure
|
79
|
+
environment = @environment
|
80
|
+
|
71
81
|
builder.map '/assets' do
|
72
82
|
run environment
|
73
83
|
end
|
@@ -84,28 +94,30 @@ module Volt
|
|
84
94
|
end
|
85
95
|
|
86
96
|
if source_map_enabled
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
97
|
+
builder.map(maps_prefix) do
|
98
|
+
require 'rack/conditionalget'
|
99
|
+
require 'rack/etag'
|
100
|
+
use Rack::ConditionalGet
|
101
|
+
use Rack::ETag
|
102
|
+
run maps_app
|
94
103
|
end
|
104
|
+
end
|
105
|
+
end
|
95
106
|
|
107
|
+
def add_image_compression
|
108
|
+
if defined?(ImageOptim)
|
109
|
+
env = @environment
|
110
|
+
image_optim = ImageOptim.new({:pngout => false, :svgo => false})
|
96
111
|
|
112
|
+
processor = proc do |_context, data|
|
113
|
+
image_optim.optimize_image_data(data) || data
|
114
|
+
end
|
97
115
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
# source_maps = SourceMapServer.new(environment)
|
104
|
-
#
|
105
|
-
# builder.map(source_maps.prefix) do
|
106
|
-
# run source_maps
|
107
|
-
# end
|
108
|
-
# end
|
116
|
+
env.register_preprocessor 'image/gif', :image_optim, &processor
|
117
|
+
env.register_preprocessor 'image/jpeg', :image_optim, &processor
|
118
|
+
env.register_preprocessor 'image/png', :image_optim, &processor
|
119
|
+
env.register_preprocessor 'image/svg+xml', :image_optim, &processor
|
120
|
+
end
|
109
121
|
end
|
110
122
|
|
111
123
|
def add_asset_folders(environment)
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'sprockets-helpers'
|
2
|
+
|
3
|
+
module Volt
|
4
|
+
class SprocketsHelpersSetup
|
5
|
+
def initialize(env)
|
6
|
+
@env = env
|
7
|
+
|
8
|
+
setup_path_helpers
|
9
|
+
add_linking_in_asset_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup_path_helpers
|
13
|
+
digest = Volt.env.production?
|
14
|
+
|
15
|
+
# Configure Sprockets::Helpers (if necessary)
|
16
|
+
Sprockets::Helpers.configure do |config|
|
17
|
+
config.environment = @env
|
18
|
+
config.prefix = '/assets'
|
19
|
+
config.public_path = 'public'
|
20
|
+
config.debug = false#!Volt.env.production?
|
21
|
+
|
22
|
+
# Force to debug mode in development mode
|
23
|
+
# Debug mode automatically sets
|
24
|
+
# expand = true, digest = false, manifest = false
|
25
|
+
|
26
|
+
config.digest = digest
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
Sprockets::Helpers.digest = digest
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_linking_in_asset_path
|
35
|
+
@env.context_class.class_eval do
|
36
|
+
# We "freedom-patch" sprockets-helpers asset_path method to
|
37
|
+
# automatically link assets.
|
38
|
+
def asset_path(source, options = {})
|
39
|
+
uri = URI.parse(source)
|
40
|
+
return source if uri.absolute?
|
41
|
+
|
42
|
+
options[:prefix] = Sprockets::Helpers.prefix unless options[:prefix]
|
43
|
+
|
44
|
+
if Sprockets::Helpers.debug || options[:debug]
|
45
|
+
options[:manifest] = false
|
46
|
+
options[:digest] = false
|
47
|
+
options[:asset_host] = false
|
48
|
+
end
|
49
|
+
|
50
|
+
source_ext = File.extname(source)
|
51
|
+
|
52
|
+
if options[:ext] && source_ext != ".#{options[:ext]}"
|
53
|
+
uri.path << ".#{options[:ext]}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Link all assets out of the box
|
57
|
+
# Added by volt
|
58
|
+
link_asset(uri)
|
59
|
+
|
60
|
+
path = find_asset_path(uri, source, options)
|
61
|
+
if options[:expand] && path.respond_to?(:to_a)
|
62
|
+
path.to_a
|
63
|
+
else
|
64
|
+
path.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -67,8 +67,7 @@ module Volt
|
|
67
67
|
Opal.compile(code)
|
68
68
|
end
|
69
69
|
|
70
|
-
def self.setup
|
71
|
-
sprockets = $volt_app.sprockets
|
70
|
+
def self.setup(sprockets=$volt_app.sprockets)
|
72
71
|
sprockets.register_mime_type 'application/vtemplate', extensions: ['.html', '.email']
|
73
72
|
sprockets.register_transformer 'application/vtemplate', 'application/javascript', Volt::ViewProcessor.new(true)
|
74
73
|
end
|
@@ -64,7 +64,7 @@ class Promise
|
|
64
64
|
|
65
65
|
# When testing with rspec, add in a custom exception! method that doesn't
|
66
66
|
# swallow ExpectationNotMetError's.
|
67
|
-
if defined?(RSpec::Expectations::ExpectationNotMetError)
|
67
|
+
if defined?(RSpec) && defined?(RSpec::Expectations::ExpectationNotMetError)
|
68
68
|
def exception!(error)
|
69
69
|
if error.is_a?(RSpec::Expectations::ExpectationNotMetError)
|
70
70
|
raise error
|
data/lib/volt/version.rb
CHANGED
data/lib/volt/volt/app.rb
CHANGED
@@ -39,6 +39,17 @@ module Volt
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def link_clicked(url = '', event = nil)
|
42
|
+
target = nil
|
43
|
+
`target = $(event.target).attr('target');`
|
44
|
+
`if (!target) {`
|
45
|
+
`target = #{nil};`
|
46
|
+
`}`
|
47
|
+
|
48
|
+
if target.present? && target != '_self'
|
49
|
+
# Don't handle if they are opening in a new window
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
42
53
|
# Skip when href == ''
|
43
54
|
return false if url.blank?
|
44
55
|
|
@@ -2,31 +2,54 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gem 'volt', path: '../../../'
|
4
4
|
|
5
|
-
#
|
5
|
+
# volt uses mongo as the default data store.
|
6
|
+
gem 'volt-mongo'
|
6
7
|
|
8
|
+
# The following gem's are optional for themeing
|
7
9
|
# Twitter bootstrap
|
8
|
-
gem 'volt-bootstrap'
|
10
|
+
gem 'volt-bootstrap', '~> 0.0.10'
|
9
11
|
|
10
12
|
# Simple theme for bootstrap, remove to theme yourself.
|
11
|
-
gem 'volt-bootstrap_jumbotron_theme'
|
13
|
+
gem 'volt-bootstrap_jumbotron_theme', '~> 0.1.0'
|
12
14
|
|
13
|
-
|
14
|
-
gem 'volt-user_templates'
|
15
|
+
# User templates for login, signup, and logout menu.
|
16
|
+
gem 'volt-user_templates', '~> 0.4.0'
|
15
17
|
|
16
|
-
#
|
17
|
-
gem 'volt-
|
18
|
+
# Add ability to send e-mail from apps.
|
19
|
+
gem 'volt-mailer', '~> 0.1.0'
|
20
|
+
|
21
|
+
gem 'volt-fields'
|
18
22
|
|
19
|
-
|
23
|
+
# Use rbnacl for message bus encrpytion
|
24
|
+
# (optional, if you don't need encryption, disable in app.rb and remove)
|
25
|
+
#
|
26
|
+
# Message Bus encryption is not supported on Windows at the moment.
|
27
|
+
platform :ruby, :jruby do
|
28
|
+
gem 'rbnacl', require: false
|
29
|
+
gem 'rbnacl-libsodium', require: false
|
30
|
+
end
|
20
31
|
|
21
|
-
|
32
|
+
# Asset compilation gems, they will be required when needed.
|
33
|
+
gem 'csso-rails', '~> 0.3.4', require: false
|
34
|
+
gem 'uglifier', '>= 2.4.0', require: false
|
35
|
+
|
36
|
+
group :test do
|
37
|
+
# Testing dependencies
|
38
|
+
gem 'rspec', '~> 3.2.0'
|
39
|
+
gem 'opal-rspec', '~> 0.4.2'
|
40
|
+
gem 'capybara', '~> 2.4.2'
|
41
|
+
gem 'selenium-webdriver', '~> 2.43.0'
|
42
|
+
gem 'chromedriver2-helper', '~> 0.0.8'
|
43
|
+
gem 'poltergeist', '~> 1.5.0'
|
44
|
+
end
|
22
45
|
|
23
46
|
# Server for MRI
|
24
47
|
platform :mri do
|
48
|
+
# The implementation of ReadWriteLock in Volt uses concurrent ruby and ext helps performance.
|
49
|
+
gem 'concurrent-ruby-ext', '~> 0.8.0'
|
50
|
+
|
51
|
+
# Thin is the default volt server, you Puma is also supported
|
25
52
|
gem 'thin', '~> 1.6.0'
|
53
|
+
# gem 'puma'
|
26
54
|
gem 'bson_ext', '~> 1.9.0'
|
27
55
|
end
|
28
|
-
|
29
|
-
# Server for jruby
|
30
|
-
platform :jruby do
|
31
|
-
gem 'jubilee'
|
32
|
-
end
|