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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 237e158164bd2f394e80998fa3314f52024fbebc
4
- data.tar.gz: abd1d018ffd679afba929518326f0139cce4deb0
3
+ metadata.gz: 848470b46efd1f1d8835f8501af6fc39d052cd4c
4
+ data.tar.gz: 6ff661da03f4f1e572c566b95d2b91868748148e
5
5
  SHA512:
6
- metadata.gz: 48b5fa1a075beab16bbe901532b11e46e6ee4aa065d21db169bc425efc9def807d2d286f5ea4a2f6b569458b05fcb7052721ceb3df26aa7d585990a1d4fe6ffb
7
- data.tar.gz: d18c325ef1f789d19a6ceaf2327452fd5b06dcefda74d0eeeca8e66b070599544ebf5176779d465a67bf641987f9af40de34a3eb7bbcffcd4b8d469910b17aea
6
+ metadata.gz: 11b26392d7fab2365755625fafd4708cc8398eecf3aad5c83757a3fbdb6f52baa1c9f38f483d23a52ca61ae98e5e42955028e382e87abf524495851d72684644
7
+ data.tar.gz: fe2f9c21b79e7da0b478b84aaffaa86da7cf63bf73b2f0b793723c73351de40971027c17044907822b43adbc88d3028ed525a0a7a5aca54fa8718068e998b915
data/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@
7
7
  - Page load performance was improved, and more of sprockets was used for component loading.
8
8
  - You can now return promises in permissions blocks. Also, can_read?, can_create?, and .can_delete?
9
9
  - Anything in /public is now served via Rack::Static in the default middleware stack. (So you can put user uploaded images in there)
10
+ - You can now use _ or - in volt tag names and attributes. (We're moving to using dash ( - ) as the standard in html)
11
+ - You can now trigger events on controllers rendered as tags. The events will bubble up through the DOM and can be caught by any e- bindings. See the docs for more information.
12
+ - Rewrote the precompile pipeline.
13
+ - Added image compression by default. (using image_optim)
14
+ - All volt CLI tasks now can run from inside of any directory in the volt app (or the root)
15
+
16
+ ### Changed
17
+ - fix issue with ```raw``` and promises (#275)
18
+ - fix issue with .length on store (#269)
10
19
 
11
20
  ## 0.9.4
12
21
  ### Lingo Change
@@ -34,6 +43,7 @@ the base collections will now be called "Repositories" or "Repo's" for short. T
34
43
  - fixed issue with local_store not persisting in some cases
35
44
  - runners now block until messages have propigated to the message bus and updates have been pushed.
36
45
  - upgraded some dependency gems to fix a conflict
46
+ - fixed bug with .last on ReactiveArray (#259)
37
47
 
38
48
  ## 0.9.3
39
49
  [0.9.3 Update Blog Post](http://blog.voltframework.com/post/121128931859/0-9-3-stuff-you-asked-for)
data/README.md CHANGED
@@ -35,11 +35,19 @@ There is also a [work in progress tutorial](https://github.com/rhgraysonii/volt_
35
35
 
36
36
  # More Videos
37
37
 
38
- Rick Carlino has been putting together some great volt tutorial videos also.
39
-
40
- - [Volt Tasks](http://datamelon.io/blog/2015/creating-volt-task-objects.html)
41
- - [Build a Realtime Chat App with Volt](http://datamelon.io/blog/2015/building-a-chat-app-in-volt.html)
42
- - [Understanding Volt Views](http://datamelon.io/blog/2015/understanding-views-in-volt-with-a-card-game.html)
38
+ Rick Carlino has been putting together some [great volt tutorial videos](http://datamelon.io/blog) also.
39
+
40
+ - [Volt URL Routing](http://datamelon.io/blog/2015/routes-and-multi-view-apps.html)
41
+ - [Volt Tasks](http://datamelon.io/blog/2015/creating-volt-task-objects.html)
42
+ - [Volt Views](http://datamelon.io/blog/2015/understanding-views-in-volt-with-a-card-game.html)
43
+ - [Volt Permissions](http://datamelon.io/blog/2015/twitter-clone-demonstrates-volt-permissions.html)
44
+ - [Volt Runners](http://datamelon.io/blog/2015/automation-of-everything-with-volt-runners.html)
45
+ - [Volt Components](http://datamelon.io/blog/2015/staying-productive-with-the-volt-component-ecosystem.html)
46
+ - [REST APIs in Volt](http://datamelon.io/blog/2015/building-rest-apis-with-volt.html)
47
+ - [Javascript Library Interop](http://datamelon.io/blog/2015/using-js-libraries-with-opal.html)
48
+ - [Credit Card Payments with Volt](http://datamelon.io/blog/2015/payment-form-using-volt-and-stripe.html)
49
+ - [Build a Realtime Chat App](http://datamelon.io/blog/2015/building-a-chat-app-in-volt.html)
50
+ - [6 Key Concepts for New Volt Learners](http://datamelon.io/blog/2015/6-concepts-for-volt-beginners.html)
43
51
 
44
52
  @ahnbizcad maintains a [playlist of Volt related videos](https://www.youtube.com/watch?v=McxtO8ybxy8&list=PLmQFeDKFCPXatHb-zEXwfeMH01DPiZjP7).
45
53
 
@@ -2,5 +2,5 @@ class ActiveVoltInstance < Volt::Model
2
2
  field :server_id, String
3
3
  field :ips, String
4
4
  field :port, Fixnum
5
- field :time, Time
5
+ field :time#, Time
6
6
  end
@@ -30,11 +30,14 @@ class LiveQuery
30
30
  end
31
31
 
32
32
  def notify_added(index, data, skip_channel)
33
- # puts "Added: #{index} - #{data.inspect}"
34
33
  # Make model for testing permissions against
35
- model = model_for_filter(data)
34
+ model = nil
36
35
 
37
36
  notify! do |channel|
37
+ # Only load the model for filtering if we are sending to a channel
38
+ # (skip if we are the only one listening)
39
+ model ||= model_for_filter(data)
40
+
38
41
  filtered_data = nil
39
42
  Volt.as_user(channel.user_id) do
40
43
  filtered_data = model.filtered_attributes.sync
@@ -52,9 +55,14 @@ class LiveQuery
52
55
  end
53
56
 
54
57
  def notify_changed(id, data, skip_channel)
55
- model = model_for_filter(data)
58
+ # puts "NOTIFY CHANGED"
59
+ model = nil
56
60
 
57
61
  notify!(skip_channel) do |channel|
62
+ # Only load the model for filtering if we are sending to a channel
63
+ # (skip if we are the only one listening)
64
+ model ||= model_for_filter(data)
65
+
58
66
  filtered_data = nil
59
67
  Volt.as_user(channel.user_id) do
60
68
  filtered_data = model.filtered_attributes.sync
@@ -9,31 +9,21 @@ class StoreTasks < Volt::Task
9
9
  model_name = collection.singularize.camelize
10
10
 
11
11
  # Fetch the model
12
- collection = store.send(:"_#{path[-2]}")
12
+ collection = store.get(path[-2])
13
13
 
14
14
  # See if the model has already been made
15
- collection.where(id: data[:id]).first.then do |model|
16
- # Otherwise assign to the collection
17
- model ||= collection
15
+ model_promise = collection.where(id: data[:id]).first
18
16
 
19
- # Create a buffer
20
- buffer = model.buffer
21
-
22
- # Assign the changed data to the buffer
23
- buffer.assign_attributes(data, false, true)
24
-
25
- buffer
26
- end
17
+ return collection, model_promise
27
18
  end
28
19
 
29
20
  def save(collection, path, data)
30
21
  data = data.symbolize_keys
31
- promise = nil
22
+ model_promise = nil
32
23
 
33
- # Don't check the permissions when we load the model, since we want all fields
34
24
  Volt.skip_permissions do
35
25
  Volt::Model.no_validate do
36
- promise = load_model(collection, path, data)
26
+ collection, model_promise = load_model(collection, path, data)
37
27
  end
38
28
  end
39
29
 
@@ -44,9 +34,16 @@ class StoreTasks < Volt::Task
44
34
  # who sent the update.
45
35
  #
46
36
  # return another promise
47
- promise.then do |model|
37
+ model_promise.then do |model|
48
38
  Thread.current['in_channel'] = @channel
49
- save_promise = model.save!.then do |result|
39
+
40
+ result = if model
41
+ model.update(data)
42
+ else
43
+ collection.create(data)
44
+ end
45
+
46
+ save_promise = result.then do |result|
50
47
  next nil
51
48
  end.fail do |err|
52
49
  # An error object, convert to hash
data/lib/volt/cli.rb CHANGED
@@ -131,5 +131,27 @@ end
131
131
  # Add in more features
132
132
  require 'volt/cli/asset_compile'
133
133
 
134
+ unless Gem.win_platform?
135
+ # Change CWD to the root of the volt project
136
+ pwd = Dir.pwd
137
+ changed = false
138
+ loop do
139
+ if File.exists?(pwd + '/Gemfile')
140
+ Dir.chdir(pwd) if changed
141
+ break
142
+ else
143
+ changed = true
144
+
145
+ # Move up a directory and try again
146
+ pwd = pwd.gsub(/\/[^\/]+$/, '')
147
+
148
+ if pwd == ''
149
+ puts "You are not currently in a volt project directory"
150
+ exit 1
151
+ end
152
+ end
153
+ end
154
+ end
155
+
134
156
  puts "Volt #{Volt::Version::STRING}"
135
157
  Volt::CLI.start(ARGV)
@@ -9,9 +9,11 @@ module Volt
9
9
  private
10
10
 
11
11
  def compile
12
- puts 'compiling project...'
12
+ say "Starting Precompile...", :red
13
13
  require 'fileutils'
14
14
  ENV['SERVER'] = 'true'
15
+ ENV['MAPS'] = 'false'
16
+ ENV['NO_FORKING'] = 'true'
15
17
 
16
18
  require 'opal'
17
19
  require 'rack'
@@ -19,90 +21,88 @@ module Volt
19
21
  require 'volt/volt/core'
20
22
  require 'volt/boot'
21
23
  require 'volt/server'
24
+ require 'volt/server/rack/component_paths'
25
+ require 'volt/server/rack/component_code'
22
26
 
23
27
  @root_path ||= Dir.pwd
24
28
  Volt.root = @root_path
25
29
 
26
- volt_app = Volt.boot(@root_path)
27
-
28
- require 'volt/server/rack/component_paths'
29
- require 'volt/server/rack/component_code'
30
+ @volt_app = Volt.boot(@root_path)
30
31
 
31
32
  @app_path = File.expand_path(File.join(@root_path, 'app'))
32
33
 
33
- @component_paths = ComponentPaths.new(@root_path)
34
- @app = Rack::Builder.new
35
- @opal_files = OpalFiles.new(@app, @app_path, @component_paths)
36
- @index_files = IndexFiles.new(@app, volt_app, @component_paths, @opal_files)
37
-
38
- puts 'Compile Opal for components'
39
- write_component_js
40
- puts 'Copy assets'
41
- write_sprockets
42
- puts 'Compile JS/CSS'
43
- write_js_and_css
44
- puts 'Write index files'
34
+ say 'Compiling RB, JS, CSS, and Images...', :red
35
+ write_files_and_manifest
36
+ compile_manifests
37
+ say 'Write index files...', :red
45
38
  write_index
46
-
47
- puts "compiled"
48
- end
49
-
50
- def logical_paths_and_full_paths
51
- env = @opal_files.environment
52
- env.each_file do |full_path|
53
- # logical_path = env[full_path].logical_path
54
- # logical_path = @opal_files.environment.send(:logical_path_for_filename, full_path, []).to_s
55
- # puts "FULL PATH: #{full_path.inspect} -- #{logical_path}"
56
-
57
- # yield(logical_path, full_path.to_s)
58
- end
59
-
39
+ say "Done", :green
60
40
  end
61
41
 
62
- def write_sprockets
63
- # Serve the opal files
64
- logical_paths_and_full_paths do |logical_path, full_path|
65
- # Only include files that aren't compiled elsewhere, like fonts
66
- if !logical_path[/[.](y|css|js|html|erb)$/] &&
67
- File.extname(logical_path) != '' &&
68
- # opal includes some node modules in the standard lib that we don't need to compile in
69
- (full_path !~ /\/opal/ && full_path !~ /\/stdlib\// && logical_path !~ /^node_js\//)
70
- write_sprocket_file(logical_path)
42
+ def write_files_and_manifest
43
+ asset_files = AssetFiles.from_cache('main', @volt_app.component_paths)
44
+ # Write a temp css file
45
+ js = asset_files.javascript(@volt_app)
46
+ css = asset_files.css
47
+ @tmp_files = []
48
+
49
+ File.open(Volt.root + '/app/main/app.js', 'wb') do |file|
50
+ js.each do |type, src_or_body|
51
+ if type == :src
52
+ src = src_or_body
53
+ url = src.gsub(/^\/assets\//, '')
54
+ file.write("//= require '#{url}'\n")
55
+ else
56
+ body = src_or_body
57
+
58
+ # Write to a tempfile, since sprockets can't mix requires and
59
+ # code.
60
+
61
+ require 'securerandom'
62
+ hex = SecureRandom.hex
63
+ tmp_path = Volt.root + "/app/main/__#{hex}.js"
64
+ url = "main/__#{hex}"
65
+ file.write("//= require '#{url}'\n")
66
+
67
+ @tmp_files << tmp_path
68
+ File.open(tmp_path, 'wb') {|f| f.write("#{body}\n") }
69
+ end
71
70
  end
72
71
  end
73
- end
74
72
 
75
- def write_js_and_css
76
- (@index_files.javascript_files + @index_files.css_files).each do |logical_path|
77
- if logical_path =~ /^\/assets\//
78
- logical_path = logical_path.gsub(/^\/assets\//, '')
79
- write_sprocket_file(logical_path)
73
+ File.open(Volt.root + '/app/main/app.scss', 'wb') do |file|
74
+ css.each do |link|
75
+ url = link.gsub(/^\/assets\//, '')
76
+ file.write("//= require '#{url}'\n")
80
77
  end
81
78
  end
82
79
  end
83
80
 
84
- def write_sprocket_file(logical_path)
85
- path = "#{@root_path}/public/assets/#{logical_path}"
86
-
87
- begin
88
- # Only write out the assets
89
- # if logical_path =~ /\/assets\//
90
- content = @opal_files.environment[logical_path].to_s
91
- write_file(path, content)
92
- # end
93
- rescue Sprockets::FileNotFound, SyntaxError => e
94
- # ignore
95
- end
96
- end
81
+ def compile_manifests
82
+ manifest = Sprockets::Manifest.new(@volt_app.sprockets, './public/assets/manifest.json')
97
83
 
98
- def write_component_js
99
- write_sprocket_file('components/main.js')
84
+ # Compile the files (and linked assets)
85
+ manifest.compile('main/app.js')
86
+ manifest.compile('main/app.css')
87
+
88
+ # Clear temp files
89
+ @tmp_files.each {|path| FileUtils.rm(path) }
90
+
91
+ # Remove the temp files
92
+ FileUtils.rm(Volt.root + '/app/main/app.js')
93
+ FileUtils.rm(Volt.root + '/app/main/app.scss')
100
94
  end
101
95
 
102
96
  def write_index
103
- path = "#{@root_path}/public/index.html"
97
+ require 'volt/cli/base_index_renderer'
98
+
99
+ output_path = "#{@root_path}/public/index.html"
100
+ require 'json'
101
+
102
+ @manifest = JSON.parse(File.read(@root_path + '/public/assets/manifest.json'))
103
+ output_html = BaseIndexRenderer.new(@manifest).html
104
104
 
105
- write_file(path, @index_files.html)
105
+ write_file(output_path, output_html)
106
106
  end
107
107
 
108
108
  def write_file(path, data)
@@ -0,0 +1,26 @@
1
+ # Render the config/base/index.html when precompiling. Here we only render
2
+ # one js and one css file.
3
+
4
+ module Volt
5
+ class BaseIndexRenderer
6
+ def initialize(manifest)
7
+ @manifest = manifest
8
+ end
9
+
10
+ def html
11
+ index_path = File.expand_path(File.join(Volt.root, 'config/base/index.html'))
12
+ html = File.read(index_path)
13
+
14
+ ERB.new(html, nil, '-').result(binding)
15
+ end
16
+
17
+ # When writing the index, we render the
18
+ def javascript_tags
19
+ "<script src=\"/assets/#{@manifest['assets']['main/app.js']}\"></script>"
20
+ end
21
+
22
+ def css_tags
23
+ "<link href=\"/assets/#{@manifest['assets']['main/app.css']}\" media=\"all\" rel=\"stylesheet\" type=\"text/css\" />"
24
+ end
25
+ end
26
+ end
@@ -81,7 +81,7 @@ class Generate < Thor
81
81
  method_option :name, type: :string, banner: 'The name of the task.'
82
82
  method_option :component, type: :string, default: 'main', banner: 'The component the task should be created in.', required: false
83
83
  def task(name, component = 'main')
84
- name = name.underscore.gsub(/_tasks$/, '').singularize + '_task'
84
+ name = name.underscore.gsub(/_tasks$/, '').singularize.gsub('_task', '') + '_task'
85
85
  output_file = Dir.pwd + "/app/#{component}/tasks/#{name}.rb"
86
86
  spec_file = Dir.pwd + "/spec/app/#{component}/tasks/#{name}_spec.rb"
87
87
  template('task/task.rb.tt', output_file, task_name: name.camelize.singularize)
data/lib/volt/config.rb CHANGED
@@ -60,6 +60,7 @@ else
60
60
 
61
61
  compress_javascript: Volt.env.production?,
62
62
  compress_css: Volt.env.production?,
63
+ compress_images: Volt.env.production?,
63
64
  abort_on_exception: true,
64
65
 
65
66
  min_worker_threads: 1,
@@ -27,16 +27,19 @@ module Volt
27
27
 
28
28
  # Container returns the node that is parent to all nodes in the section.
29
29
  def container
30
+ check_section!('container')
30
31
  section.container_node
31
32
  end
32
33
 
33
34
  def dom_nodes
35
+ check_section!('dom_nodes')
34
36
  section.range
35
37
  end
36
38
 
37
39
  # Walks the dom_nodes range until it finds an element. Typically this will
38
40
  # be the container element without the whitespace text nodes.
39
41
  def first_element
42
+ check_section!('first_element')
40
43
  range = dom_nodes
41
44
  nodes = `range.startContainer.childNodes`
42
45
 
@@ -76,6 +79,24 @@ module Volt
76
79
  end
77
80
  end
78
81
 
82
+ def trigger(event, *args)
83
+ # Trigger on the current controller if an e- was setup on the component.
84
+ component_event = attrs.send(:"e_#{event}")
85
+
86
+ if component_event
87
+ # Add a nil arg for the event, trim to arity
88
+ args2 = (args + [nil])[0...component_event.arity]
89
+ component_event.call(*args2)
90
+ end
91
+
92
+ args.unshift(self)
93
+ # Trigger via jquery, so it bubbles up through the DOM
94
+ `$(#{first_element}).trigger(#{event}, #{args});`
95
+
96
+ # return nil, so we return a ruby object
97
+ nil
98
+ end
99
+
79
100
  def self.model(val)
80
101
  self.default_model = val
81
102
  end
@@ -208,7 +229,14 @@ module Volt
208
229
  # Raw marks a string as html safe, so bindings can be rendered as html.
209
230
  # With great power comes great responsibility.
210
231
  def raw(str)
211
- str = str.to_s unless str.is_a?(String)
232
+ # Promises need to have .to_s called using .then, since .to_s is a promise
233
+ # method, so it won't be passed down to the value.
234
+ if str.is_a?(Promise)
235
+ str = str.then(&:to_s)
236
+ else
237
+ str = str.to_s unless str.is_a?(String)
238
+ end
239
+
212
240
  str.html_safe
213
241
  end
214
242
 
@@ -230,5 +258,13 @@ module Volt
230
258
  super
231
259
  end
232
260
  end
261
+
262
+ private
263
+ def check_section!(method_name)
264
+ unless section
265
+ raise "##{method_name} can't be called before the {action}_ready method is called"
266
+ end
267
+ end
268
+
233
269
  end
234
270
  end