volt 0.2.3
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 +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +37 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +23 -0
- data/Readme.md +34 -0
- data/VERSION +1 -0
- data/bin/volt +4 -0
- data/docs/GETTING_STARTED.md +7 -0
- data/docs/GUIDE.md +33 -0
- data/lib/volt.rb +15 -0
- data/lib/volt/benchmark/benchmark.rb +25 -0
- data/lib/volt/cli.rb +34 -0
- data/lib/volt/console.rb +19 -0
- data/lib/volt/controllers/model_controller.rb +29 -0
- data/lib/volt/extra_core/array.rb +10 -0
- data/lib/volt/extra_core/blank.rb +88 -0
- data/lib/volt/extra_core/extra_core.rb +7 -0
- data/lib/volt/extra_core/numeric.rb +9 -0
- data/lib/volt/extra_core/object.rb +36 -0
- data/lib/volt/extra_core/string.rb +29 -0
- data/lib/volt/extra_core/stringify_keys.rb +7 -0
- data/lib/volt/extra_core/true_false.rb +44 -0
- data/lib/volt/extra_core/try.rb +31 -0
- data/lib/volt/models.rb +5 -0
- data/lib/volt/models/array_model.rb +37 -0
- data/lib/volt/models/model.rb +210 -0
- data/lib/volt/models/model_wrapper.rb +23 -0
- data/lib/volt/models/params.rb +67 -0
- data/lib/volt/models/url.rb +192 -0
- data/lib/volt/page/url_tracker.rb +36 -0
- data/lib/volt/reactive/array_extensions.rb +13 -0
- data/lib/volt/reactive/event_chain.rb +126 -0
- data/lib/volt/reactive/events.rb +283 -0
- data/lib/volt/reactive/object_tracker.rb +99 -0
- data/lib/volt/reactive/object_tracking.rb +15 -0
- data/lib/volt/reactive/reactive_array.rb +222 -0
- data/lib/volt/reactive/reactive_tags.rb +64 -0
- data/lib/volt/reactive/reactive_value.rb +368 -0
- data/lib/volt/reactive/string_extensions.rb +34 -0
- data/lib/volt/router/routes.rb +83 -0
- data/lib/volt/server.rb +121 -0
- data/lib/volt/server/binding_setup.rb +2 -0
- data/lib/volt/server/channel_handler.rb +31 -0
- data/lib/volt/server/component_handler.rb +88 -0
- data/lib/volt/server/if_binding_setup.rb +29 -0
- data/lib/volt/server/request_handler.rb +16 -0
- data/lib/volt/server/scope.rb +43 -0
- data/lib/volt/server/source_map_server.rb +31 -0
- data/lib/volt/server/template_parser.rb +452 -0
- data/lib/volt/store/mongo.rb +5 -0
- data/lib/volt/templates/attribute_binding.rb +110 -0
- data/lib/volt/templates/base_binding.rb +37 -0
- data/lib/volt/templates/channel.rb +48 -0
- data/lib/volt/templates/content_binding.rb +35 -0
- data/lib/volt/templates/document_events.rb +80 -0
- data/lib/volt/templates/each_binding.rb +115 -0
- data/lib/volt/templates/event_binding.rb +51 -0
- data/lib/volt/templates/if_binding.rb +74 -0
- data/lib/volt/templates/memory_test.rb +26 -0
- data/lib/volt/templates/page.rb +146 -0
- data/lib/volt/templates/reactive_template.rb +38 -0
- data/lib/volt/templates/render_queue.rb +5 -0
- data/lib/volt/templates/sub_context.rb +23 -0
- data/lib/volt/templates/targets/attribute_section.rb +33 -0
- data/lib/volt/templates/targets/attribute_target.rb +18 -0
- data/lib/volt/templates/targets/base_section.rb +14 -0
- data/lib/volt/templates/targets/binding_document/base_node.rb +3 -0
- data/lib/volt/templates/targets/binding_document/component_node.rb +112 -0
- data/lib/volt/templates/targets/binding_document/html_node.rb +11 -0
- data/lib/volt/templates/targets/dom_section.rb +147 -0
- data/lib/volt/templates/targets/dom_target.rb +11 -0
- data/lib/volt/templates/template_binding.rb +159 -0
- data/lib/volt/templates/template_renderer.rb +50 -0
- data/spec/models/event_chain_spec.rb +129 -0
- data/spec/models/model_spec.rb +340 -0
- data/spec/models/old_model_spec.rb +109 -0
- data/spec/models/reactive_array_spec.rb +262 -0
- data/spec/models/reactive_tags_spec.rb +35 -0
- data/spec/models/reactive_value_spec.rb +336 -0
- data/spec/models/string_extensions_spec.rb +57 -0
- data/spec/router/routes_spec.rb +24 -0
- data/spec/server/template_parser_spec.rb +50 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/store/mongo_spec.rb +4 -0
- data/spec/templates/targets/binding_document/component_node_spec.rb +18 -0
- data/spec/templates/template_binding_spec.rb +98 -0
- data/templates/.gitignore +12 -0
- data/templates/Gemfile.tt +8 -0
- data/templates/app/.empty_directory +0 -0
- data/templates/app/home/config/routes.rb +1 -0
- data/templates/app/home/controllers/index_controller.rb +5 -0
- data/templates/app/home/css/.empty_directory +0 -0
- data/templates/app/home/models/.empty_directory +0 -0
- data/templates/app/home/views/index/about.html +9 -0
- data/templates/app/home/views/index/home.html +7 -0
- data/templates/app/home/views/index/index.html +28 -0
- data/templates/config.ru +4 -0
- data/templates/public/css/ansi.css +0 -0
- data/templates/public/css/bootstrap-theme.css +459 -0
- data/templates/public/css/bootstrap.css +7098 -0
- data/templates/public/css/jumbotron.css +79 -0
- data/templates/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/templates/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/templates/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/templates/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/templates/public/index.html +25 -0
- data/templates/public/js/bootstrap.js +0 -0
- data/templates/public/js/jquery-2.0.3.js +8829 -0
- data/templates/public/js/sockjs-0.2.1.min.js +27 -0
- data/templates/spec/spec_helper.rb +20 -0
- data/volt.gemspec +41 -0
- metadata +412 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class String
|
|
2
|
+
include ReactiveTags
|
|
3
|
+
|
|
4
|
+
alias :__old_plus :+
|
|
5
|
+
if RUBY_PLATFORM != 'opal'
|
|
6
|
+
alias :__old_concat :<<
|
|
7
|
+
end
|
|
8
|
+
# alias :concat :__old_concat
|
|
9
|
+
|
|
10
|
+
# In volt, we want a value + reactive strings to return a reactive string. So we
|
|
11
|
+
# over-ride + to check for when we are adding a reactive string to a string.
|
|
12
|
+
def +(val)
|
|
13
|
+
result = __old_plus(val.cur)
|
|
14
|
+
if val.reactive? && !result.reactive?
|
|
15
|
+
result = ReactiveValue.new(result)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
return result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if RUBY_PLATFORM != 'opal'
|
|
22
|
+
tag_method(:<<) do
|
|
23
|
+
destructive!
|
|
24
|
+
end
|
|
25
|
+
def <<(val)
|
|
26
|
+
if val.reactive?
|
|
27
|
+
raise "Cannot append a reactive string to non-reactive string. Use + instead"
|
|
28
|
+
end
|
|
29
|
+
result = __old_concat(val)
|
|
30
|
+
|
|
31
|
+
return result
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class Routes
|
|
2
|
+
attr_reader :routes
|
|
3
|
+
|
|
4
|
+
def initialize
|
|
5
|
+
@routes = []
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def define(&block)
|
|
9
|
+
instance_eval(&block)
|
|
10
|
+
|
|
11
|
+
return self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(path, options)
|
|
15
|
+
if path.index(':')
|
|
16
|
+
sections = path.split(/([:][^:\/]+)/)
|
|
17
|
+
|
|
18
|
+
sections.each do |section|
|
|
19
|
+
if section[0] == ':'
|
|
20
|
+
options[section[1..-1]] = nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
path = Proc.new do |params|
|
|
24
|
+
# Create a path using the params in the path
|
|
25
|
+
sections.map do |section|
|
|
26
|
+
if section[0] == ':'
|
|
27
|
+
params[section[1..-1]]
|
|
28
|
+
else
|
|
29
|
+
section
|
|
30
|
+
end
|
|
31
|
+
end.join('')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@routes << [path, options]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Takes in params and generates a path and the remaining params
|
|
39
|
+
# that should be shown in the url.
|
|
40
|
+
def url_for_params(params)
|
|
41
|
+
routes.each do |route|
|
|
42
|
+
if params_match_options?(params, route[1])
|
|
43
|
+
return path_and_params(params, route[0], route[1])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return '/', params
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Takes in a path and returns the matching params.
|
|
51
|
+
def params_for_path(path)
|
|
52
|
+
routes.each do |route|
|
|
53
|
+
if route[0] == path
|
|
54
|
+
# Found the matching route
|
|
55
|
+
return route[1]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
def path_and_params(params, path, options)
|
|
64
|
+
params = params.attributes.dup
|
|
65
|
+
path = path.call(params) if path.class == Proc
|
|
66
|
+
|
|
67
|
+
options.keys.each do |key|
|
|
68
|
+
params.delete(key)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
return path, params
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def params_match_options?(params, options)
|
|
75
|
+
options.each_pair do |key, value|
|
|
76
|
+
if value == nil || value != params.send(key)
|
|
77
|
+
return false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
return true
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/volt/server.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require 'opal'
|
|
2
|
+
require "rack"
|
|
3
|
+
if RUBY_PLATFORM != 'java'
|
|
4
|
+
require "rack/sockjs"
|
|
5
|
+
require "eventmachine"
|
|
6
|
+
end
|
|
7
|
+
require "sprockets-sass"
|
|
8
|
+
require "sass"
|
|
9
|
+
|
|
10
|
+
require 'volt/extra_core/extra_core'
|
|
11
|
+
require 'volt/server/request_handler'
|
|
12
|
+
require 'volt/server/component_handler'
|
|
13
|
+
if RUBY_PLATFORM != 'java'
|
|
14
|
+
require 'volt/server/channel_handler'
|
|
15
|
+
end
|
|
16
|
+
require 'volt/server/source_map_server'
|
|
17
|
+
|
|
18
|
+
class Index
|
|
19
|
+
|
|
20
|
+
def initialize(app, files)
|
|
21
|
+
@app = app
|
|
22
|
+
@files = files
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call(env)
|
|
26
|
+
if %w[/ /demo /blog /todos /page3 /page4].include?(env['PATH_INFO']) || env['PATH_INFO'][0..5] == '/todos'
|
|
27
|
+
[200, { 'Content-Type' => 'text/html' }, [html]]
|
|
28
|
+
else
|
|
29
|
+
@app.call env
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def html
|
|
34
|
+
index_path = File.expand_path(File.join(Dir.pwd, "public/index.html"))
|
|
35
|
+
html = File.read(index_path)
|
|
36
|
+
|
|
37
|
+
ERB.new(html).result(binding)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
SOURCE_MAPS = !!ENV['MAPS']
|
|
44
|
+
|
|
45
|
+
Opal::Processor.source_map_enabled = SOURCE_MAPS
|
|
46
|
+
# Opal::Processor.arity_check_enabled = true
|
|
47
|
+
# Opal::Processor.dynamic_require_severity = :raise
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Server
|
|
52
|
+
def self.app
|
|
53
|
+
|
|
54
|
+
builder = Rack::Builder.new do
|
|
55
|
+
use Rack::CommonLogger
|
|
56
|
+
# run RequestHandler.new
|
|
57
|
+
|
|
58
|
+
use Rack::ShowExceptions
|
|
59
|
+
|
|
60
|
+
map '/components' do
|
|
61
|
+
run ComponentHandler.new
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
environment = Opal::Environment.new
|
|
65
|
+
|
|
66
|
+
app_path = File.expand_path(File.join(Dir.pwd, "app"))
|
|
67
|
+
environment.append_path(app_path)
|
|
68
|
+
|
|
69
|
+
volt_gem_lib_path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
|
70
|
+
environment.append_path(volt_gem_lib_path)
|
|
71
|
+
|
|
72
|
+
# Add the opal load paths
|
|
73
|
+
Opal.paths.each do |path|
|
|
74
|
+
environment.append_path(path)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# opal-jquery gem
|
|
78
|
+
spec = Gem::Specification.find_by_name("opal-jquery")
|
|
79
|
+
environment.append_path(spec.gem_dir + "/opal")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
map '/assets' do
|
|
83
|
+
run environment
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if SOURCE_MAPS
|
|
87
|
+
source_maps = SourceMapServer.new(environment)
|
|
88
|
+
|
|
89
|
+
map(source_maps.prefix) do
|
|
90
|
+
run source_maps
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if RUBY_PLATFORM != 'java'
|
|
95
|
+
map "/channel" do
|
|
96
|
+
run Rack::SockJS.new(ChannelHandler)#, :websocket => false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if SOURCE_MAPS
|
|
101
|
+
files = environment['volt/templates/page'].to_a.map {|v| v.logical_path }
|
|
102
|
+
else
|
|
103
|
+
files = []
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
use Index, files
|
|
107
|
+
|
|
108
|
+
use Rack::Static,
|
|
109
|
+
:urls => ["/"],
|
|
110
|
+
:root => "public",
|
|
111
|
+
:index => "",
|
|
112
|
+
:header_rules => [
|
|
113
|
+
[:all, {'Cache-Control' => 'public, max-age=86400'}]
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
run lambda{ |env| [ 404, { 'Content-Type' => 'text/html' }, ['404 - page not found'] ] }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
return builder
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'sockjs/session'
|
|
3
|
+
|
|
4
|
+
class ChannelHandler < SockJS::Session
|
|
5
|
+
def self.message_all
|
|
6
|
+
@@channels.each do |channel|
|
|
7
|
+
channel.send(message)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(session, *args)
|
|
13
|
+
@session = session
|
|
14
|
+
|
|
15
|
+
@@channels ||= []
|
|
16
|
+
@@channels << self
|
|
17
|
+
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def process_message(message)
|
|
22
|
+
puts "Process: #{message}"
|
|
23
|
+
self.class.message_all(message)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def closed
|
|
27
|
+
# Remove ourself from the available channels
|
|
28
|
+
@@channels.delete(self)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'volt'
|
|
3
|
+
require 'volt/server/template_parser'
|
|
4
|
+
|
|
5
|
+
class ComponentHandler
|
|
6
|
+
def call(env)
|
|
7
|
+
req = Rack::Request.new(env)
|
|
8
|
+
|
|
9
|
+
# TODO: Sanatize template path
|
|
10
|
+
@component_path = req.path.strip.gsub(/^\/components\//, '').gsub(/[.]js$/, '')
|
|
11
|
+
|
|
12
|
+
code = generate_controller_code + generate_view_code + generate_model_code + generate_routes_code
|
|
13
|
+
|
|
14
|
+
javascript_code = Opal.compile(code)
|
|
15
|
+
|
|
16
|
+
# puts "ENV: #{env.inspect}"
|
|
17
|
+
[200, {"Content-Type" => "text/html"}, StringIO.new(javascript_code)]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def generate_view_code
|
|
21
|
+
code = ''
|
|
22
|
+
views_path = Volt.root + "/app/#{@component_path}/views/"
|
|
23
|
+
|
|
24
|
+
# Load all templates in the folder
|
|
25
|
+
Dir["#{views_path}*/*.html"].each do |view_path|
|
|
26
|
+
# Get the path for the template, supports templates in folders
|
|
27
|
+
template_path = view_path[views_path.size..((-1 * ('.html'.size + 1)))]
|
|
28
|
+
template_path = "#{@component_path}/#{template_path}"
|
|
29
|
+
# puts "Template Path: #{template_path.inspect}"
|
|
30
|
+
|
|
31
|
+
all_templates = TemplateParser.new(File.read(view_path), template_path)
|
|
32
|
+
|
|
33
|
+
binding_initializers = []
|
|
34
|
+
all_templates.templates.each_pair do |name, template|
|
|
35
|
+
binding_code = []
|
|
36
|
+
|
|
37
|
+
template['bindings'].each_pair do |key,value|
|
|
38
|
+
binding_code << "#{key.inspect} => [#{value.join(', ')}]"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
binding_code = "{#{binding_code.join(', ')}}"
|
|
42
|
+
|
|
43
|
+
code << "$page.add_template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})\n"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
# puts "--------------"
|
|
47
|
+
# puts "CODE: #{code}"
|
|
48
|
+
|
|
49
|
+
return code
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def generate_controller_code
|
|
53
|
+
code = ''
|
|
54
|
+
controllers_path = Volt.root + "/app/#{@component_path}/controllers/"
|
|
55
|
+
|
|
56
|
+
Dir["#{controllers_path}*_controller.rb"].each do |controller_path|
|
|
57
|
+
code << File.read(controller_path) + "\n\n"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
return code
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_model_code
|
|
64
|
+
code = ''
|
|
65
|
+
models_path = Volt.root + "/app/#{@component_path}/models/"
|
|
66
|
+
|
|
67
|
+
Dir["#{models_path}*.rb"].each do |model_path|
|
|
68
|
+
code << File.read(model_path) + "\n\n"
|
|
69
|
+
|
|
70
|
+
model_name = model_path.match(/([^\/]+)[.]rb$/)[1]
|
|
71
|
+
|
|
72
|
+
code << "$page.add_model(#{model_name.inspect})\n\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return code
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def generate_routes_code
|
|
79
|
+
code = ''
|
|
80
|
+
routes_path = Volt.root + "/app/#{@component_path}/config/routes.rb"
|
|
81
|
+
|
|
82
|
+
code << "$page.add_routes do\n"
|
|
83
|
+
code << "\n" + File.read(routes_path) + "\n"
|
|
84
|
+
code << "end\n\n"
|
|
85
|
+
|
|
86
|
+
return code
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# With if bindings, we need to track each branch, which is on the
|
|
2
|
+
# same scope level as the original if statement. We use this class
|
|
3
|
+
# to track each branch.
|
|
4
|
+
|
|
5
|
+
require 'volt/server/binding_setup'
|
|
6
|
+
class IfBindingSetup < BindingSetup
|
|
7
|
+
def initialize
|
|
8
|
+
@branches = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add_branch(content, template_name)
|
|
12
|
+
@branches << [content, template_name]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_setup_code
|
|
16
|
+
branches = @branches.map do |branch|
|
|
17
|
+
content = branch[0]
|
|
18
|
+
if content == nil
|
|
19
|
+
content = nil.inspect
|
|
20
|
+
else
|
|
21
|
+
content = "Proc.new { #{branch[0]} }"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
"[#{content}, #{branch[1].inspect}]"
|
|
25
|
+
end.join(', ')
|
|
26
|
+
|
|
27
|
+
"lambda { |target, context, id| IfBinding.new(target, context, id, [#{branches}]) }"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
|
|
3
|
+
class RequestHandler
|
|
4
|
+
def call(env)
|
|
5
|
+
req = Rack::Request.new(env)
|
|
6
|
+
# puts env.inspect
|
|
7
|
+
# puts req.inspect
|
|
8
|
+
|
|
9
|
+
puts req.path
|
|
10
|
+
req.post?
|
|
11
|
+
puts req.params["data"]
|
|
12
|
+
|
|
13
|
+
# puts "ENV: #{env.inspect}"
|
|
14
|
+
[200, {"Content-Type" => "text/html"}, StringIO.new("Hello Rack!")]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Template parsing is fairly simple at the moment. Basically we walk the dom and
|
|
2
|
+
# do two types of replacements.
|
|
3
|
+
# 1) replacement in text nodes
|
|
4
|
+
# 2) attribute replacements
|
|
5
|
+
|
|
6
|
+
class Scope
|
|
7
|
+
attr_accessor :bindings, :outer_binding_number, :closed_block_scopes, :last_if_binding
|
|
8
|
+
|
|
9
|
+
def initialize(outer_binding_number=nil)
|
|
10
|
+
# For block bindings, the outer binding number lets us know what the name
|
|
11
|
+
# of the comments are that go before/after this scope block.
|
|
12
|
+
@outer_binding_number = outer_binding_number
|
|
13
|
+
@bindings = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_closed_child_scope(scope)
|
|
17
|
+
@closed_block_scopes ||= []
|
|
18
|
+
@closed_block_scopes << scope
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_binding(binding_name, setup_code)
|
|
22
|
+
@bindings[binding_name] ||= []
|
|
23
|
+
@bindings[binding_name] << setup_code
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start_if_binding(binding_name, if_binding_setup)
|
|
27
|
+
@last_if_binding = [binding_name, if_binding_setup]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def current_if_binding
|
|
31
|
+
@last_if_binding
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def close_if_binding!
|
|
35
|
+
if @last_if_binding
|
|
36
|
+
binding_name, if_binding_setup = @last_if_binding
|
|
37
|
+
@last_if_binding = nil
|
|
38
|
+
|
|
39
|
+
add_binding(binding_name, if_binding_setup.to_setup_code)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|