volt 0.9.5.pre4 → 0.9.5.pre5

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +13 -5
  4. data/app/volt/assets/css/{notices.css.scss → notices.scss} +0 -0
  5. data/app/volt/models/active_volt_instance.rb +1 -1
  6. data/app/volt/tasks/live_query/live_query.rb +11 -3
  7. data/app/volt/tasks/store_tasks.rb +14 -17
  8. data/lib/volt/cli.rb +22 -0
  9. data/lib/volt/cli/asset_compile.rb +63 -63
  10. data/lib/volt/cli/base_index_renderer.rb +26 -0
  11. data/lib/volt/cli/generate.rb +1 -1
  12. data/lib/volt/config.rb +1 -0
  13. data/lib/volt/controllers/model_controller.rb +37 -1
  14. data/lib/volt/extra_core/array.rb +22 -0
  15. data/lib/volt/models/array_model.rb +7 -1
  16. data/lib/volt/models/errors.rb +1 -1
  17. data/lib/volt/models/field_helpers.rb +36 -21
  18. data/lib/volt/models/model.rb +16 -0
  19. data/lib/volt/models/validations/validations.rb +21 -6
  20. data/lib/volt/models/validators/type_validator.rb +35 -3
  21. data/lib/volt/page/bindings/content_binding.rb +1 -1
  22. data/lib/volt/page/bindings/event_binding.rb +40 -16
  23. data/lib/volt/page/document_events.rb +8 -6
  24. data/lib/volt/reactive/reactive_array.rb +18 -1
  25. data/lib/volt/server/forking_server.rb +7 -1
  26. data/lib/volt/server/html_parser/attribute_scope.rb +26 -0
  27. data/lib/volt/server/html_parser/component_view_scope.rb +30 -22
  28. data/lib/volt/server/middleware/default_middleware_stack.rb +6 -1
  29. data/lib/volt/server/rack/asset_files.rb +5 -3
  30. data/lib/volt/server/rack/opal_files.rb +35 -23
  31. data/lib/volt/server/rack/sprockets_helpers_setup.rb +71 -0
  32. data/lib/volt/server/template_handlers/view_processor.rb +1 -2
  33. data/lib/volt/utils/promise_extensions.rb +1 -1
  34. data/lib/volt/version.rb +1 -1
  35. data/lib/volt/volt/app.rb +0 -2
  36. data/lib/volt/volt/client_setup/browser.rb +11 -0
  37. data/spec/apps/kitchen_sink/Gemfile +37 -14
  38. data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
  39. data/spec/apps/kitchen_sink/app/main/controllers/events_controller.rb +26 -0
  40. data/spec/apps/kitchen_sink/app/main/views/events/index.html +30 -0
  41. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +3 -0
  42. data/spec/apps/kitchen_sink/app/main/views/main/yield.html +1 -6
  43. data/spec/apps/kitchen_sink/app/main/views/{yield-component → yield_component}/index.html +0 -0
  44. data/spec/extra_core/array_spec.rb +26 -0
  45. data/spec/integration/bindings_spec.rb +9 -0
  46. data/spec/integration/event_spec.rb +19 -0
  47. data/spec/models/array_model_spec.rb +13 -0
  48. data/spec/models/field_helpers_spec.rb +2 -2
  49. data/spec/models/validations_spec.rb +31 -0
  50. data/spec/models/validators/type_validator_spec.rb +47 -1
  51. data/spec/reactive/reactive_array_spec.rb +46 -0
  52. data/spec/server/forking_server_spec.rb +27 -0
  53. data/spec/server/html_parser/view_scope_spec.rb +44 -0
  54. data/spec/server/rack/asset_files_spec.rb +2 -2
  55. data/templates/project/Gemfile.tt +8 -0
  56. data/templates/project/config/app.rb.tt +2 -1
  57. data/volt.gemspec +1 -1
  58. 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
- # if this attribute has bindings
18
- if binding_count > 0
19
- if binding_count > 1
20
- # Multiple bindings
21
- elsif parts.size == 1 && binding_count == 1
22
- # A single binding
23
- getter = value[2...-2].strip
24
- data_hash << "#{name.inspect} => Proc.new { #{getter} }"
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
- setter = getter_to_setter(getter)
27
- data_hash << "#{(name + '=').inspect} => Proc.new { |val| #{setter} }"
31
+ data_hash << "#{name.inspect} => Proc.new { #{getter} }"
28
32
 
29
- # Add an _parent fetcher. Useful for things like volt-fields to get the parent model.
30
- parent = parent_fetcher(getter)
33
+ setter = getter_to_setter(getter)
34
+ data_hash << "#{(name + '=').inspect} => Proc.new { |val| #{setter} }"
31
35
 
32
- # TODO: This adds some overhead, perhaps there is a way to compute this dynamically on the
33
- # front-end.
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
- # Add a _last_method property. This is useful
37
- data_hash << "#{(name + '_last_method').inspect} => #{last_method_name(getter).inspect}"
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
- rack_app.use Rack::ShowExceptions
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
- # [[:url, '/somefile.js'], [:body, 'var inlinejs = true;']]
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
- env = @enviroment
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
- builder.map(maps_prefix) do
88
- require 'rack/conditionalget'
89
- require 'rack/etag'
90
- use Rack::ConditionalGet
91
- use Rack::ETag
92
- run maps_app
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
- # map server.source_maps.prefix do
99
- # run server.source_maps
100
- # end
101
-
102
- # if Volt.source_maps?
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
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Version
3
- STRING = '0.9.5.pre4'
3
+ STRING = '0.9.5.pre5'
4
4
  end
5
5
  end
data/lib/volt/volt/app.rb CHANGED
@@ -99,8 +99,6 @@ module Volt
99
99
  setup_postboot_middleware
100
100
 
101
101
  start_message_bus
102
-
103
- Volt::ViewProcessor.setup
104
102
  end
105
103
  end
106
104
 
@@ -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
- # The following gem's are optional for themeing
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
- gem 'volt-fields'
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
- # use mongo for data store while testing
17
- gem 'volt-mongo'
18
+ # Add ability to send e-mail from apps.
19
+ gem 'volt-mailer', '~> 0.1.0'
20
+
21
+ gem 'volt-fields'
18
22
 
19
- gem 'opal'
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
- gem 'concurrent-ruby-ext'
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