volt 0.7.23 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +8 -0
- data/Guardfile +2 -2
- data/Readme.md +139 -136
- data/VERSION +1 -1
- data/app/volt/assets/js/setImmediate.js +175 -0
- data/app/volt/tasks/live_query/data_store.rb +0 -2
- data/app/volt/tasks/live_query/live_query.rb +4 -4
- data/docs/GETTING_STARTED.md +24 -3
- data/docs/WHY.md +1 -22
- data/lib/volt.rb +20 -1
- data/lib/volt/console.rb +20 -0
- data/lib/volt/controllers/model_controller.rb +25 -11
- data/lib/volt/extra_core/object.rb +2 -14
- data/lib/volt/extra_core/string.rb +4 -0
- data/lib/volt/models.rb +0 -1
- data/lib/volt/models/array_model.rb +8 -16
- data/lib/volt/models/cursor.rb +1 -1
- data/lib/volt/models/model.rb +40 -60
- data/lib/volt/models/model_hash_behaviour.rb +10 -24
- data/lib/volt/models/model_helpers.rb +2 -2
- data/lib/volt/models/model_state.rb +1 -1
- data/lib/volt/models/model_wrapper.rb +4 -4
- data/lib/volt/models/persistors/array_store.rb +44 -28
- data/lib/volt/models/persistors/base.rb +1 -1
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/params.rb +5 -1
- data/lib/volt/models/persistors/query/query_listener.rb +2 -0
- data/lib/volt/models/persistors/store.rb +3 -2
- data/lib/volt/models/persistors/store_state.rb +7 -2
- data/lib/volt/models/url.rb +35 -29
- data/lib/volt/models/validations.rb +7 -17
- data/lib/volt/page/bindings/attribute_binding.rb +57 -39
- data/lib/volt/page/bindings/base_binding.rb +0 -14
- data/lib/volt/page/bindings/content_binding.rb +15 -18
- data/lib/volt/page/bindings/each_binding.rb +67 -34
- data/lib/volt/page/bindings/if_binding.rb +15 -12
- data/lib/volt/page/bindings/template_binding.rb +77 -59
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
- data/lib/volt/page/channel.rb +22 -38
- data/lib/volt/page/channel_stub.rb +3 -6
- data/lib/volt/page/page.rb +24 -26
- data/lib/volt/page/string_template_renderer.rb +46 -0
- data/lib/volt/page/sub_context.rb +7 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
- data/lib/volt/page/tasks.rb +3 -2
- data/lib/volt/page/url_tracker.rb +4 -3
- data/lib/volt/reactive/computation.rb +131 -0
- data/lib/volt/reactive/dependency.rb +71 -0
- data/lib/volt/reactive/eventable.rb +82 -0
- data/lib/volt/reactive/hash_dependency.rb +36 -0
- data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
- data/lib/volt/reactive/reactive_array.rb +100 -193
- data/lib/volt/reactive/reactive_hash.rb +49 -0
- data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
- data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
- data/lib/volt/server/html_parser/view_scope.rb +31 -1
- data/spec/apps/kitchen_sink/Gemfile +4 -8
- data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
- data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
- data/spec/controllers/reactive_accessors_spec.rb +13 -15
- data/spec/integration/bindings_spec.rb +159 -0
- data/spec/integration/templates_spec.rb +15 -0
- data/spec/models/model_spec.rb +130 -228
- data/spec/reactive/computation_spec.rb +63 -0
- data/spec/reactive/dependency_spec.rb +5 -0
- data/spec/reactive/eventable_spec.rb +48 -0
- data/spec/reactive/reactive_array_spec.rb +97 -0
- data/spec/router/routes_spec.rb +26 -27
- data/spec/server/html_parser/view_parser_spec.rb +3 -21
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/templates/project/app/main/views/main/main.html +2 -2
- metadata +29 -41
- data/lib/volt/extra_core/time.rb +0 -16
- data/lib/volt/page/draw_cycle.rb +0 -31
- data/lib/volt/page/memory_test.rb +0 -26
- data/lib/volt/page/reactive_template.rb +0 -32
- data/lib/volt/reactive/array_extensions.rb +0 -12
- data/lib/volt/reactive/destructive_methods.rb +0 -19
- data/lib/volt/reactive/event_chain.rb +0 -125
- data/lib/volt/reactive/events.rb +0 -216
- data/lib/volt/reactive/object_tracking.rb +0 -14
- data/lib/volt/reactive/reactive_block.rb +0 -88
- data/lib/volt/reactive/reactive_generator.rb +0 -44
- data/lib/volt/reactive/reactive_tags.rb +0 -71
- data/lib/volt/reactive/reactive_value.rb +0 -427
- data/lib/volt/reactive/string_extensions.rb +0 -31
- data/spec/integration/test_integration_spec.rb +0 -14
- data/spec/models/event_chain_spec.rb +0 -150
- data/spec/models/model_buffers_spec.rb +0 -9
- data/spec/models/old_model_spec.rb +0 -67
- data/spec/models/reactive_array_spec.rb +0 -364
- data/spec/models/reactive_block_spec.rb +0 -13
- data/spec/models/reactive_call_times_spec.rb +0 -28
- data/spec/models/reactive_generator_spec.rb +0 -58
- data/spec/models/reactive_tags_spec.rb +0 -35
- data/spec/models/reactive_value_spec.rb +0 -370
- data/spec/models/store_spec.rb +0 -16
- data/spec/models/string_extensions_spec.rb +0 -57
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
@@ -0,0 +1,175 @@
|
|
1
|
+
(function (global, undefined) {
|
2
|
+
"use strict";
|
3
|
+
|
4
|
+
if (global.setImmediate) {
|
5
|
+
return;
|
6
|
+
}
|
7
|
+
|
8
|
+
var nextHandle = 1; // Spec says greater than zero
|
9
|
+
var tasksByHandle = {};
|
10
|
+
var currentlyRunningATask = false;
|
11
|
+
var doc = global.document;
|
12
|
+
var setImmediate;
|
13
|
+
|
14
|
+
function addFromSetImmediateArguments(args) {
|
15
|
+
tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args);
|
16
|
+
return nextHandle++;
|
17
|
+
}
|
18
|
+
|
19
|
+
// This function accepts the same arguments as setImmediate, but
|
20
|
+
// returns a function that requires no arguments.
|
21
|
+
function partiallyApplied(handler) {
|
22
|
+
var args = [].slice.call(arguments, 1);
|
23
|
+
return function() {
|
24
|
+
if (typeof handler === "function") {
|
25
|
+
handler.apply(undefined, args);
|
26
|
+
} else {
|
27
|
+
(new Function("" + handler))();
|
28
|
+
}
|
29
|
+
};
|
30
|
+
}
|
31
|
+
|
32
|
+
function runIfPresent(handle) {
|
33
|
+
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
|
34
|
+
// So if we're currently running a task, we'll need to delay this invocation.
|
35
|
+
if (currentlyRunningATask) {
|
36
|
+
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
|
37
|
+
// "too much recursion" error.
|
38
|
+
setTimeout(partiallyApplied(runIfPresent, handle), 0);
|
39
|
+
} else {
|
40
|
+
var task = tasksByHandle[handle];
|
41
|
+
if (task) {
|
42
|
+
currentlyRunningATask = true;
|
43
|
+
try {
|
44
|
+
task();
|
45
|
+
} finally {
|
46
|
+
clearImmediate(handle);
|
47
|
+
currentlyRunningATask = false;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
function clearImmediate(handle) {
|
54
|
+
delete tasksByHandle[handle];
|
55
|
+
}
|
56
|
+
|
57
|
+
function installNextTickImplementation() {
|
58
|
+
setImmediate = function() {
|
59
|
+
var handle = addFromSetImmediateArguments(arguments);
|
60
|
+
process.nextTick(partiallyApplied(runIfPresent, handle));
|
61
|
+
return handle;
|
62
|
+
};
|
63
|
+
}
|
64
|
+
|
65
|
+
function canUsePostMessage() {
|
66
|
+
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
|
67
|
+
// where `global.postMessage` means something completely different and can't be used for this purpose.
|
68
|
+
if (global.postMessage && !global.importScripts) {
|
69
|
+
var postMessageIsAsynchronous = true;
|
70
|
+
var oldOnMessage = global.onmessage;
|
71
|
+
global.onmessage = function() {
|
72
|
+
postMessageIsAsynchronous = false;
|
73
|
+
};
|
74
|
+
global.postMessage("", "*");
|
75
|
+
global.onmessage = oldOnMessage;
|
76
|
+
return postMessageIsAsynchronous;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
function installPostMessageImplementation() {
|
81
|
+
// Installs an event handler on `global` for the `message` event: see
|
82
|
+
// * https://developer.mozilla.org/en/DOM/window.postMessage
|
83
|
+
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
|
84
|
+
|
85
|
+
var messagePrefix = "setImmediate$" + Math.random() + "$";
|
86
|
+
var onGlobalMessage = function(event) {
|
87
|
+
if (event.source === global &&
|
88
|
+
typeof event.data === "string" &&
|
89
|
+
event.data.indexOf(messagePrefix) === 0) {
|
90
|
+
runIfPresent(+event.data.slice(messagePrefix.length));
|
91
|
+
}
|
92
|
+
};
|
93
|
+
|
94
|
+
if (global.addEventListener) {
|
95
|
+
global.addEventListener("message", onGlobalMessage, false);
|
96
|
+
} else {
|
97
|
+
global.attachEvent("onmessage", onGlobalMessage);
|
98
|
+
}
|
99
|
+
|
100
|
+
setImmediate = function() {
|
101
|
+
var handle = addFromSetImmediateArguments(arguments);
|
102
|
+
global.postMessage(messagePrefix + handle, "*");
|
103
|
+
return handle;
|
104
|
+
};
|
105
|
+
}
|
106
|
+
|
107
|
+
function installMessageChannelImplementation() {
|
108
|
+
var channel = new MessageChannel();
|
109
|
+
channel.port1.onmessage = function(event) {
|
110
|
+
var handle = event.data;
|
111
|
+
runIfPresent(handle);
|
112
|
+
};
|
113
|
+
|
114
|
+
setImmediate = function() {
|
115
|
+
var handle = addFromSetImmediateArguments(arguments);
|
116
|
+
channel.port2.postMessage(handle);
|
117
|
+
return handle;
|
118
|
+
};
|
119
|
+
}
|
120
|
+
|
121
|
+
function installReadyStateChangeImplementation() {
|
122
|
+
var html = doc.documentElement;
|
123
|
+
setImmediate = function() {
|
124
|
+
var handle = addFromSetImmediateArguments(arguments);
|
125
|
+
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
|
126
|
+
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
|
127
|
+
var script = doc.createElement("script");
|
128
|
+
script.onreadystatechange = function () {
|
129
|
+
runIfPresent(handle);
|
130
|
+
script.onreadystatechange = null;
|
131
|
+
html.removeChild(script);
|
132
|
+
script = null;
|
133
|
+
};
|
134
|
+
html.appendChild(script);
|
135
|
+
return handle;
|
136
|
+
};
|
137
|
+
}
|
138
|
+
|
139
|
+
function installSetTimeoutImplementation() {
|
140
|
+
setImmediate = function() {
|
141
|
+
var handle = addFromSetImmediateArguments(arguments);
|
142
|
+
setTimeout(partiallyApplied(runIfPresent, handle), 0);
|
143
|
+
return handle;
|
144
|
+
};
|
145
|
+
}
|
146
|
+
|
147
|
+
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
|
148
|
+
var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
|
149
|
+
attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
|
150
|
+
|
151
|
+
// Don't get fooled by e.g. browserify environments.
|
152
|
+
if ({}.toString.call(global.process) === "[object process]") {
|
153
|
+
// For Node.js before 0.9
|
154
|
+
installNextTickImplementation();
|
155
|
+
|
156
|
+
} else if (canUsePostMessage()) {
|
157
|
+
// For non-IE10 modern browsers
|
158
|
+
installPostMessageImplementation();
|
159
|
+
|
160
|
+
} else if (global.MessageChannel) {
|
161
|
+
// For web workers, where supported
|
162
|
+
installMessageChannelImplementation();
|
163
|
+
|
164
|
+
} else if (doc && "onreadystatechange" in doc.createElement("script")) {
|
165
|
+
// For IE 6–8
|
166
|
+
installReadyStateChangeImplementation();
|
167
|
+
|
168
|
+
} else {
|
169
|
+
// For older browsers
|
170
|
+
installSetTimeoutImplementation();
|
171
|
+
}
|
172
|
+
|
173
|
+
attachTo.setImmediate = setImmediate;
|
174
|
+
attachTo.clearImmediate = clearImmediate;
|
175
|
+
}(new Function("return this")()));
|
@@ -23,29 +23,29 @@ class LiveQuery
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def notify_removed(ids, skip_channel)
|
26
|
+
# puts "Removed: #{ids.inspect}"
|
26
27
|
notify! do |channel|
|
27
|
-
# puts "Removed: #{ids.inspect} to #{channel.inspect}"
|
28
28
|
channel.send_message("removed", nil, @collection, @query, ids)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def notify_added(index, data, skip_channel)
|
33
|
+
# puts "Added: #{index} - #{data.inspect}"
|
33
34
|
notify! do |channel|
|
34
|
-
# puts "Added: #{index} - #{data.inspect} to #{channel.inspect}"
|
35
35
|
channel.send_message("added", nil, @collection, @query, index, data)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
def notify_moved(id, new_position, skip_channel)
|
40
|
+
# puts "Moved: #{id}, #{new_position}"
|
40
41
|
notify! do |channel|
|
41
|
-
# puts "Moved: #{id}, #{new_position} to #{channel.inspect}"
|
42
42
|
channel.send_message("moved", nil, @collection, @query, id, new_position)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
def notify_changed(id, data, skip_channel)
|
47
|
+
# puts "Changed: #{id}, #{data}"
|
47
48
|
notify!(skip_channel) do |channel|
|
48
|
-
# puts "Changed: #{id}, #{data} to #{channel.inspect}"
|
49
49
|
channel.send_message("changed", nil, @collection, @query, id, data)
|
50
50
|
end
|
51
51
|
end
|
data/docs/GETTING_STARTED.md
CHANGED
@@ -1,7 +1,28 @@
|
|
1
1
|
# Getting Started
|
2
2
|
|
3
|
-
Volt relies on a few concepts to take make web development faster and easier. The first of these is reactive programming. Data on the front and back end is stored in models. Instead of manually updating a page when the data changes, the page is coded using a
|
3
|
+
Volt relies on a few concepts to take make web development faster and easier. The first of these is reactive programming. Data on the front and back end is stored in models. Instead of manually updating a page when the data changes, the page is coded using a templating language which automatically updates when the data changes.
|
4
4
|
|
5
|
-
##
|
5
|
+
## Bindings and Models
|
6
6
|
|
7
|
-
|
7
|
+
This automaic updating is done via bindings and models. In Volt app's all data is stored in a model. From your html, you can bind things like attributes and text to a value in a model.
|
8
|
+
|
9
|
+
### Name Example
|
10
|
+
|
11
|
+
```html
|
12
|
+
<label>Name:</label>
|
13
|
+
<input type="text" value="{page._name}" />
|
14
|
+
<p>Hello {page._name}</p>
|
15
|
+
```
|
16
|
+
|
17
|
+
In the example above, our model is called page (more about page later). Any time a user changes the value of the field, page._name will be updated to the fields value. When page._name is changed, the fields value changes. Also when ```page._name``` changes, the page will show the text "Hello ..." where ... is the value of page._name. These "two-way bindings" help us eliminiate a lot of code by keeping all of our application state in our models. Data displayed in a view is always computed live from the data in the models.
|
18
|
+
|
19
|
+
### Meal Cost Splitter Example
|
20
|
+
|
21
|
+
```html
|
22
|
+
<label>Cost:</label><input type="text" value="{page._cost}" /><br />
|
23
|
+
<label>People:</label><input type="text" value="{page._people}" /><br />
|
24
|
+
<p>Cost Per Person: {page._cost.to_f / page._people.to_f}</p>
|
25
|
+
```
|
26
|
+
In this example, a user can enter a cost and a number of people. When either changes, the Cost Per Person will update.
|
27
|
+
|
28
|
+
###
|
data/docs/WHY.md
CHANGED
@@ -18,30 +18,9 @@ Data in volt is reactive by default. Changes to the data is automatically updat
|
|
18
18
|
A lot of modern web development is moving data between the front-end to the back-end. Volt eliminates all of that work. Model's on the front-end automatically sync to the back-end, and vice versa. Validations are run on both sides for security. Models on the front-end are automatically updated whenever they are changed anywhere else (another browser, a background task, etc..)
|
19
19
|
|
20
20
|
|
21
|
-
## Speed
|
22
|
-
|
23
|
-
Volt's reactive objects contain extra data about how to propigate events. Things that don't need to be updated when data changes aren't. Volt uses an intellegent managed draw cycle to do efficient DOM updates. Only the changed part of the page is re-rendered.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
21
|
# Why Volt is Awesome
|
29
22
|
|
30
|
-
- only the relevant DOM is updated. There is no match and patch algorithm to update from strings like other frameworks, all associations are tracked through our reactive
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
23
|
+
- only the relevant DOM is updated. There is no match and patch algorithm to update from strings like other frameworks, all associations are tracked through our reactive core, so we know exactly what needs to be updated without the need to generate any extra HTML. This has a few advantages, namely that things like input fields are retained, so any properties (focus, tab position, etc...) are also retained.
|
45
24
|
|
46
25
|
|
47
26
|
# Why Ruby
|
data/lib/volt.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
require 'volt/volt/environment'
|
2
2
|
require 'volt/extra_core/extra_core'
|
3
|
-
require 'volt/reactive/
|
3
|
+
require 'volt/reactive/computation'
|
4
|
+
require 'volt/reactive/dependency'
|
4
5
|
|
5
6
|
class Volt
|
7
|
+
if RUBY_PLATFORM == 'opal'
|
8
|
+
@@in_browser = `!!document && !window.OPAL_SPEC_PHANTOM`
|
9
|
+
else
|
10
|
+
@@in_browser = false
|
11
|
+
end
|
12
|
+
|
6
13
|
def self.root
|
7
14
|
@root ||= File.expand_path(Dir.pwd)
|
8
15
|
end
|
@@ -26,4 +33,16 @@ class Volt
|
|
26
33
|
def self.env
|
27
34
|
@env ||= Volt::Environment.new
|
28
35
|
end
|
36
|
+
|
37
|
+
def self.logger
|
38
|
+
@logger ||= Logger.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.logger=(val)
|
42
|
+
@logger = val
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.in_browser?
|
46
|
+
@@in_browser
|
47
|
+
end
|
29
48
|
end
|
data/lib/volt/console.rb
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
class Pry
|
4
|
+
# To make the console more useful, we make it so we flush the event registry
|
5
|
+
# after each line. This makes it so events are triggered after each line.
|
6
|
+
# To accomplish this we monkey-patch pry.
|
7
|
+
def rep(target=TOPLEVEL_BINDING)
|
8
|
+
target = Pry.binding_for(target)
|
9
|
+
result = re(target)
|
10
|
+
|
11
|
+
Pry.critical_section do
|
12
|
+
show_result(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Automatically flush after each line
|
16
|
+
Computation.flush!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
1
21
|
class Console
|
2
22
|
def self.start
|
3
23
|
require 'pry'
|
@@ -1,8 +1,10 @@
|
|
1
|
-
require 'volt/
|
1
|
+
require 'volt/reactive/reactive_accessors'
|
2
2
|
|
3
3
|
class ModelController
|
4
4
|
include ReactiveAccessors
|
5
5
|
|
6
|
+
reactive_accessor :current_model
|
7
|
+
|
6
8
|
def self.model(val)
|
7
9
|
@default_model = val
|
8
10
|
end
|
@@ -10,24 +12,31 @@ class ModelController
|
|
10
12
|
# Sets the current model on this controller
|
11
13
|
def model=(val)
|
12
14
|
# Start with a nil reactive value.
|
13
|
-
|
15
|
+
self.current_model ||= Model.new
|
14
16
|
|
15
17
|
if Symbol === val || String === val
|
16
18
|
collections = [:page, :store, :params, :controller]
|
17
19
|
if collections.include?(val.to_sym)
|
18
|
-
|
20
|
+
self.current_model = self.send(val)
|
19
21
|
else
|
20
22
|
raise "#{val} is not the name of a valid model, choose from: #{collections.join(', ')}"
|
21
23
|
end
|
22
24
|
elsif val
|
23
|
-
|
25
|
+
self.current_model = val
|
24
26
|
else
|
25
27
|
raise "model can not be #{val.inspect}"
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
31
|
def model
|
30
|
-
|
32
|
+
model = self.current_model
|
33
|
+
|
34
|
+
# If the model is a proc, call it now
|
35
|
+
if model.is_a?(Proc)
|
36
|
+
model = model.call
|
37
|
+
end
|
38
|
+
|
39
|
+
return model
|
31
40
|
end
|
32
41
|
|
33
42
|
def self.new(*args, &block)
|
@@ -40,11 +49,16 @@ class ModelController
|
|
40
49
|
return inst
|
41
50
|
end
|
42
51
|
|
52
|
+
attr_accessor :attrs
|
53
|
+
|
43
54
|
def initialize(*args)
|
44
|
-
|
45
|
-
|
46
|
-
args[0]
|
47
|
-
|
55
|
+
if args[0]
|
56
|
+
# Assign the first passed in argument to attrs
|
57
|
+
self.attrs = args[0]
|
58
|
+
|
59
|
+
# If a model attribute is passed in, we assign it directly
|
60
|
+
if attrs.respond_to?(:model)
|
61
|
+
self.model = attrs.locals[:model]
|
48
62
|
end
|
49
63
|
end
|
50
64
|
end
|
@@ -87,10 +101,10 @@ class ModelController
|
|
87
101
|
end
|
88
102
|
|
89
103
|
def controller
|
90
|
-
@controller ||=
|
104
|
+
@controller ||= Model.new
|
91
105
|
end
|
92
106
|
|
93
107
|
def method_missing(method_name, *args, &block)
|
94
|
-
return
|
108
|
+
return model.send(method_name, *args, &block)
|
95
109
|
end
|
96
110
|
end
|
@@ -9,7 +9,7 @@ class Object
|
|
9
9
|
# Provides the same functionality as ||, but since ReactiveValue's only
|
10
10
|
# work with method calls, we provide .or as a convience.
|
11
11
|
def or(other)
|
12
|
-
if self.
|
12
|
+
if self && !self.nil?
|
13
13
|
return self
|
14
14
|
else
|
15
15
|
return other
|
@@ -19,25 +19,13 @@ class Object
|
|
19
19
|
# Provides the same functionality as &&, but since ReactiveValue's only
|
20
20
|
# work with method calls, we provide .and as a convience
|
21
21
|
def and(other)
|
22
|
-
if self.
|
22
|
+
if self && !self.nil?
|
23
23
|
return other
|
24
24
|
else
|
25
25
|
return self
|
26
26
|
end
|
27
27
|
end
|
28
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
|
-
|
37
|
-
def deep_cur
|
38
|
-
self.cur
|
39
|
-
end
|
40
|
-
|
41
29
|
def html_inspect
|
42
30
|
inspect.gsub('<', '<').gsub('>', '>')
|
43
31
|
end
|