volt 0.7.14 → 0.7.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fc433aea390d72ea67bc96a667a68184162990c
4
- data.tar.gz: d75d3644a9633f81c9ccb20da751586f67463c73
3
+ metadata.gz: 7e05bc81468f67f439f7f8a045a68a0f9881ba28
4
+ data.tar.gz: db637839b429dee614b69e7acb573a56c47e39fc
5
5
  SHA512:
6
- metadata.gz: 6a9a01e11d069071794c7d279a49bbbafb54b16a9a9f7a4f4d1210291914390c26383908884428d79ff5fa139608d1df270d8e83a742d15ec10e5e0de3db4487
7
- data.tar.gz: a72b58ada2bda7c71d5be6ba48328b8c7390052f6a4ba479c948f1c1923d07bf7f7cea60736f97b7e4964652108187b19de9fd065b3db6fe96149ecdbf85b0dd
6
+ metadata.gz: e5419069da6a0ca76db6ae50d11f905f2b87638d341800a2922014fbdedc75de47faced77e92c8594c2d941c46ce4432c3676703074cc383db9a58b7be7a63aa
7
+ data.tar.gz: 1bcf04fe2b4c70e8e36a73286fda0fb8810ccb302d1f221f7459f23a66b4ab90307ff0a2f222dc6dfbb4528418d081e2891860701dde4a1afc1ebc3c42e941dd
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
  # gem 'rspec', '3.0.0.beta1'
6
6
  gem 'opal', git: 'https://github.com/opal/opal.git'
7
7
  gem 'opal-jquery', :git => 'https://github.com/opal/opal-jquery.git'
8
- gem 'sockjs', git: 'https://github.com/kacperk/sockjs-ruby.git', require: false, platforms: :mri
8
+ gem 'sockjs', git: 'https://github.com/voltrb/sockjs-ruby.git', require: false, platforms: :mri
9
9
 
10
10
  # gem 'thor'
11
11
 
data/Readme.md CHANGED
@@ -658,13 +658,13 @@ or
658
658
  To find the control's views and optional controller, Volt will search the following (in order):
659
659
 
660
660
 
661
- | Component | View Folder | View File | Section |
662
- |-------------|----------------|--------------|-----------|
663
- | | | | :{name} |
664
- | | | {name}.html | :body |
665
- | | {name} | index.html | :body |
666
- | {name} | index | index.html | :body |
667
- | gems/{name} | index | index.html | :body |
661
+ | Section | View File | View Folder | Component |
662
+ |-----------|--------------|----------------|-------------|
663
+ | :{name} | | | |
664
+ | :body | {name}.html | | |
665
+ | :body | index.html | {name} | |
666
+ | :body | index.html | index | {name} |
667
+ | :body | index.html | index | gems/{name} |
668
668
 
669
669
  **Note that anything with a view folder will also load a controller if the name/folder matches.**
670
670
 
@@ -694,12 +694,12 @@ When you create a control, you can also specify multiple parts of the search pat
694
694
 
695
695
  The above would search the following:
696
696
 
697
- | Component | View Folder | View File | Section |
698
- |-------------|----------------|--------------|-----------|
699
- | | | blog.html | :comments |
700
- | | blog | comments.html| :body |
701
- | blog | comments | index.html | :body |
702
- | gems/blog | comments | index.html | :body |
697
+ | Section | View File | View Folder | Component |
698
+ |-----------|--------------|----------------|-------------|
699
+ | :comments | blog.html | | |
700
+ | :body | comments.html| blog | |
701
+ | :body | index.html | comments | blog |
702
+ | :body | index.html | comments | gems/blog |
703
703
 
704
704
  Once the view file for the control or template is found, it will look for a matching controller. If the control is specified as a local template, an empty ModelController will be used. If a controller is found and loaded, a corrosponding "action" method will be called on it if its exists. Action methods default to "index" unless the component or template path has two parts, in which case the last part is the action.
705
705
 
@@ -799,4 +799,4 @@ store._things
799
799
 
800
800
  # Contributing
801
801
 
802
- You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on #voltrb on freenode (irc) so we don't duplicate work. Pull requests are always welcome, but asking about helping on IRC should save some duplication.
802
+ You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on #voltrb on freenode (irc) so we don't duplicate work. Pull requests are always welcome, but asking about helping on IRC should save some duplication.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.14
1
+ 0.7.15
@@ -2,49 +2,114 @@ class CLI
2
2
 
3
3
  desc "precompile", "precompile all application assets"
4
4
  def precompile
5
- require 'fileutils'
6
- require 'opal'
7
- require 'volt'
8
- require 'volt/server/rack/component_paths'
9
- require 'volt/server/rack/component_code'
10
- require 'volt/server/rack/opal_files'
5
+ compile
6
+ end
7
+
8
+ desc "watch", "compiles the project to /compiled when a file changes"
9
+ def watch
10
+ require 'listen'
11
+
12
+ listener = Listen.to('app') do |modified, added, removed|
13
+ compile
14
+ end
15
+
16
+ listener.start # non-blocking
17
+
18
+ Signal.trap("SIGINT") do
19
+ listener.stop
20
+ end
11
21
 
22
+ compile
12
23
 
13
- write_component_js
14
- write_sprockets
24
+ begin
25
+ sleep
26
+ rescue ThreadError => e
27
+ # ignore, breaks out on sigint
28
+ end
15
29
  end
16
30
 
17
31
  private
18
- def write_sprockets
19
- root_path ||= Dir.pwd
20
- Volt.root = root_path
32
+ def compile
33
+ print "compiling project..."
34
+ require 'fileutils'
35
+ require 'opal'
36
+ require 'volt'
37
+ require 'volt/server/rack/component_paths'
38
+ require 'volt/server/rack/component_code'
39
+ require 'volt/server/rack/opal_files'
40
+ require 'volt/server/rack/index_files'
41
+ require 'volt/server/component_handler'
21
42
 
22
- @app_path = File.expand_path(File.join(root_path, "app"))
43
+ @root_path ||= Dir.pwd
44
+ Volt.root = @root_path
23
45
 
24
- @component_paths = ComponentPaths.new(root_path)
46
+ @app_path = File.expand_path(File.join(@root_path, "app"))
47
+
48
+ @component_paths = ComponentPaths.new(@root_path)
25
49
  @app = Rack::Builder.new
50
+ @opal_files = OpalFiles.new(@app, @app_path, @component_paths)
51
+ @index_files = IndexFiles.new(@app, @component_paths, @opal_files)
52
+ @component_handler = ComponentHandler.new(@component_paths)
53
+
54
+ write_component_js
55
+ write_sprockets
56
+ write_js_and_css
57
+ write_index
26
58
 
59
+ puts "\rcompiled "
60
+ end
61
+
62
+ def write_sprockets
27
63
  # Serve the opal files
28
- opal_files = OpalFiles.new(@app, @app_path, @component_paths)
29
- opal_files.environment.each_file do |file_path|
30
- unless file_path.to_s[/[.]rb$/]
31
- puts "FILE: #{file_path}"
64
+ @opal_files.environment.each_logical_path do |logical_path|
65
+ logical_path = logical_path.to_s
66
+ # Only include files that aren't compiled elsewhere, like fonts
67
+ if !logical_path[/[.](y|css|js|html|erb)$/]
68
+ write_file(logical_path)
32
69
  end
33
70
  end
34
71
  end
35
72
 
36
- def write_component_js
37
- component_paths = ComponentPaths.new(Volt.root)
73
+ def write_js_and_css
74
+ (@index_files.javascript_files + @index_files.css_files).each do |logical_path|
75
+ logical_path = logical_path.gsub(/^\/assets\//, '')
76
+ write_file(logical_path)
77
+ end
78
+
79
+ end
38
80
 
39
- code = ComponentCode.new('main', component_paths).code
81
+ def write_file(logical_path)
82
+ path = "#{@root_path}/compiled/assets/#{logical_path}"
83
+
84
+ FileUtils.mkdir_p(File.dirname(path))
85
+
86
+ begin
87
+ content = @opal_files.environment[logical_path].to_s
88
+ File.open(path, "wb") do |file|
89
+ file.write(content)
90
+ end
91
+ rescue Sprockets::FileNotFound, SyntaxError => e
92
+ # ignore
93
+ end
94
+ end
40
95
 
41
- javascript_code = Opal.compile(code)
96
+ def write_component_js
97
+ javascript_code = @component_handler.compile_for_component('main')
42
98
 
43
- components_folder = File.join(Volt.root, '/public/components')
99
+ components_folder = File.join(Volt.root, '/compiled/components')
44
100
  FileUtils.mkdir_p(components_folder)
45
101
  File.open(File.join(components_folder, '/main.js'), 'w') do |file|
46
102
  file.write(javascript_code)
47
103
  end
48
104
  end
49
105
 
106
+
107
+ def write_index
108
+ path = "#{@root_path}/compiled/index.html"
109
+ FileUtils.mkdir_p(File.dirname(path))
110
+
111
+ File.open(path, 'w') do |file|
112
+ file.write(@index_files.html)
113
+ end
114
+ end
50
115
  end
@@ -1,3 +1,4 @@
1
+ require 'volt/extra_core/log'
1
2
  require 'volt/extra_core/array'
2
3
  require 'volt/extra_core/hash'
3
4
  require 'volt/extra_core/object'
@@ -0,0 +1,56 @@
1
+ if RUBY_PLATFORM == 'opal'
2
+ require 'volt/extra_core/logger'
3
+ else
4
+ require 'logger'
5
+ end
6
+
7
+ # Simple global access to the logger.
8
+ # You can also include Log into a class to get the logger
9
+ # inside of it.
10
+ module Log
11
+ def self.logger
12
+ @logger = Logger.new(STDOUT)
13
+ end
14
+
15
+ # Module methods, Log.info...
16
+ def self.fatal(*args, &block)
17
+ logger.fatal(*args, &block)
18
+ end
19
+
20
+ def self.info(*args, &block)
21
+ logger.info(*args, &block)
22
+ end
23
+
24
+ def self.warn(*args, &block)
25
+ logger.warn(*args, &block)
26
+ end
27
+
28
+ def self.debug(*args, &block)
29
+ logger.debug(*args, &block)
30
+ end
31
+
32
+ def self.error(*args, &block)
33
+ logger.error(*args, &block)
34
+ end
35
+
36
+ # Included methods, info "something"
37
+ def fatal(*args, &block)
38
+ Log.fatal(*args, &block)
39
+ end
40
+
41
+ def info(*args, &block)
42
+ Log.info(*args, &block)
43
+ end
44
+
45
+ def warn(*args, &block)
46
+ Log.warn(*args, &block)
47
+ end
48
+
49
+ def debug(*args, &block)
50
+ Log.debug(*args, &block)
51
+ end
52
+
53
+ def error(*args, &block)
54
+ Log.error(*args, &block)
55
+ end
56
+ end
@@ -0,0 +1,12 @@
1
+ class Logger
2
+ def initialize(log_to)
3
+ end
4
+
5
+ [:fatal, :info, :warn, :debug, :error].each do |method_name|
6
+ define_method(method_name) do |text, &block|
7
+ text = block.call if block
8
+
9
+ `console[method_name](text);`
10
+ end
11
+ end
12
+ end
@@ -18,7 +18,9 @@ class QueryListener
18
18
  @tasks.call('QueryTasks', 'add_listener', @collection, @query) do |results, errors|
19
19
  # When the initial data comes back, add it into the stores.
20
20
  @stores.each do |store|
21
- store.model.clear
21
+ # Clear if there are existing items
22
+ store.model.clear if store.model.size > 0
23
+
22
24
  results.each do |index, data|
23
25
  store.add(index, data)
24
26
  end
@@ -11,11 +11,16 @@ module StoreState
11
11
  end
12
12
 
13
13
  # Called from the QueryListener when the data is loaded
14
- def change_state_to(new_state)
14
+ def change_state_to(new_state, skip_trigger=false)
15
+ old_state = @state
15
16
  @state = new_state
16
17
 
17
18
  # Trigger changed on the 'state' method
18
- @model.trigger_for_methods!('changed', :state, :loaded?)
19
+ unless skip_trigger
20
+ if old_state != @state
21
+ @model.trigger_for_methods!('changed', :state, :loaded?)
22
+ end
23
+ end
19
24
 
20
25
  if @state == :loaded && @fetch_promises
21
26
  # Trigger each waiting fetch
@@ -23,19 +23,20 @@ class URL
23
23
  update!
24
24
  else
25
25
  host = `document.location.host`
26
+ protocol = `document.location.protocol`
26
27
 
27
- if url[0..3] != 'http'
28
- # Add the host for localized names
29
- url = "http://#{host}" + url
28
+ if url !~ /[:]\/\//
29
+ # Add the host for local urls
30
+ url = protocol + "//#{host}" + url
30
31
  else
31
- # Make sure its on the same host, otherwise its external.
32
- if url !~ /https?[:]\/\/#{host}/
32
+ # Make sure its on the same protocol and host, otherwise its external.
33
+ if url !~ /#{protocol}\/\/#{host}/
33
34
  # Different host, don't process
34
35
  return false
35
36
  end
36
37
  end
37
38
 
38
- matcher = url.match(/^(https?)[:]\/\/([^\/]+)(.*)$/)
39
+ matcher = url.match(/^(#{protocol[0..-2]})[:]\/\/([^\/]+)(.*)$/)
39
40
  @scheme = matcher[1]
40
41
  @host, @port = matcher[2].split(':')
41
42
  @port ||= 80
@@ -91,9 +92,13 @@ class URL
91
92
  if Volt.client?
92
93
  new_url = full_url()
93
94
 
94
- if `(document.location.href != new_url)`
95
- `history.pushState(null, null, new_url)`
96
- end
95
+ # Push the new url if pushState is supported
96
+ # TODO: add fragment fallback
97
+ %x{
98
+ if (document.location.href != new_url && history && history.pushState) {
99
+ history.pushState(null, null, new_url);
100
+ }
101
+ }
97
102
  end
98
103
  end
99
104
 
@@ -106,7 +111,6 @@ class URL
106
111
  if (anchor.length == 0) {
107
112
  anchor = $('*[name="' + this.fragment + '"]:first');
108
113
  }
109
- console.log('found anchor: ', anchor);
110
114
  if (anchor && anchor.length > 0) {
111
115
  console.log('scroll to: ', anchor.offset().top);
112
116
  $(document.body).scrollTop(anchor.offset().top);
@@ -88,7 +88,7 @@ class EachBinding < BaseBinding
88
88
  values = @value.cur
89
89
 
90
90
  return [] if values.is_a?(Model) || values.is_a?(Exception)
91
- values = values.attributes
91
+ values = values.attributes unless values.is_a?(ReactiveArray)
92
92
 
93
93
  return values
94
94
  end
@@ -11,8 +11,11 @@ class JSEvent
11
11
  `this.js_event.keyCode`
12
12
  end
13
13
 
14
- def stop
15
- # `this.js_event.stopPropagation();`
14
+ def stop!
15
+ `this.js_event.stopPropagation();`
16
+ end
17
+
18
+ def prevent_default!
16
19
  `this.js_event.preventDefault();`
17
20
  end
18
21
 
@@ -30,7 +33,7 @@ class EventBinding < BaseBinding
30
33
 
31
34
  handler = Proc.new do |js_event|
32
35
  event = JSEvent.new(js_event)
33
- event.stop if event_name == 'submit'
36
+ event.prevent_default! if event_name == 'submit'
34
37
 
35
38
  # Call the proc the user setup for the event in context,
36
39
  # pass in the wrapper for the JS event
@@ -1,5 +1,5 @@
1
1
  require 'volt/reactive/object_tracking'
2
- require 'volt/reactive/reactive_count'
2
+ require 'volt/reactive/reactive_block'
3
3
 
4
4
  class ReactiveArray# < Array
5
5
  include ReactiveTags
@@ -115,7 +115,6 @@ class ReactiveArray# < Array
115
115
 
116
116
  trigger_for_index!('changed', self.size-1)
117
117
  trigger_on_direct_listeners!('added', self.size-1)
118
-
119
118
  trigger_size_change!
120
119
 
121
120
  return result
@@ -231,6 +230,8 @@ class ReactiveArray# < Array
231
230
  true
232
231
  end
233
232
 
233
+ result = false if method_name == :reject
234
+
234
235
  result
235
236
  end
236
237
  end
@@ -242,9 +243,45 @@ class ReactiveArray# < Array
242
243
  # tag_method(:count) do
243
244
  # destructive!
244
245
  # end
245
- def count(&block)
246
+ def count(*args, &block)
247
+ # puts "GET COUNT"
246
248
  if block
247
- return ReactiveCount.new(self, block)
249
+ run_block = Proc.new do |source|
250
+ count = 0
251
+ source.cur.size.times do |index|
252
+ val = source[index]
253
+ result = block.call(val).cur
254
+ if result == true
255
+ count += 1
256
+ end
257
+ end
258
+
259
+ count
260
+ end
261
+
262
+ return ReactiveBlock.new(self, block, run_block)
263
+ else
264
+ @array.count(*args)
265
+ end
266
+ end
267
+
268
+ def reject(*args, &block)
269
+ if block
270
+ run_block = Proc.new do |source|
271
+ puts "RUN REJECT"
272
+ new_array = []
273
+ source.cur.size.times do |index|
274
+ val = source[index]
275
+ result = block.call(val).cur
276
+ if result != true
277
+ new_array << val.cur
278
+ end
279
+ end
280
+
281
+ ReactiveArray.new(new_array)
282
+ end
283
+
284
+ return ReactiveBlock.new(self, block, run_block)
248
285
  else
249
286
  @array.count
250
287
  end
@@ -1,40 +1,20 @@
1
- class ReactiveCount
1
+ class ReactiveBlock
2
2
  include ReactiveTags
3
3
 
4
4
  def reactive?
5
5
  true
6
6
  end
7
7
 
8
- def initialize(source, block)
8
+ def initialize(source, check_block, run_block)
9
9
  @source = ReactiveValue.new(source)
10
- @block = block
10
+ @check_block = check_block
11
+ @run_block = run_block
11
12
  end
12
13
 
13
14
  def cur
14
- direct_count
15
- end
16
-
17
- # After events are bound, we keep a cache of each cell's count
18
- # value, and base the results
19
- def cached_count
20
- @cached_results = []
21
-
22
-
23
- end
24
-
25
- # Before events are bound, when .cur is called, we simply
26
- # run the count on the source object.
27
- def direct_count
28
- count = 0
29
- @source.cur.size.times do |index|
30
- val = @source[index]
31
- result = @block.call(val).cur
32
- if result == true
33
- count += 1
34
- end
35
- end
15
+ val = @run_block.call(@source)
36
16
 
37
- count
17
+ return val
38
18
  end
39
19
 
40
20
  def setup_listeners
@@ -57,7 +37,6 @@ class ReactiveCount
57
37
  # We need to make sure we're listening on the result from each cell,
58
38
  # that way we can trigger when the value changes.
59
39
  def change_cell_count(size)
60
- # puts "CHANGE SIZE: #{size}"
61
40
  current_size = @cell_trackers.size
62
41
 
63
42
  if current_size < size
@@ -67,10 +46,10 @@ class ReactiveCount
67
46
  # Get the reactive value for the index
68
47
  val = @source[index]
69
48
 
70
- result = @block.call(val)
49
+ result = @check_block.call(val)
71
50
 
72
51
  @cell_trackers << result.on('changed') do
73
- # puts "RESULT CHANGED: #{index}"
52
+ puts "RESULT CHANGED: #{index} - #{self.object_id}"
74
53
  trigger!('changed')
75
54
  end
76
55
  end
@@ -96,7 +96,9 @@ class Routes
96
96
  # Next, split the url and walk the sections
97
97
  parts = url_parts(path)
98
98
 
99
- return match_path(parts, parts, @indirect_routes)
99
+ result = match_path(parts, parts, @indirect_routes)
100
+
101
+ return result
100
102
  end
101
103
 
102
104
  private
@@ -13,11 +13,22 @@ class ComponentHandler
13
13
  # TODO: Sanatize template path
14
14
  component_name = req.path.strip.gsub(/^\/components\//, '').gsub(/[.]js$/, '')
15
15
 
16
+ javascript_code = compile_for_component(component_name)
17
+
18
+ return [200, {"Content-Type" => "text/html; charset=utf-8"}, StringIO.new(javascript_code)]
19
+ end
20
+
21
+ def compile_for_component(component_name)
16
22
  code = ComponentCode.new(component_name, @component_paths).code
17
23
 
24
+ # Add the lib directory to the load path
25
+ Opal.append_path(Volt.root + '/lib')
26
+
27
+ # Compile the code
18
28
  javascript_code = Opal.compile(code)
19
29
 
20
- [200, {"Content-Type" => "text/html; charset=utf-8"}, StringIO.new(javascript_code)]
30
+ return javascript_code
31
+
21
32
  end
22
33
 
23
34
 
@@ -13,7 +13,7 @@ class SandlebarsParser
13
13
  def self.truth_hash(array)
14
14
  hash = {}
15
15
  array.each {|v| hash[v] = true }
16
-
16
+
17
17
  return hash
18
18
  end
19
19
 
@@ -23,28 +23,28 @@ class SandlebarsParser
23
23
  ATTRIBUTES = /([-\:A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/
24
24
 
25
25
  # Types of elements
26
- BLOCK = truth_hash(%w{address applet blockquote button center dd del dir div dl dt fieldset form frameset hr iframe ins isindex li map menu noframes noscript object ol p pre script table tbody td tfoot th thead tr ul})
26
+ BLOCK = truth_hash(%w{a address applet blockquote button center dd del dir div dl dt fieldset form frameset hr iframe ins isindex li map menu noframes noscript object ol p pre script table tbody td tfoot th thead tr ul})
27
27
  EMPTY = truth_hash(%w{area base basefont br col frame hr img input isindex link meta param embed})
28
- INLINE = truth_hash(%w{a abbr acronym applet b basefont bdo big br button cite code del dfn em font i iframe img input ins kbd label map object q s samp script select small span strike strong sub sup textarea tt u var})
28
+ INLINE = truth_hash(%w{abbr acronym applet b basefont bdo big br button cite code del dfn em font i iframe img input ins kbd label map object q s samp script select small span strike strong sub sup textarea tt u var})
29
29
  CLOSE_SELF = truth_hash(%w{colgroup dd dt li options p td tfoot th thead tr})
30
30
  SPECIAL = truth_hash(%w{script style})
31
-
31
+
32
32
  FILL_IN_ATTRIBUTES = truth_hash(%w{checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected})
33
-
33
+
34
34
  def initialize(html, handler, file_path=nil)
35
35
  @html = StringScanner.new(html)
36
36
  @handler = handler
37
37
  @file_path = file_path
38
-
38
+
39
39
  @stack = []
40
-
40
+
41
41
  parse
42
42
  end
43
-
43
+
44
44
  def last
45
45
  @stack.last
46
46
  end
47
-
47
+
48
48
  def parse
49
49
  loop do
50
50
  if last && SPECIAL[last]
@@ -52,27 +52,27 @@ class SandlebarsParser
52
52
  close_tag = "</#{last}>"
53
53
  body = @html.scan_until(/#{close_tag}/)
54
54
  body = body[0..((-1 * close_tag.size)-1)]
55
-
55
+
56
56
  body = body.gsub(/\<\!--(.*?)--\>/, "\\1").gsub(/\<\!\[CDATA\[(.*?)\]\]\>/, "\\1")
57
-
57
+
58
58
  text(body)
59
-
59
+
60
60
  end_tag(last, last)
61
61
  elsif @html.scan(/\<\!--/)
62
62
  # start comment
63
63
  comment = @html.scan_until(/--\>/)
64
64
  comment = comment[0..-4]
65
-
65
+
66
66
  @handler.comment(comment) if @handler.respond_to?(:comment)
67
67
  elsif (tag = @html.scan(START_TAG))
68
68
  tag_name = @html[1]
69
69
  rest = @html[2]
70
70
  unary = @html[3]
71
-
71
+
72
72
  start_tag(tag, tag_name, rest, unary)
73
73
  elsif @html.scan(END_TAG)
74
74
  tag_name = @html[1]
75
-
75
+
76
76
  end_tag(tag_name, tag_name)
77
77
  elsif (escaped = @html.scan(/\{\{\{(.*?)\}\}\}([^\}]|$)/))
78
78
  # Anything between {{{ and }}} is escaped and not processed (treaded as text)
@@ -80,7 +80,7 @@ class SandlebarsParser
80
80
  # Move back if we matched a new non } for close, skip if we hit the end
81
81
  @html.pos = @html.pos - 1
82
82
  end
83
-
83
+
84
84
  text(@html[1])
85
85
  elsif (binding = @html.scan(/\{/))
86
86
  # We are in text mode and matched the start of a binding
@@ -93,14 +93,14 @@ class SandlebarsParser
93
93
  break
94
94
  end
95
95
  end
96
-
96
+
97
97
  end_tag(nil, nil)
98
98
  end
99
-
99
+
100
100
  def text(text)
101
101
  @handler.text(text) if @handler.respond_to?(:text)
102
102
  end
103
-
103
+
104
104
  # Findings the end of a binding
105
105
  def start_binding
106
106
  binding = ''
@@ -109,7 +109,7 @@ class SandlebarsParser
109
109
  # scan until we reach a { or }
110
110
  loop do
111
111
  binding << @html.scan_until(/([\{\}\n]|\Z)/)
112
-
112
+
113
113
  match = @html[1]
114
114
  if match == '}'
115
115
  # close
@@ -126,23 +126,23 @@ class SandlebarsParser
126
126
  raise "should not reach here"
127
127
  end
128
128
  end
129
-
129
+
130
130
  binding = binding[0..-2]
131
- @handler.binding(binding) if @handler.respond_to?(:binding)
131
+ @handler.binding(binding) if @handler.respond_to?(:binding)
132
132
  end
133
-
133
+
134
134
  def raise_parse_error(error)
135
135
  line_number = @html.pre_match.count("\n") + 1
136
-
136
+
137
137
  error_str = error + " on line: #{line_number}"
138
138
  error_str += " of #{@file_path}" if @file_path
139
139
 
140
140
  raise HTMLParseError, error_str
141
141
  end
142
-
142
+
143
143
  def start_tag(tag, tag_name, rest, unary)
144
144
  section_tag = tag_name[0] == ':' && tag_name[1] =~ /[A-Z]/
145
-
145
+
146
146
  tag_name = tag_name.downcase
147
147
 
148
148
  # handle doctype so we get it output exactly the same way
@@ -157,33 +157,33 @@ class SandlebarsParser
157
157
  end_tag(nil, last)
158
158
  end
159
159
  end
160
-
160
+
161
161
  # Some tags close themselves when a new one of themselves is reached.
162
162
  # ex, a tr will close the previous tr
163
163
  if CLOSE_SELF[tag_name] && last == tag_name
164
164
  end_tag(nil, tag_name)
165
165
  end
166
-
166
+
167
167
  unary = EMPTY[tag_name] || !unary.blank?
168
-
168
+
169
169
  # Section tag's are also unary
170
170
  unless unary || section_tag
171
171
  @stack.push(tag_name)
172
172
  end
173
-
173
+
174
174
  if @handler.respond_to?(:start_tag)
175
175
  attributes = {}
176
-
176
+
177
177
  # Take the rest string and extract the attributes, filling in any
178
178
  # "fill in" attribute values if not provided.
179
179
  rest.scan(ATTRIBUTES).each do |match|
180
180
  name = match[0]
181
-
181
+
182
182
  value = match[1] || match[2] || match[3] || FILL_IN_ATTRIBUTES[name] || ''
183
-
183
+
184
184
  attributes[name] = value
185
185
  end
186
-
186
+
187
187
  if section_tag
188
188
  @handler.start_section(tag_name, attributes, unary)
189
189
  else
@@ -191,11 +191,11 @@ class SandlebarsParser
191
191
  end
192
192
  end
193
193
  end
194
-
194
+
195
195
  def end_tag(tag, tag_name)
196
196
  # If no tag name is provided, close all the way up
197
197
  new_size = 0
198
-
198
+
199
199
  if tag
200
200
  # Find the closest tag that closes.
201
201
  (@stack.size-1).downto(0) do |index|
@@ -205,7 +205,7 @@ class SandlebarsParser
205
205
  end
206
206
  end
207
207
  end
208
-
208
+
209
209
  if new_size >= 0
210
210
  if @handler.respond_to?(:end_tag)
211
211
  (@stack.size-1).downto(new_size) do |index|
@@ -1,7 +1,18 @@
1
1
  class Volt
2
2
  class Environment
3
3
  def initialize
4
- @env = ENV['VOLT_ENV'] || 'development'
4
+ @env = ENV['VOLT_ENV']
5
+
6
+ # If we're in opal, we can set the env from JS before opal loads
7
+ if RUBY_PLATFORM == 'opal'
8
+ unless @env
9
+ `if (window.start_env) {`
10
+ @env = `window.start_env`
11
+ `}`
12
+ end
13
+ end
14
+
15
+ @env ||= 'development'
5
16
  end
6
17
 
7
18
  def ==(val)
@@ -327,4 +327,38 @@ describe ReactiveArray do
327
327
  expect(count).to eq(0)
328
328
  end
329
329
  end
330
+
331
+ # describe "concat, diff" do
332
+ # it "should concat two arrays and trigger added/removed through" do
333
+ # a = ReactiveValue.new(ReactiveArray.new([1,2,3]))
334
+ # b = ReactiveValue.new(ReactiveArray.new([1,2,3]))
335
+ #
336
+ # c = a + b
337
+ #
338
+ # count = 0
339
+ # # c.on('added') { count += 1 }
340
+ # c.on('changed') { count += 1 }
341
+ # expect(count).to eq(0)
342
+ #
343
+ # b << 4
344
+ #
345
+ # expect(count).to eq(1)
346
+ # end
347
+ # end
348
+
349
+ describe "array methods" do
350
+ it "should handle compact with events" do
351
+ a = ReactiveValue.new(ReactiveArray.new([1,2,nil,3]))
352
+
353
+ count = 0
354
+ last_position = nil
355
+ compact = a.compact
356
+ compact.on('changed') { count += 1 }
357
+ expect(count).to eq(0)
358
+
359
+ a << 4
360
+
361
+ expect(count).to eq(1)
362
+ end
363
+ end
330
364
  end
@@ -1,6 +1,6 @@
1
1
  require 'volt/models'
2
2
 
3
- describe ReactiveCount do
3
+ describe ReactiveBlock do
4
4
  it "should call cur through the reactive count to the number" do
5
5
  model = ReactiveValue.new(Model.new)
6
6
 
@@ -5,4 +5,5 @@ tmp
5
5
  .idea
6
6
  .yardoc
7
7
  .sass-cache
8
- .DS_Store
8
+ .DS_Store
9
+ compiled
@@ -28,4 +28,4 @@ end
28
28
  # Needed at the moment
29
29
  gem 'opal', git: 'https://github.com/opal/opal.git'
30
30
  gem 'opal-jquery', :git => 'https://github.com/opal/opal-jquery.git'
31
- gem 'sockjs', git: 'https://github.com/kacperk/sockjs-ruby.git', require: false, platforms: :mri
31
+ gem 'sockjs', git: 'https://github.com/voltrb/sockjs-ruby.git', require: false, platforms: :mri
File without changes
@@ -9,9 +9,9 @@
9
9
  <% css_files.each do |css_file| %>
10
10
  <link href="<%= css_file %>" media="all" rel="stylesheet" type="text/css" />
11
11
  <% end %>
12
-
12
+
13
13
  </head>
14
14
  <body>
15
-
15
+
16
16
  </body>
17
17
  </html>
data/volt.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  # spec.add_dependency "bson_ext", "~> 1.9.0"
30
30
  # spec.add_dependency "thin", "~> 1.6.0"
31
31
  spec.add_dependency "rake", "~> 10.0.4"
32
- spec.add_dependency "listen", "~> 2.4.0"
32
+ spec.add_dependency "listen", "~> 2.7.0"
33
33
  spec.add_dependency "uglifier", "~> 2.4.0"
34
34
  spec.add_dependency "yui-compressor", "~> 0.12.0"
35
35
  # spec.add_dependency "promise.rb", "~> 0.6.1"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.14
4
+ version: 0.7.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Stout
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-25 00:00:00.000000000 Z
11
+ date: 2014-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 2.4.0
131
+ version: 2.7.0
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 2.4.0
138
+ version: 2.7.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: uglifier
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -370,6 +370,8 @@ files:
370
370
  - lib/volt/extra_core/inflector.rb
371
371
  - lib/volt/extra_core/inflector/inflections.rb
372
372
  - lib/volt/extra_core/inflector/methods.rb
373
+ - lib/volt/extra_core/log.rb
374
+ - lib/volt/extra_core/logger.rb
373
375
  - lib/volt/extra_core/numeric.rb
374
376
  - lib/volt/extra_core/object.rb
375
377
  - lib/volt/extra_core/string.rb
@@ -439,7 +441,7 @@ files:
439
441
  - lib/volt/reactive/events.rb
440
442
  - lib/volt/reactive/object_tracking.rb
441
443
  - lib/volt/reactive/reactive_array.rb
442
- - lib/volt/reactive/reactive_count.rb
444
+ - lib/volt/reactive/reactive_block.rb
443
445
  - lib/volt/reactive/reactive_generator.rb
444
446
  - lib/volt/reactive/reactive_tags.rb
445
447
  - lib/volt/reactive/reactive_value.rb
@@ -496,8 +498,8 @@ files:
496
498
  - spec/models/persistors/params_spec.rb
497
499
  - spec/models/persistors/store_spec.rb
498
500
  - spec/models/reactive_array_spec.rb
501
+ - spec/models/reactive_block_spec.rb
499
502
  - spec/models/reactive_call_times_spec.rb
500
- - spec/models/reactive_count_spec.rb
501
503
  - spec/models/reactive_generator_spec.rb
502
504
  - spec/models/reactive_tags_spec.rb
503
505
  - spec/models/reactive_value_spec.rb
@@ -557,6 +559,7 @@ files:
557
559
  - templates/project/app/main/views/main/index.html
558
560
  - templates/project/app/main/views/main/main.html
559
561
  - templates/project/config.ru
562
+ - templates/project/lib/.empty_directory
560
563
  - templates/project/public/index.html
561
564
  - templates/project/spec/spec_helper.rb
562
565
  - volt.gemspec
@@ -609,8 +612,8 @@ test_files:
609
612
  - spec/models/persistors/params_spec.rb
610
613
  - spec/models/persistors/store_spec.rb
611
614
  - spec/models/reactive_array_spec.rb
615
+ - spec/models/reactive_block_spec.rb
612
616
  - spec/models/reactive_call_times_spec.rb
613
- - spec/models/reactive_count_spec.rb
614
617
  - spec/models/reactive_generator_spec.rb
615
618
  - spec/models/reactive_tags_spec.rb
616
619
  - spec/models/reactive_value_spec.rb