volt 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Readme.md +97 -56
- data/VERSION +1 -1
- data/app/volt/assets/js/sockjs-0.3.4.min.js +27 -0
- data/app/volt/assets/js/vertxbus.js +216 -0
- data/app/volt/tasks/live_query/live_query.rb +5 -5
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +5 -0
- data/app/volt/tasks/store_tasks.rb +44 -18
- data/docs/WHY.md +10 -0
- data/lib/volt/cli.rb +18 -7
- data/lib/volt/controllers/model_controller.rb +30 -8
- data/lib/volt/extra_core/inflections.rb +63 -0
- data/lib/volt/extra_core/inflector/inflections.rb +203 -0
- data/lib/volt/extra_core/inflector/methods.rb +63 -0
- data/lib/volt/extra_core/inflector.rb +4 -0
- data/lib/volt/extra_core/object.rb +9 -0
- data/lib/volt/extra_core/string.rb +10 -14
- data/lib/volt/models/array_model.rb +45 -27
- data/lib/volt/models/cursor.rb +6 -0
- data/lib/volt/models/model.rb +127 -12
- data/lib/volt/models/model_hash_behaviour.rb +8 -5
- data/lib/volt/models/model_helpers.rb +4 -4
- data/lib/volt/models/model_state.rb +22 -0
- data/lib/volt/models/persistors/array_store.rb +49 -35
- data/lib/volt/models/persistors/base.rb +3 -3
- data/lib/volt/models/persistors/model_store.rb +17 -6
- data/lib/volt/models/persistors/query/query_listener.rb +0 -2
- data/lib/volt/models/persistors/store.rb +0 -4
- data/lib/volt/models/persistors/store_state.rb +27 -0
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validations/errors.rb +0 -0
- data/lib/volt/models/validations/length.rb +13 -0
- data/lib/volt/models/validations/validations.rb +82 -0
- data/lib/volt/models.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +29 -14
- data/lib/volt/page/bindings/base_binding.rb +2 -2
- data/lib/volt/page/bindings/component_binding.rb +29 -25
- data/lib/volt/page/bindings/content_binding.rb +1 -0
- data/lib/volt/page/bindings/each_binding.rb +25 -33
- data/lib/volt/page/bindings/event_binding.rb +0 -1
- data/lib/volt/page/bindings/if_binding.rb +3 -1
- data/lib/volt/page/bindings/template_binding.rb +61 -28
- data/lib/volt/page/document_events.rb +3 -1
- data/lib/volt/page/draw_cycle.rb +22 -0
- data/lib/volt/page/page.rb +10 -1
- data/lib/volt/page/reactive_template.rb +23 -16
- data/lib/volt/page/sub_context.rb +1 -1
- data/lib/volt/page/targets/attribute_section.rb +3 -2
- data/lib/volt/page/targets/attribute_target.rb +0 -4
- data/lib/volt/page/targets/base_section.rb +25 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +13 -14
- data/lib/volt/page/targets/binding_document/html_node.rb +4 -0
- data/lib/volt/page/targets/dom_section.rb +16 -67
- data/lib/volt/page/targets/dom_template.rb +99 -0
- data/lib/volt/page/targets/helpers/comment_searchers.rb +29 -0
- data/lib/volt/page/template_renderer.rb +2 -14
- data/lib/volt/reactive/array_extensions.rb +0 -1
- data/lib/volt/reactive/event_chain.rb +9 -2
- data/lib/volt/reactive/events.rb +44 -37
- data/lib/volt/reactive/object_tracking.rb +1 -1
- data/lib/volt/reactive/reactive_array.rb +18 -0
- data/lib/volt/reactive/reactive_count.rb +108 -0
- data/lib/volt/reactive/reactive_generator.rb +44 -0
- data/lib/volt/reactive/reactive_value.rb +73 -73
- data/lib/volt/reactive/string_extensions.rb +1 -1
- data/lib/volt/router/routes.rb +205 -88
- data/lib/volt/server/component_handler.rb +3 -1
- data/lib/volt/server/html_parser/view_parser.rb +20 -4
- data/lib/volt/server/rack/component_paths.rb +13 -10
- data/lib/volt/server/rack/index_files.rb +4 -4
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server.rb +10 -3
- data/spec/apps/kitchen_sink/.gitignore +8 -0
- data/spec/apps/kitchen_sink/Gemfile +32 -0
- data/spec/apps/kitchen_sink/app/home/views/index/index.html +3 -5
- data/spec/apps/kitchen_sink/config.ru +4 -0
- data/spec/apps/kitchen_sink/public/index.html +2 -2
- data/spec/extra_core/inflector_spec.rb +8 -0
- data/spec/models/event_chain_spec.rb +18 -0
- data/spec/models/model_buffers_spec.rb +9 -0
- data/spec/models/model_spec.rb +22 -9
- data/spec/models/reactive_array_spec.rb +26 -1
- data/spec/models/reactive_call_times_spec.rb +28 -0
- data/spec/models/reactive_value_spec.rb +19 -0
- data/spec/models/validations_spec.rb +39 -0
- data/spec/page/bindings/content_binding_spec.rb +1 -0
- data/spec/{templates → page/bindings}/template_binding_spec.rb +54 -0
- data/spec/router/routes_spec.rb +156 -8
- data/spec/server/html_parser/sandlebars_parser_spec.rb +55 -47
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -11
- data/spec/templates/targets/binding_document/component_node_spec.rb +12 -0
- data/templates/project/Gemfile.tt +11 -0
- data/templates/project/app/home/config/routes.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +5 -5
- data/templates/project/app/home/views/index/index.html +6 -6
- data/volt.gemspec +5 -6
- metadata +34 -76
- data/app/volt/assets/js/sockjs-0.2.1.min.js +0 -27
- data/lib/volt/reactive/object_tracker.rb +0 -107
@@ -0,0 +1,216 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2011-2012 the original author or authors.
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
var vertx = vertx || {};
|
18
|
+
|
19
|
+
!function(factory) {
|
20
|
+
if (typeof define === "function" && define.amd) {
|
21
|
+
// Expose as an AMD module with SockJS dependency.
|
22
|
+
// "vertxbus" and "sockjs" names are used because
|
23
|
+
// AMD module names are derived from file names.
|
24
|
+
define("vertxbus", ["sockjs"], factory);
|
25
|
+
} else {
|
26
|
+
// No AMD-compliant loader
|
27
|
+
factory(SockJS);
|
28
|
+
}
|
29
|
+
}(function(SockJS) {
|
30
|
+
|
31
|
+
vertx.EventBus = function(url, options) {
|
32
|
+
|
33
|
+
var that = this;
|
34
|
+
var sockJSConn = new SockJS(url, undefined, options);
|
35
|
+
var handlerMap = {};
|
36
|
+
var replyHandlers = {};
|
37
|
+
var state = vertx.EventBus.CONNECTING;
|
38
|
+
var sessionID = null;
|
39
|
+
var pingTimerID = null;
|
40
|
+
|
41
|
+
that.onopen = null;
|
42
|
+
that.onclose = null;
|
43
|
+
|
44
|
+
that.login = function(username, password, replyHandler) {
|
45
|
+
sendOrPub("send", 'vertx.basicauthmanager.login', {username: username, password: password}, function(reply) {
|
46
|
+
if (reply.status === 'ok') {
|
47
|
+
that.sessionID = reply.sessionID;
|
48
|
+
}
|
49
|
+
if (replyHandler) {
|
50
|
+
delete reply.sessionID;
|
51
|
+
replyHandler(reply)
|
52
|
+
}
|
53
|
+
});
|
54
|
+
}
|
55
|
+
|
56
|
+
that.send = function(address, message, replyHandler) {
|
57
|
+
sendOrPub("send", address, message, replyHandler)
|
58
|
+
}
|
59
|
+
|
60
|
+
that.publish = function(address, message, replyHandler) {
|
61
|
+
sendOrPub("publish", address, message, replyHandler)
|
62
|
+
}
|
63
|
+
|
64
|
+
that.registerHandler = function(address, handler) {
|
65
|
+
checkSpecified("address", 'string', address);
|
66
|
+
checkSpecified("handler", 'function', handler);
|
67
|
+
checkOpen();
|
68
|
+
var handlers = handlerMap[address];
|
69
|
+
if (!handlers) {
|
70
|
+
handlers = [handler];
|
71
|
+
handlerMap[address] = handlers;
|
72
|
+
// First handler for this address so we should register the connection
|
73
|
+
var msg = { type : "register",
|
74
|
+
address: address };
|
75
|
+
sockJSConn.send(JSON.stringify(msg));
|
76
|
+
} else {
|
77
|
+
handlers[handlers.length] = handler;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
that.unregisterHandler = function(address, handler) {
|
82
|
+
checkSpecified("address", 'string', address);
|
83
|
+
checkSpecified("handler", 'function', handler);
|
84
|
+
checkOpen();
|
85
|
+
var handlers = handlerMap[address];
|
86
|
+
if (handlers) {
|
87
|
+
var idx = handlers.indexOf(handler);
|
88
|
+
if (idx != -1) handlers.splice(idx, 1);
|
89
|
+
if (handlers.length == 0) {
|
90
|
+
// No more local handlers so we should unregister the connection
|
91
|
+
|
92
|
+
var msg = { type : "unregister",
|
93
|
+
address: address};
|
94
|
+
sockJSConn.send(JSON.stringify(msg));
|
95
|
+
delete handlerMap[address];
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
that.close = function() {
|
101
|
+
checkOpen();
|
102
|
+
if (pingTimerID) clearInterval(pingTimerID);
|
103
|
+
state = vertx.EventBus.CLOSING;
|
104
|
+
sockJSConn.close();
|
105
|
+
}
|
106
|
+
|
107
|
+
that.readyState = function() {
|
108
|
+
return state;
|
109
|
+
}
|
110
|
+
|
111
|
+
sockJSConn.onopen = function() {
|
112
|
+
// Send the first ping then send a ping every 5 seconds
|
113
|
+
sendPing();
|
114
|
+
pingTimerID = setInterval(sendPing, 5000);
|
115
|
+
state = vertx.EventBus.OPEN;
|
116
|
+
if (that.onopen) {
|
117
|
+
that.onopen();
|
118
|
+
}
|
119
|
+
};
|
120
|
+
|
121
|
+
sockJSConn.onclose = function() {
|
122
|
+
state = vertx.EventBus.CLOSED;
|
123
|
+
if (that.onclose) {
|
124
|
+
that.onclose();
|
125
|
+
}
|
126
|
+
};
|
127
|
+
|
128
|
+
sockJSConn.onmessage = function(e) {
|
129
|
+
var msg = e.data;
|
130
|
+
var json = JSON.parse(msg);
|
131
|
+
var body = json.body;
|
132
|
+
var replyAddress = json.replyAddress;
|
133
|
+
var address = json.address;
|
134
|
+
var replyHandler;
|
135
|
+
if (replyAddress) {
|
136
|
+
replyHandler = function(reply, replyHandler) {
|
137
|
+
// Send back reply
|
138
|
+
that.send(replyAddress, reply, replyHandler);
|
139
|
+
};
|
140
|
+
}
|
141
|
+
var handlers = handlerMap[address];
|
142
|
+
if (handlers) {
|
143
|
+
// We make a copy since the handler might get unregistered from within the
|
144
|
+
// handler itself, which would screw up our iteration
|
145
|
+
var copy = handlers.slice(0);
|
146
|
+
for (var i = 0; i < copy.length; i++) {
|
147
|
+
copy[i](body, replyHandler);
|
148
|
+
}
|
149
|
+
} else {
|
150
|
+
// Might be a reply message
|
151
|
+
var handler = replyHandlers[address];
|
152
|
+
if (handler) {
|
153
|
+
delete replyHandlers[address];
|
154
|
+
handler(body, replyHandler);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
function sendPing() {
|
160
|
+
var msg = {
|
161
|
+
type: "ping"
|
162
|
+
}
|
163
|
+
sockJSConn.send(JSON.stringify(msg));
|
164
|
+
}
|
165
|
+
|
166
|
+
function sendOrPub(sendOrPub, address, message, replyHandler) {
|
167
|
+
checkSpecified("address", 'string', address);
|
168
|
+
checkSpecified("replyHandler", 'function', replyHandler, true);
|
169
|
+
checkOpen();
|
170
|
+
var envelope = { type : sendOrPub,
|
171
|
+
address: address,
|
172
|
+
body: message };
|
173
|
+
if (that.sessionID) {
|
174
|
+
envelope.sessionID = that.sessionID;
|
175
|
+
}
|
176
|
+
if (replyHandler) {
|
177
|
+
var replyAddress = makeUUID();
|
178
|
+
envelope.replyAddress = replyAddress;
|
179
|
+
replyHandlers[replyAddress] = replyHandler;
|
180
|
+
}
|
181
|
+
var str = JSON.stringify(envelope);
|
182
|
+
sockJSConn.send(str);
|
183
|
+
}
|
184
|
+
|
185
|
+
function checkOpen() {
|
186
|
+
if (state != vertx.EventBus.OPEN) {
|
187
|
+
throw new Error('INVALID_STATE_ERR');
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
function checkSpecified(paramName, paramType, param, optional) {
|
192
|
+
if (!optional && !param) {
|
193
|
+
throw new Error("Parameter " + paramName + " must be specified");
|
194
|
+
}
|
195
|
+
if (param && typeof param != paramType) {
|
196
|
+
throw new Error("Parameter " + paramName + " must be of type " + paramType);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
function isFunction(obj) {
|
201
|
+
return !!(obj && obj.constructor && obj.call && obj.apply);
|
202
|
+
}
|
203
|
+
|
204
|
+
function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
205
|
+
.replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
|
206
|
+
|
207
|
+
}
|
208
|
+
|
209
|
+
vertx.EventBus.CONNECTING = 0;
|
210
|
+
vertx.EventBus.OPEN = 1;
|
211
|
+
vertx.EventBus.CLOSING = 2;
|
212
|
+
vertx.EventBus.CLOSED = 3;
|
213
|
+
|
214
|
+
return vertx.EventBus;
|
215
|
+
|
216
|
+
});
|
@@ -24,28 +24,28 @@ class LiveQuery
|
|
24
24
|
|
25
25
|
def notify_removed(ids, skip_channel)
|
26
26
|
notify! do |channel|
|
27
|
-
|
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
|
-
notify!
|
34
|
-
|
33
|
+
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
40
|
notify! do |channel|
|
41
|
-
|
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
47
|
notify!(skip_channel) do |channel|
|
48
|
-
|
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
|
@@ -15,7 +15,7 @@ class LiveQueryPool < GenericPool
|
|
15
15
|
|
16
16
|
def updated_collection(collection, skip_channel)
|
17
17
|
lookup_all(collection).each do |live_query|
|
18
|
-
|
18
|
+
puts "RUN ON: #{live_query} with #{live_query.instance_variable_get('@channels').inspect}"
|
19
19
|
live_query.run(skip_channel)
|
20
20
|
end
|
21
21
|
end
|
@@ -19,12 +19,17 @@ class QueryTasks
|
|
19
19
|
live_query = @@live_query_pool.lookup(collection, query)
|
20
20
|
track_channel_in_live_query(live_query)
|
21
21
|
|
22
|
+
puts "Load data on #{collection.inspect} - #{query.inspect}"
|
22
23
|
live_query.add_channel(@channel)
|
23
24
|
|
24
25
|
# Return the initial data
|
25
26
|
return live_query.initial_data
|
26
27
|
end
|
27
28
|
|
29
|
+
def initial_data
|
30
|
+
return live_query.initial_data
|
31
|
+
end
|
32
|
+
|
28
33
|
# Remove a listening channel, the LiveQuery will automatically remove
|
29
34
|
# itsself from the pool when there are no channels.
|
30
35
|
def remove_listener(collection, query)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mongo'
|
2
|
+
require 'query_tasks'
|
2
3
|
|
3
4
|
class StoreTasks
|
4
5
|
def initialize(channel=nil, dispatcher=nil)
|
@@ -13,29 +14,54 @@ class StoreTasks
|
|
13
14
|
@@db
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
|
17
|
+
def model_errors(collection, data)
|
18
|
+
model_name = collection[1..-1].singularize.camelize
|
19
|
+
|
20
|
+
# TODO: Security check to make sure we have a valid model
|
21
|
+
begin
|
22
|
+
model_class = Object.send(:const_get, model_name)
|
23
|
+
rescue NameError => e
|
24
|
+
model_class = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
if model_class
|
28
|
+
return model_class.new(data).errors
|
29
|
+
end
|
30
|
+
|
31
|
+
return {}
|
32
|
+
end
|
18
33
|
|
34
|
+
def save(collection, data)
|
35
|
+
puts "Insert: #{data.inspect} on #{collection.inspect}"
|
19
36
|
data = data.symbolize_keys
|
20
|
-
id = data[:_id]
|
21
37
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
38
|
+
errors = model_errors(collection, data)
|
39
|
+
|
40
|
+
if errors.size == 0
|
41
|
+
id = data[:_id]
|
42
|
+
|
43
|
+
# Try to create
|
44
|
+
# TODO: Seems mongo is dumb and doesn't let you upsert with custom id's
|
45
|
+
begin
|
46
|
+
@@db[collection].insert(data)
|
47
|
+
rescue Mongo::OperationFailure => error
|
48
|
+
# Really mongo client?
|
49
|
+
if error.message[/^11000[:]/]
|
50
|
+
# Update because the id already exists
|
51
|
+
update_data = data.dup
|
52
|
+
update_data.delete(:_id)
|
53
|
+
@@db[collection].update({:_id => id}, update_data)
|
54
|
+
else
|
55
|
+
raise
|
56
|
+
end
|
35
57
|
end
|
36
|
-
end
|
37
58
|
|
38
|
-
|
59
|
+
puts "SAVE: #{@channel.inspect}"
|
60
|
+
QueryTasks.live_query_pool.updated_collection(collection, @channel)
|
61
|
+
return {}
|
62
|
+
else
|
63
|
+
return errors
|
64
|
+
end
|
39
65
|
end
|
40
66
|
|
41
67
|
def delete(collection, id)
|
data/docs/WHY.md
CHANGED
@@ -25,6 +25,14 @@ Volt's reactive objects contain extra data about how to propigate events. Thing
|
|
25
25
|
|
26
26
|
|
27
27
|
|
28
|
+
# Why Volt is Awesome
|
29
|
+
|
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 values, 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.
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
28
36
|
|
29
37
|
|
30
38
|
|
@@ -38,6 +46,8 @@ Volt's reactive objects contain extra data about how to propigate events. Thing
|
|
38
46
|
|
39
47
|
# Why Ruby
|
40
48
|
|
49
|
+
Isomorphic type system with javascript
|
50
|
+
|
41
51
|
In web development today, JavaScript gets to be the default language by virtue of being in the browser. JavaScript is a very good language, but it has a lot of warts. (See http://wtfjs.com/ for some great examples) Some of these can introduce bugs, others are just difficult to deal with. JavaScript was rushed to market quickly and standardized very quickly. Ruby was used by a small community for years while most of the kinks were worked out. Ruby also has some great concepts such as [uniform access](http://en.wikipedia.org/wiki/Uniform_access_principle), [mixin's](http://en.wikipedia.org/wiki/Mixin), [duck typing](http://en.wikipedia.org/wiki/Duck_typing), and [blocks](http://yehudakatz.com/2012/01/10/javascript-needs-blocks/) to name a few. While many of these features can be implemented in JavaScript in userland, few are standardardized and the solutions are seldom eloquent.
|
42
52
|
|
43
53
|
[5,10,1].sort()
|
data/lib/volt/cli.rb
CHANGED
@@ -23,7 +23,12 @@ class CLI < Thor
|
|
23
23
|
desc "server", "run the server on the project in the current directory"
|
24
24
|
method_option :port, :type => :string, :aliases => '-p', :banner => 'specify which port the server should run on'
|
25
25
|
def server
|
26
|
-
|
26
|
+
if RUBY_PLATFORM == 'java'
|
27
|
+
require 'volt/server'
|
28
|
+
else
|
29
|
+
require 'thin'
|
30
|
+
end
|
31
|
+
|
27
32
|
require 'fileutils'
|
28
33
|
|
29
34
|
# If we're in a Volt project, clear the temp directory
|
@@ -36,14 +41,20 @@ class CLI < Thor
|
|
36
41
|
return
|
37
42
|
end
|
38
43
|
|
39
|
-
|
40
|
-
|
44
|
+
if RUBY_PLATFORM == 'java'
|
45
|
+
server = Server.new.app
|
46
|
+
Rack::Handler::Jubilee.run(server)
|
47
|
+
Thread.stop
|
48
|
+
else
|
49
|
+
ENV['SERVER'] = 'true'
|
50
|
+
args = ['start', '--threaded', '--max-persistent-conns', '300', "--max-conns", "400"]
|
41
51
|
|
42
|
-
|
43
|
-
|
44
|
-
|
52
|
+
if options[:port]
|
53
|
+
args += ['-p', options[:port].to_s]
|
54
|
+
end
|
45
55
|
|
46
|
-
|
56
|
+
Thin::Runner.new(args).run!
|
57
|
+
end
|
47
58
|
|
48
59
|
# require 'volt/server'
|
49
60
|
#
|
@@ -1,35 +1,57 @@
|
|
1
1
|
class ModelController
|
2
2
|
def self.model(val)
|
3
|
-
|
3
|
+
@default_model = val
|
4
4
|
end
|
5
5
|
|
6
6
|
# Sets the current model on this controller
|
7
|
-
def model(val)
|
7
|
+
def model=(val)
|
8
|
+
# Start with a nil reactive value.
|
9
|
+
@model ||= ReactiveValue.new(Proc.new { nil })
|
10
|
+
|
8
11
|
if Symbol === val || String === val
|
9
|
-
collections = [:page, :store, :params]
|
12
|
+
collections = [:page, :store, :params, :controller]
|
10
13
|
if collections.include?(val.to_sym)
|
11
|
-
@model = self.send(val)
|
14
|
+
@model.cur = self.send(val).cur
|
12
15
|
else
|
13
16
|
raise "#{val} is not the name of a valid model, choose from: #{collections.join(', ')}"
|
14
17
|
end
|
15
18
|
elsif val
|
16
|
-
@model = val
|
19
|
+
@model.cur = val.cur
|
17
20
|
else
|
18
21
|
raise "model can not be #{val.inspect}"
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
25
|
+
def model
|
26
|
+
@model
|
27
|
+
end
|
28
|
+
|
22
29
|
def self.new(*args, &block)
|
23
30
|
inst = self.allocate
|
24
|
-
|
25
|
-
|
26
|
-
end
|
31
|
+
|
32
|
+
inst.model = (@default_model || :controller)
|
27
33
|
|
28
34
|
inst.initialize(*args, &block)
|
29
35
|
|
30
36
|
return inst
|
31
37
|
end
|
32
38
|
|
39
|
+
def initialize(*args)
|
40
|
+
|
41
|
+
|
42
|
+
# Set the instance variable to match any passed in arguments
|
43
|
+
if args.size > 0
|
44
|
+
args[0].each_pair do |key, value|
|
45
|
+
instance_variable_set(:"@#{key}", value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Change the url params, similar to redirecting to a new url
|
51
|
+
def go(url)
|
52
|
+
self.url.parse(url)
|
53
|
+
end
|
54
|
+
|
33
55
|
def page
|
34
56
|
$page.page
|
35
57
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'volt/extra_core/inflector/inflections'
|
2
|
+
|
3
|
+
Inflector.inflections(:en) do |inflect|
|
4
|
+
inflect.plural(/$/, 's')
|
5
|
+
inflect.plural(/s$/i, 's')
|
6
|
+
inflect.plural(/^(ax|test)is$/i, '\1es')
|
7
|
+
inflect.plural(/(octop|vir)us$/i, '\1i')
|
8
|
+
inflect.plural(/(octop|vir)i$/i, '\1i')
|
9
|
+
inflect.plural(/(alias|status)$/i, '\1es')
|
10
|
+
inflect.plural(/(bu)s$/i, '\1ses')
|
11
|
+
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
|
12
|
+
inflect.plural(/([ti])um$/i, '\1a')
|
13
|
+
inflect.plural(/([ti])a$/i, '\1a')
|
14
|
+
inflect.plural(/sis$/i, 'ses')
|
15
|
+
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
16
|
+
inflect.plural(/(hive)$/i, '\1s')
|
17
|
+
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
18
|
+
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
|
19
|
+
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
20
|
+
inflect.plural(/^(m|l)ouse$/i, '\1ice')
|
21
|
+
inflect.plural(/^(m|l)ice$/i, '\1ice')
|
22
|
+
inflect.plural(/^(ox)$/i, '\1en')
|
23
|
+
inflect.plural(/^(oxen)$/i, '\1')
|
24
|
+
inflect.plural(/(quiz)$/i, '\1zes')
|
25
|
+
|
26
|
+
inflect.singular(/s$/i, '')
|
27
|
+
inflect.singular(/(ss)$/i, '\1')
|
28
|
+
inflect.singular(/(n)ews$/i, '\1ews')
|
29
|
+
inflect.singular(/([ti])a$/i, '\1um')
|
30
|
+
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
|
31
|
+
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
|
32
|
+
inflect.singular(/([^f])ves$/i, '\1fe')
|
33
|
+
inflect.singular(/(hive)s$/i, '\1')
|
34
|
+
inflect.singular(/(tive)s$/i, '\1')
|
35
|
+
inflect.singular(/([lr])ves$/i, '\1f')
|
36
|
+
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
37
|
+
inflect.singular(/(s)eries$/i, '\1eries')
|
38
|
+
inflect.singular(/(m)ovies$/i, '\1ovie')
|
39
|
+
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
|
40
|
+
inflect.singular(/^(m|l)ice$/i, '\1ouse')
|
41
|
+
inflect.singular(/(bus)(es)?$/i, '\1')
|
42
|
+
inflect.singular(/(o)es$/i, '\1')
|
43
|
+
inflect.singular(/(shoe)s$/i, '\1')
|
44
|
+
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
|
45
|
+
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
|
46
|
+
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
|
47
|
+
inflect.singular(/(alias|status)(es)?$/i, '\1')
|
48
|
+
inflect.singular(/^(ox)en/i, '\1')
|
49
|
+
inflect.singular(/(vert|ind)ices$/i, '\1ex')
|
50
|
+
inflect.singular(/(matr)ices$/i, '\1ix')
|
51
|
+
inflect.singular(/(quiz)zes$/i, '\1')
|
52
|
+
inflect.singular(/(database)s$/i, '\1')
|
53
|
+
|
54
|
+
inflect.irregular('person', 'people')
|
55
|
+
inflect.irregular('man', 'men')
|
56
|
+
inflect.irregular('child', 'children')
|
57
|
+
inflect.irregular('sex', 'sexes')
|
58
|
+
inflect.irregular('move', 'moves')
|
59
|
+
# inflect.irregular('cow', 'kine')
|
60
|
+
inflect.irregular('zombie', 'zombies')
|
61
|
+
|
62
|
+
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
|
63
|
+
end
|