volt 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +37 -0
  6. data/Guardfile +9 -0
  7. data/LICENSE.txt +22 -0
  8. data/Rakefile +23 -0
  9. data/Readme.md +34 -0
  10. data/VERSION +1 -0
  11. data/bin/volt +4 -0
  12. data/docs/GETTING_STARTED.md +7 -0
  13. data/docs/GUIDE.md +33 -0
  14. data/lib/volt.rb +15 -0
  15. data/lib/volt/benchmark/benchmark.rb +25 -0
  16. data/lib/volt/cli.rb +34 -0
  17. data/lib/volt/console.rb +19 -0
  18. data/lib/volt/controllers/model_controller.rb +29 -0
  19. data/lib/volt/extra_core/array.rb +10 -0
  20. data/lib/volt/extra_core/blank.rb +88 -0
  21. data/lib/volt/extra_core/extra_core.rb +7 -0
  22. data/lib/volt/extra_core/numeric.rb +9 -0
  23. data/lib/volt/extra_core/object.rb +36 -0
  24. data/lib/volt/extra_core/string.rb +29 -0
  25. data/lib/volt/extra_core/stringify_keys.rb +7 -0
  26. data/lib/volt/extra_core/true_false.rb +44 -0
  27. data/lib/volt/extra_core/try.rb +31 -0
  28. data/lib/volt/models.rb +5 -0
  29. data/lib/volt/models/array_model.rb +37 -0
  30. data/lib/volt/models/model.rb +210 -0
  31. data/lib/volt/models/model_wrapper.rb +23 -0
  32. data/lib/volt/models/params.rb +67 -0
  33. data/lib/volt/models/url.rb +192 -0
  34. data/lib/volt/page/url_tracker.rb +36 -0
  35. data/lib/volt/reactive/array_extensions.rb +13 -0
  36. data/lib/volt/reactive/event_chain.rb +126 -0
  37. data/lib/volt/reactive/events.rb +283 -0
  38. data/lib/volt/reactive/object_tracker.rb +99 -0
  39. data/lib/volt/reactive/object_tracking.rb +15 -0
  40. data/lib/volt/reactive/reactive_array.rb +222 -0
  41. data/lib/volt/reactive/reactive_tags.rb +64 -0
  42. data/lib/volt/reactive/reactive_value.rb +368 -0
  43. data/lib/volt/reactive/string_extensions.rb +34 -0
  44. data/lib/volt/router/routes.rb +83 -0
  45. data/lib/volt/server.rb +121 -0
  46. data/lib/volt/server/binding_setup.rb +2 -0
  47. data/lib/volt/server/channel_handler.rb +31 -0
  48. data/lib/volt/server/component_handler.rb +88 -0
  49. data/lib/volt/server/if_binding_setup.rb +29 -0
  50. data/lib/volt/server/request_handler.rb +16 -0
  51. data/lib/volt/server/scope.rb +43 -0
  52. data/lib/volt/server/source_map_server.rb +31 -0
  53. data/lib/volt/server/template_parser.rb +452 -0
  54. data/lib/volt/store/mongo.rb +5 -0
  55. data/lib/volt/templates/attribute_binding.rb +110 -0
  56. data/lib/volt/templates/base_binding.rb +37 -0
  57. data/lib/volt/templates/channel.rb +48 -0
  58. data/lib/volt/templates/content_binding.rb +35 -0
  59. data/lib/volt/templates/document_events.rb +80 -0
  60. data/lib/volt/templates/each_binding.rb +115 -0
  61. data/lib/volt/templates/event_binding.rb +51 -0
  62. data/lib/volt/templates/if_binding.rb +74 -0
  63. data/lib/volt/templates/memory_test.rb +26 -0
  64. data/lib/volt/templates/page.rb +146 -0
  65. data/lib/volt/templates/reactive_template.rb +38 -0
  66. data/lib/volt/templates/render_queue.rb +5 -0
  67. data/lib/volt/templates/sub_context.rb +23 -0
  68. data/lib/volt/templates/targets/attribute_section.rb +33 -0
  69. data/lib/volt/templates/targets/attribute_target.rb +18 -0
  70. data/lib/volt/templates/targets/base_section.rb +14 -0
  71. data/lib/volt/templates/targets/binding_document/base_node.rb +3 -0
  72. data/lib/volt/templates/targets/binding_document/component_node.rb +112 -0
  73. data/lib/volt/templates/targets/binding_document/html_node.rb +11 -0
  74. data/lib/volt/templates/targets/dom_section.rb +147 -0
  75. data/lib/volt/templates/targets/dom_target.rb +11 -0
  76. data/lib/volt/templates/template_binding.rb +159 -0
  77. data/lib/volt/templates/template_renderer.rb +50 -0
  78. data/spec/models/event_chain_spec.rb +129 -0
  79. data/spec/models/model_spec.rb +340 -0
  80. data/spec/models/old_model_spec.rb +109 -0
  81. data/spec/models/reactive_array_spec.rb +262 -0
  82. data/spec/models/reactive_tags_spec.rb +35 -0
  83. data/spec/models/reactive_value_spec.rb +336 -0
  84. data/spec/models/string_extensions_spec.rb +57 -0
  85. data/spec/router/routes_spec.rb +24 -0
  86. data/spec/server/template_parser_spec.rb +50 -0
  87. data/spec/spec_helper.rb +20 -0
  88. data/spec/store/mongo_spec.rb +4 -0
  89. data/spec/templates/targets/binding_document/component_node_spec.rb +18 -0
  90. data/spec/templates/template_binding_spec.rb +98 -0
  91. data/templates/.gitignore +12 -0
  92. data/templates/Gemfile.tt +8 -0
  93. data/templates/app/.empty_directory +0 -0
  94. data/templates/app/home/config/routes.rb +1 -0
  95. data/templates/app/home/controllers/index_controller.rb +5 -0
  96. data/templates/app/home/css/.empty_directory +0 -0
  97. data/templates/app/home/models/.empty_directory +0 -0
  98. data/templates/app/home/views/index/about.html +9 -0
  99. data/templates/app/home/views/index/home.html +7 -0
  100. data/templates/app/home/views/index/index.html +28 -0
  101. data/templates/config.ru +4 -0
  102. data/templates/public/css/ansi.css +0 -0
  103. data/templates/public/css/bootstrap-theme.css +459 -0
  104. data/templates/public/css/bootstrap.css +7098 -0
  105. data/templates/public/css/jumbotron.css +79 -0
  106. data/templates/public/fonts/glyphicons-halflings-regular.eot +0 -0
  107. data/templates/public/fonts/glyphicons-halflings-regular.svg +229 -0
  108. data/templates/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  109. data/templates/public/fonts/glyphicons-halflings-regular.woff +0 -0
  110. data/templates/public/index.html +25 -0
  111. data/templates/public/js/bootstrap.js +0 -0
  112. data/templates/public/js/jquery-2.0.3.js +8829 -0
  113. data/templates/public/js/sockjs-0.2.1.min.js +27 -0
  114. data/templates/spec/spec_helper.rb +20 -0
  115. data/volt.gemspec +41 -0
  116. metadata +412 -0
@@ -0,0 +1,7 @@
1
+ require 'volt/extra_core/array'
2
+ require 'volt/extra_core/object'
3
+ require 'volt/extra_core/blank'
4
+ require 'volt/extra_core/stringify_keys'
5
+ require 'volt/extra_core/string'
6
+ require 'volt/extra_core/numeric'
7
+ require 'volt/extra_core/true_false'
@@ -0,0 +1,9 @@
1
+ class Numeric
2
+ def in_units_of(unit)
3
+ if self == 1
4
+ return "1 #{unit}"
5
+ else
6
+ return "#{self} #{unit}s"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ class Object
2
+ # Setup a default pretty_inspect
3
+ # alias_method :pretty_inspect, :inspect
4
+
5
+ def instance_values
6
+ Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
7
+ end
8
+
9
+ # Provides the same functionality as ||, but since ReactiveValue's only
10
+ # work with method calls, we provide .or as a convience.
11
+ def or(other)
12
+ if self.true?
13
+ return self
14
+ else
15
+ return other
16
+ end
17
+ end
18
+
19
+ # Provides the same functionality as &&, but since ReactiveValue's only
20
+ # work with method calls, we provide .and as a convience
21
+ def and(other)
22
+ if self.true?
23
+ return other
24
+ else
25
+ return self
26
+ end
27
+ end
28
+
29
+ def try(*a, &b)
30
+ if a.empty? && block_given?
31
+ yield self
32
+ else
33
+ __send__(*a, &b)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ class String
2
+ # TODO: replace with better implementations
3
+ # NOTE: strings are currently immutable in Opal, so no ! methods
4
+ def camelize
5
+ self.split("_").map {|s| s.capitalize }.join("")
6
+ end
7
+
8
+ def underscore
9
+ self.scan(/[A-Z][a-z]*/).join("_").downcase
10
+ end
11
+
12
+ def pluralize
13
+ # TODO: Temp implementation
14
+ if self[-1] != 's'
15
+ return self + 's'
16
+ else
17
+ return self
18
+ end
19
+ end
20
+
21
+ def singularize
22
+ # TODO: Temp implementation
23
+ if self[-1] == 's'
24
+ return self[0..-2]
25
+ else
26
+ return self
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+ def stringify_keys
3
+ self.each_with_object({}) { |(key, value), hash|
4
+ hash[key.to_s] = value
5
+ }
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ # The true?/false? predicates are convience methods since if ..reactive_value.. does
2
+ # not correctly evaluate. The reason for this is that ruby currently does not allow
3
+ # anything besides nil and false to be falsy.
4
+
5
+ class Object
6
+ def true?
7
+ true
8
+ end
9
+
10
+ def false?
11
+ false
12
+ end
13
+ end
14
+
15
+ class FalseClass
16
+ def true?
17
+ false
18
+ end
19
+
20
+ def false?
21
+ true
22
+ end
23
+ end
24
+
25
+ class NilClass
26
+ def true?
27
+ false
28
+ end
29
+
30
+ def false?
31
+ true
32
+ end
33
+ end
34
+
35
+ # Opal only has a single class for true/false
36
+ class Boolean
37
+ def true?
38
+ self
39
+ end
40
+
41
+ def false?
42
+ self
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ class Object
2
+
3
+ class TryProxy
4
+ def initialize(original)
5
+ @original = original
6
+ end
7
+
8
+ def method_missing(method_name, *args, &block)
9
+ if @original.respond_to?(method_name)
10
+
11
+ else
12
+ NilProxy.new
13
+ end
14
+ end
15
+ end
16
+
17
+ class NilProxy
18
+ def method_missing(method_name, *args, &block)
19
+ if @original.respond_to?(method_name)
20
+
21
+ else
22
+ NilProxy.new
23
+ end
24
+ end
25
+ end
26
+
27
+ def try
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,5 @@
1
+ require 'volt/extra_core/extra_core'
2
+ require 'volt/reactive/reactive_value'
3
+ require 'volt/models/model'
4
+ require 'volt/models/params'
5
+
@@ -0,0 +1,37 @@
1
+ require 'volt/models/model_wrapper'
2
+
3
+ class ArrayModel < ReactiveArray
4
+ include ModelWrapper
5
+
6
+ def initialize(array=[], parent=nil, path=nil)
7
+ @parent = parent
8
+ @path = path
9
+
10
+ array = wrap_values(array)
11
+
12
+ super(array)
13
+ end
14
+
15
+ def attributes
16
+ self
17
+ end
18
+
19
+ # Make sure it gets wrapped
20
+ def <<(*args)
21
+ args = wrap_values(args)
22
+
23
+ super(*args)
24
+ end
25
+
26
+ # Make sure it gets wrapped
27
+ def inject(*args)
28
+ args = wrap_values(args)
29
+ super(*args)
30
+ end
31
+
32
+ # Make sure it gets wrapped
33
+ def +(*args)
34
+ args = wrap_values(args)
35
+ super(*args)
36
+ end
37
+ end
@@ -0,0 +1,210 @@
1
+ require 'volt/models/model_wrapper'
2
+ require 'volt/models/array_model'
3
+ require 'volt/reactive/object_tracking'
4
+
5
+ class NilMethodCall < NoMethodError
6
+ def true?
7
+ false
8
+ end
9
+
10
+ def false?
11
+ true
12
+ end
13
+ end
14
+
15
+ class Model
16
+ include ReactiveTags
17
+ include ModelWrapper
18
+ include ObjectTracking
19
+
20
+ attr_accessor :attributes
21
+ attr_reader :parent, :path
22
+
23
+ def nil?
24
+ attributes.nil?
25
+ end
26
+
27
+ def false?
28
+ attributes.false?
29
+ end
30
+
31
+ def true?
32
+ attributes.true?
33
+ end
34
+
35
+ def initialize(attributes={}, parent=nil, path=nil, class_paths=nil)
36
+ self.attributes = wrap_values(attributes)
37
+ @parent = parent
38
+ @path = path
39
+ end
40
+
41
+ # Pass the comparison through
42
+ def ==(val)
43
+ attributes == val
44
+ end
45
+
46
+ # Pass through needed
47
+ def !
48
+ !attributes
49
+ end
50
+
51
+ tag_method(:delete) do
52
+ destructive!
53
+ end
54
+ def delete(*args)
55
+ __clear_element(args[0])
56
+ attributes.delete(*args)
57
+ trigger_by_attribute!('changed', args[0])
58
+ end
59
+
60
+ tag_all_methods do
61
+ destructive! do |method_name|
62
+ method_name[0] == '_' && method_name[-1] == '='
63
+ end
64
+ pass_reactive! do |method_name|
65
+ method_name[0] == '_' && method_name[-1] == '='
66
+ end
67
+ end
68
+ def method_missing(method_name, *args, &block)
69
+ if method_name[0] == '_'
70
+ if method_name[-1] == '='
71
+ # Assigning an attribute with =
72
+ assign_attribute(method_name, *args, &block)
73
+ else
74
+ read_attribute(method_name)
75
+ end
76
+ else
77
+ # Call method directly on attributes. (since they are
78
+ # not using _ )
79
+ attributes.send(method_name, *args, &block)
80
+ end
81
+ end
82
+
83
+ # Do the assignment to a model and trigger a changed event
84
+ def assign_attribute(method_name, *args, &block)
85
+ self.expand!
86
+ # Assign, without the =
87
+ attribute_name = method_name[0..-2].to_sym
88
+
89
+ value = args[0]
90
+ __assign_element(attribute_name, value)
91
+
92
+ attributes[attribute_name] = wrap_value(value)
93
+ trigger_by_attribute!('changed', attribute_name)
94
+ end
95
+
96
+ # When reading an attribute, we need to handle reading on:
97
+ # 1) a nil model, which returns a wrapped error
98
+ # 2) reading directly from attributes
99
+ # 3) trying to read a key that doesn't exist.
100
+ def read_attribute(method_name)
101
+ # Reading an attribute, we may get back a nil model.
102
+ method_name = method_name.to_sym
103
+
104
+ if attributes == nil
105
+ # The method we are calling is on a nil model, return a wrapped
106
+ # exception.
107
+ return return_undefined_method(method_name)
108
+ elsif attributes && attributes.has_key?(method_name)
109
+ # Method has the key, look it up directly
110
+ return attributes[method_name]
111
+ else
112
+ return new_model(nil, self, method_name)
113
+ end
114
+ end
115
+
116
+ def return_undefined_method(method_name)
117
+ # Methods called on nil capture an error so the user can know where
118
+ # their nil calls are. This error can be re-raised at a later point.
119
+ begin
120
+ raise NilMethodCall.new("undefined method `#{method_name}' for #{self.to_s}")
121
+ rescue => e
122
+ result = e
123
+
124
+ # Cleanup backtrace around ReactiveValue's
125
+ # TODO: this could be better
126
+ result.backtrace.reject! {|line| line['lib/models/model.rb'] || line['lib/models/live_value.rb'] }
127
+ end
128
+ end
129
+
130
+ def new_model(*args)
131
+ Model.new(*args)
132
+ end
133
+
134
+ def trigger_by_attribute!(event_name, attribute, *passed_args)
135
+ trigger_by_scope!(event_name, *passed_args) do |scope|
136
+ method_name, *args, block = scope
137
+
138
+ # TODO: Opal bug
139
+ args ||= []
140
+
141
+ # Any methods without _ are not directly related to one attribute, so
142
+ # they should all trigger
143
+ !method_name || method_name[0] != '_' || (method_name == attribute.to_sym && args.size == 0)
144
+ end
145
+ end
146
+
147
+ # If this model is nil, it makes it into a hash model, then
148
+ # sets it up to track from the parent.
149
+ def expand!
150
+ if attributes.nil?
151
+ self.attributes = {}
152
+ if @parent
153
+ @parent.expand!
154
+
155
+ @parent.attributes[@path] = self
156
+ end
157
+ end
158
+ end
159
+
160
+ tag_method(:<<) do
161
+ destructive!
162
+ pass_reactive!
163
+ end
164
+ # Initialize an empty array and append to it
165
+ def <<(value)
166
+ @parent.expand!
167
+ result = @parent.send(@path)
168
+
169
+ if result.nil?
170
+ # If this isn't a model yet, instantiate it
171
+ @parent.send(:"#{@path}=", ArrayModel.new([], @parent, @path))
172
+ result = @parent.send(@path)
173
+
174
+ # Add the new item
175
+ result << value
176
+ end
177
+
178
+ return result
179
+ end
180
+
181
+ def inspect
182
+ "<#{self.class.to_s}:#{@path} #{attributes.inspect}>"
183
+ end
184
+
185
+
186
+ private
187
+ # Clear the previous value and assign a new one
188
+ def __assign_element(key, value)
189
+ __clear_element(key)
190
+ __track_element(key, value)
191
+ end
192
+
193
+ # TODO: Somewhat duplicated from ReactiveArray
194
+ def __clear_element(key)
195
+ # Cleanup any tracking on an index
196
+ # TODO: is this send a security risk?
197
+ # puts "TRY TO CLEAR: #{key} - #{@reactive_element_listeners && @reactive_element_listeners.keys.inspect}"
198
+ if @reactive_element_listeners && @reactive_element_listeners[key]
199
+ @reactive_element_listeners[key].remove
200
+ @reactive_element_listeners.delete(key)
201
+ end
202
+ end
203
+
204
+ def __track_element(key, value)
205
+ puts "TRACK: #{key} - #{value.inspect}"
206
+ __setup_tracking(key, value) do |event, key, args|
207
+ trigger_by_attribute!(event, key, *args)
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,23 @@
1
+ module ModelWrapper
2
+ # For cretain values, we wrap them to make the behave as a
3
+ # model.
4
+ def wrap_value(value)
5
+ if value.cur.is_a?(Array)
6
+ value = ArrayModel.new(value, self, nil)
7
+ elsif value.cur.is_a?(Hash)
8
+ value = Model.new(value)
9
+ end
10
+
11
+ return value
12
+ end
13
+
14
+ def wrap_values(values)
15
+ if values.cur.is_a?(Array)
16
+ values = values.map {|v| wrap_value(v) }
17
+ elsif values.cur.is_a?(Hash)
18
+ values = Hash[values.map {|k,v| [k, wrap_value(v)] }]
19
+ end
20
+
21
+ return values
22
+ end
23
+ end