volt 0.9.2 → 0.9.3.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +4 -0
- data/Gemfile +3 -0
- data/app/volt/assets/js/volt_js_polyfills.js +0 -1
- data/app/volt/assets/js/volt_watch.js +217 -0
- data/app/volt/models/user.rb +7 -2
- data/app/volt/tasks/query_tasks.rb +6 -0
- data/lib/volt/boot.rb +1 -1
- data/lib/volt/cli/generate.rb +1 -1
- data/lib/volt/config.rb +12 -2
- data/lib/volt/controllers/model_controller.rb +1 -1
- data/lib/volt/data_stores/base_adaptor_client.rb +34 -0
- data/lib/volt/data_stores/{base.rb → base_adaptor_server.rb} +1 -1
- data/lib/volt/data_stores/data_store.rb +23 -7
- data/lib/volt/models/array_model.rb +3 -2
- data/lib/volt/models/model.rb +29 -91
- data/lib/volt/models/{dirty.rb → model_helpers/dirty.rb} +0 -0
- data/lib/volt/models/{listener_tracker.rb → model_helpers/listener_tracker.rb} +0 -0
- data/lib/volt/models/model_helpers/model_change_helpers.rb +76 -0
- data/lib/volt/models/{model_helpers.rb → model_helpers/model_helpers.rb} +0 -0
- data/lib/volt/models/persistors/array_store.rb +2 -23
- data/lib/volt/models/persistors/query/normalizer.rb +0 -44
- data/{templates/project/lib/.empty_directory → lib/volt/models/validations/errors.rb} +0 -0
- data/lib/volt/models/{validations.rb → validations/validations.rb} +80 -26
- data/lib/volt/page/bindings/attribute_binding.rb +17 -3
- data/lib/volt/page/page.rb +1 -0
- data/lib/volt/reactive/eventable.rb +1 -0
- data/lib/volt/server.rb +2 -1
- data/lib/volt/server/component_templates.rb +66 -16
- data/lib/volt/server/forking_server.rb +16 -14
- data/lib/volt/server/html_parser/sandlebars_parser.rb +2 -0
- data/lib/volt/server/html_parser/view_scope.rb +2 -0
- data/lib/volt/server/rack/component_paths.rb +4 -2
- data/lib/volt/server/rack/opal_files.rb +4 -2
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server/template_handlers/handlers.rb +0 -0
- data/lib/volt/spec/setup.rb +23 -8
- data/lib/volt/tasks/dispatcher.rb +4 -0
- data/lib/volt/utils/promise_patch.rb +3 -0
- data/lib/volt/version.rb +1 -1
- data/spec/apps/kitchen_sink/Gemfile +5 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +10 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +20 -0
- data/spec/apps/kitchen_sink/app/main/views/main/form.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
- data/spec/integration/bindings_spec.rb +33 -0
- data/spec/integration/user_spec.rb +51 -21
- data/spec/models/associations_spec.rb +8 -0
- data/spec/models/model_spec.rb +7 -0
- data/spec/models/user_spec.rb +20 -0
- data/spec/models/validations_spec.rb +2 -1
- data/spec/models/validators/block_validations_spec.rb +53 -0
- data/spec/page/bindings/template_binding/view_lookup_for_path_spec.rb +10 -0
- data/spec/page/path_string_renderer_spec.rb +6 -0
- data/spec/reactive/eventable_spec.rb +24 -6
- data/spec/server/component_templates_spec.rb +21 -0
- data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -13
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +2 -2
- data/spec/server/rack/http_resource_spec.rb +10 -0
- data/spec/tasks/dispatcher_spec.rb +5 -0
- data/spec/tasks/user_tasks_spec.rb +59 -0
- data/spec/utils/task_argument_filtererer_spec.rb +6 -0
- data/templates/newgem/app/newgem/config/initializers/boot.rb +10 -0
- data/templates/newgem/lib/newgem.rb.tt +13 -0
- data/templates/project/Gemfile.tt +3 -0
- data/templates/project/app/main/lib/.empty_directory +0 -0
- data/volt.gemspec +3 -1
- metadata +24 -25
- data/lib/volt/data_stores/mongo_driver.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6e09afcd5fa1abe6f17a9c2912bc79cf4cf74aa
|
4
|
+
data.tar.gz: f0ee7b0085bddf48461122bfccec1b21b06b2e3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed02ac1418ee8f8ed50a67331dfb58eeabf933a55ecb60a2cb05052245164e851331fea5a5de8977a1fbfa31859bedea4dcaf8117b28258559c0954c5ff3f9e3
|
7
|
+
data.tar.gz: 7069f1ac08014813da1501c252fed11031c248b479d7b7323ff49a69b17cdc5dc54e793f48b48b199a6e23fd56178ff6684b47151640ab321af3fd0cfcfe5d71
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.9.3.pre1
|
4
|
+
### Added
|
5
|
+
- Added validations block for conditional validation runs
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
- All logic associated with mongo has been moved into the volt-mongo gem. If you are migrating from a previous version, be sure to add ```gem 'volt-mongo'``` to the Gemfile.
|
9
|
+
|
3
10
|
## 0.9.2
|
4
11
|
### Changed
|
5
12
|
- We released 0.9.1 with a bug for destroy (doh!). Specs added and bug fixed.
|
data/CONTRIBUTING.md
CHANGED
@@ -41,6 +41,10 @@ If not installed, you can install from the source or use Homebrew:
|
|
41
41
|
brew upgrade && brew install phantomjs
|
42
42
|
```
|
43
43
|
|
44
|
+
#### Caveat
|
45
|
+
|
46
|
+
You need 2.0 for MutationObserver API support, so until this issue https://github.com/teampoltergeist/poltergeist/issues/574 is resolved that poltergeist will support phantomjs 2.0, the test for attribute binding has to be in a real browser.
|
47
|
+
|
44
48
|
#### Bundle Install and Test
|
45
49
|
|
46
50
|
Ensure that you can build the project and run tests.
|
data/Gemfile
CHANGED
@@ -0,0 +1,217 @@
|
|
1
|
+
/*
|
2
|
+
* Watch Event Listener
|
3
|
+
*
|
4
|
+
* @author Darcy Clarke
|
5
|
+
* Modified by Penn Su
|
6
|
+
*
|
7
|
+
* Copyright (c) 2014 Darcy Clarke
|
8
|
+
* Dual licensed under the MIT and GPL licenses.
|
9
|
+
*
|
10
|
+
* Usage:
|
11
|
+
* watch(element, 'width height', function(){
|
12
|
+
* console.log(this.style.width, this.style.height);
|
13
|
+
* });
|
14
|
+
*/
|
15
|
+
|
16
|
+
(function (window) {
|
17
|
+
|
18
|
+
var toArray;
|
19
|
+
var eqlArrays;
|
20
|
+
|
21
|
+
// http://jsfiddle.net/moagrius/YxzfV/
|
22
|
+
Array.prototype.multisplice = function(){
|
23
|
+
var args = Array.apply(null, arguments);
|
24
|
+
args.sort(function(a,b){
|
25
|
+
return a - b;
|
26
|
+
});
|
27
|
+
for(var i = 0; i < args.length; i++){
|
28
|
+
var index = args[i] - i;
|
29
|
+
this.splice(index, 1);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
// Object to Array
|
34
|
+
toArray = function (obj) {
|
35
|
+
|
36
|
+
var arr = [];
|
37
|
+
|
38
|
+
for (var i = obj.length >>> 0; i--;) {
|
39
|
+
arr[i] = obj[i];
|
40
|
+
}
|
41
|
+
|
42
|
+
return arr;
|
43
|
+
|
44
|
+
};
|
45
|
+
|
46
|
+
eqlArrays = function (a, b) {
|
47
|
+
a.length == b.length && a.every(function (e, i) { e === b[i] });
|
48
|
+
}
|
49
|
+
|
50
|
+
var _watch = function (elements, props, options, callback){
|
51
|
+
|
52
|
+
// Setup
|
53
|
+
var self = this;
|
54
|
+
var check;
|
55
|
+
|
56
|
+
// Check if we should fire callback
|
57
|
+
check = function (e) {
|
58
|
+
|
59
|
+
var self = this;
|
60
|
+
|
61
|
+
for (var i = 0; i < self.watching.length; i++) {
|
62
|
+
|
63
|
+
var data = self.watching[i];
|
64
|
+
var changed = true;
|
65
|
+
var temp;
|
66
|
+
|
67
|
+
// Iterate through properties
|
68
|
+
for (var j = 0; j < data.props.length; j++) {
|
69
|
+
temp = self.attributes[data.props[j]] || self.style[data.props[j]];
|
70
|
+
if (data.vals[j] != temp) {
|
71
|
+
data.vals[j] = temp;
|
72
|
+
data.changed[j] = true;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
// Check changed attributes
|
77
|
+
for (var k = 0; k < data.props.length; k++) {
|
78
|
+
if (!data.changed[k]) {
|
79
|
+
changed = false;
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
// Run callback if property has changed
|
85
|
+
if (changed && data.callback) {
|
86
|
+
data.callback.apply(self, e);
|
87
|
+
}
|
88
|
+
|
89
|
+
};
|
90
|
+
|
91
|
+
};
|
92
|
+
|
93
|
+
// Elements from node list to array
|
94
|
+
elements = toArray(elements);
|
95
|
+
|
96
|
+
// Type check options
|
97
|
+
if (typeof(options) == 'function') {
|
98
|
+
callback = options;
|
99
|
+
options = {};
|
100
|
+
}
|
101
|
+
|
102
|
+
// Type check callback
|
103
|
+
if (typeof(callback) != 'function') {
|
104
|
+
callback = function(){};
|
105
|
+
}
|
106
|
+
|
107
|
+
// Set throttle
|
108
|
+
options.throttle = options.throttle || 10;
|
109
|
+
|
110
|
+
// Iterate over elements
|
111
|
+
for (var i = 0; i < elements.length; i++) {
|
112
|
+
|
113
|
+
var element = elements[i];
|
114
|
+
var data = {
|
115
|
+
props: props.split(' '),
|
116
|
+
vals: [],
|
117
|
+
changed: [],
|
118
|
+
callback: callback
|
119
|
+
};
|
120
|
+
|
121
|
+
// Grab each property's initial value
|
122
|
+
for (var j = 0; j < data.props.length; j++) {
|
123
|
+
data.vals[j] = element.attributes[data.props[j]] || element.style[data.props[j]];
|
124
|
+
data.changed[j] = false;
|
125
|
+
}
|
126
|
+
|
127
|
+
// Set watch array
|
128
|
+
if (!element.watching) {
|
129
|
+
element.watching = [];
|
130
|
+
}
|
131
|
+
|
132
|
+
// Store data in watch array
|
133
|
+
element.watching.push(data);
|
134
|
+
|
135
|
+
// Create new Mutation Observer
|
136
|
+
var observer = new MutationObserver(function (mutations) {
|
137
|
+
console.log(mutations);
|
138
|
+
for (var k = 0; k < mutations.length; k++) {
|
139
|
+
check.call(mutations[k].target, mutations[k]);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
|
143
|
+
// Set observer array
|
144
|
+
if (!element.observers) {
|
145
|
+
element.observers = [];
|
146
|
+
}
|
147
|
+
|
148
|
+
// Store element observer
|
149
|
+
element.observers.push(observer);
|
150
|
+
|
151
|
+
// Start observing
|
152
|
+
observer.observe(element, { subtree: false, attributes: true });
|
153
|
+
|
154
|
+
}
|
155
|
+
|
156
|
+
// Return elements to enable chaining
|
157
|
+
return self;
|
158
|
+
|
159
|
+
};
|
160
|
+
|
161
|
+
var _unwatch = function (elements, props){
|
162
|
+
|
163
|
+
// Setup
|
164
|
+
var self = this;
|
165
|
+
|
166
|
+
// Elements from node list to array
|
167
|
+
elements = toArray(elements);
|
168
|
+
|
169
|
+
// Iterate over elements
|
170
|
+
for (var i = 0; i < elements.length; i++) {
|
171
|
+
|
172
|
+
var element = elements[i];
|
173
|
+
var indexes = []
|
174
|
+
|
175
|
+
if (element.watching) {
|
176
|
+
for (var j = 0; j < element.watching.length; j++) {
|
177
|
+
|
178
|
+
var data = element.watching[j];
|
179
|
+
if (eqlArrays(data.props, props.split(' '))) {
|
180
|
+
indexes.push(j);
|
181
|
+
}
|
182
|
+
|
183
|
+
}
|
184
|
+
|
185
|
+
element.watching.multisplice.apply(element.watching, indexes);
|
186
|
+
}
|
187
|
+
|
188
|
+
}
|
189
|
+
|
190
|
+
// Return elements to enable chaining
|
191
|
+
return self;
|
192
|
+
|
193
|
+
};
|
194
|
+
|
195
|
+
// Expose watch to window
|
196
|
+
window.watch = function () {
|
197
|
+
return _watch.apply(arguments[0], arguments);
|
198
|
+
};
|
199
|
+
|
200
|
+
window.unwatch = function () {
|
201
|
+
return _unwatch.apply(arguments[0], arguments);
|
202
|
+
};
|
203
|
+
|
204
|
+
// Expose watch to jQuery
|
205
|
+
(function ($) {
|
206
|
+
$.fn.watch = function () {
|
207
|
+
Array.prototype.unshift.call(arguments, this);
|
208
|
+
return _watch.apply(this, arguments);
|
209
|
+
};
|
210
|
+
|
211
|
+
$.fn.unwatch = function () {
|
212
|
+
Array.prototype.unshift.call(arguments, this);
|
213
|
+
return _unwatch.apply(this, arguments);
|
214
|
+
};
|
215
|
+
})(jQuery);
|
216
|
+
|
217
|
+
})(window);
|
data/app/volt/models/user.rb
CHANGED
@@ -23,8 +23,13 @@ module Volt
|
|
23
23
|
end
|
24
24
|
|
25
25
|
if RUBY_PLATFORM == 'opal'
|
26
|
-
|
27
|
-
|
26
|
+
validations do
|
27
|
+
# Only validate password when it has changed
|
28
|
+
if changed?(:password)
|
29
|
+
# Don't validate on the server
|
30
|
+
validate :password, length: 8
|
31
|
+
end
|
32
|
+
end
|
28
33
|
end
|
29
34
|
|
30
35
|
def password=(val)
|
@@ -16,6 +16,12 @@ class QueryTasks < Volt::Task
|
|
16
16
|
self.class.live_query_pool
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.reset!
|
20
|
+
@@channel_live_queries = {}
|
21
|
+
@@live_query_pool = nil
|
22
|
+
live_query_pool
|
23
|
+
end
|
24
|
+
|
19
25
|
def add_listener(collection, query)
|
20
26
|
live_query = @@live_query_pool.lookup(collection, query)
|
21
27
|
track_channel_in_live_query(live_query)
|
data/lib/volt/boot.rb
CHANGED
@@ -2,7 +2,7 @@ unless RUBY_PLATFORM == 'opal'
|
|
2
2
|
# An option to skip requiring.
|
3
3
|
unless ENV['SKIP_BUNDLER_REQUIRE']
|
4
4
|
# Require in gems
|
5
|
-
Bundler.require((ENV['VOLT_ENV'] || ENV['RACK_ENV'] || :development).to_sym)
|
5
|
+
Bundler.require(:default, (ENV['VOLT_ENV'] || ENV['RACK_ENV'] || :development).to_sym)
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
data/lib/volt/cli/generate.rb
CHANGED
@@ -74,7 +74,7 @@ class Generate < Thor
|
|
74
74
|
method_option :name, type: :string, banner: 'The name of the task.'
|
75
75
|
method_option :component, type: :string, default: 'main', banner: 'The component the task should be created in.', required: false
|
76
76
|
def task(name, component = 'main')
|
77
|
-
name = name.underscore.gsub(/_tasks$/, '').singularize + '
|
77
|
+
name = name.underscore.gsub(/_tasks$/, '').singularize + '_task'
|
78
78
|
output_file = Dir.pwd + "/app/#{component}/tasks/#{name}.rb"
|
79
79
|
spec_file = Dir.pwd + "/spec/app/#{component}/tasks/#{name}_spec.rb"
|
80
80
|
template('task/task.rb.tt', output_file, task_name: name.camelize.singularize)
|
data/lib/volt/config.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Config lets a user set global config options for Volt.
|
2
|
-
# The hash is setup on the server, then
|
2
|
+
# The hash is setup on the server, then the settings under .public are passed
|
3
|
+
# to the client on initial page render. Volt.configure can be called multiple
|
4
|
+
# times and settings will be added to the existing configuration.
|
3
5
|
if RUBY_PLATFORM == 'opal'
|
4
6
|
require 'ostruct'
|
5
7
|
|
@@ -82,8 +84,16 @@ else
|
|
82
84
|
alias_method :config, :configuration
|
83
85
|
end
|
84
86
|
|
87
|
+
inst = self
|
85
88
|
configuration_defaults do |c|
|
86
|
-
|
89
|
+
current_config = inst.instance_variable_get(:@configuration)
|
90
|
+
if current_config
|
91
|
+
# Default to the existing config, so we can extend
|
92
|
+
c.from_h(current_config.to_h)
|
93
|
+
else
|
94
|
+
# First call
|
95
|
+
c.from_h(Volt.defaults)
|
96
|
+
end
|
87
97
|
end
|
88
98
|
end
|
89
99
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Volt
|
2
|
+
class DataStore
|
3
|
+
class BaseAdaptorClient
|
4
|
+
# normalize_query should take all parts of the query and return a
|
5
|
+
# "normalized" version, so that two queries that are eseitnally the same
|
6
|
+
# except for things like order are the same.
|
7
|
+
#
|
8
|
+
# Typically this means sorting parts of the query so that two queries
|
9
|
+
# which do the same things are the same, so they can be uniquely
|
10
|
+
# identified.
|
11
|
+
#
|
12
|
+
# The default implementation does no normalizing. This works, but results
|
13
|
+
# in more queries being sent to the backend.
|
14
|
+
def self.normalize_query(query)
|
15
|
+
query
|
16
|
+
end
|
17
|
+
|
18
|
+
# A class method that takes an array of method names we want to provide
|
19
|
+
# on the ArrayModel class. (These are typically query/sort/order/etc...
|
20
|
+
# methods). This adds a proxy method to the store persistor for all
|
21
|
+
# passed in method names, and then sets up a default tracking method
|
22
|
+
# on the ArrayStore persistor.
|
23
|
+
def self.data_store_methods(*method_names)
|
24
|
+
Volt::ArrayModel.proxy_to_persistor(*method_names)
|
25
|
+
|
26
|
+
method_names.each do |method_name|
|
27
|
+
Volt::Persistors::ArrayStore.send(:define_method, method_name) do |*args|
|
28
|
+
add_query_part(method_name, *args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,20 +1,36 @@
|
|
1
|
-
require 'volt/data_stores/
|
1
|
+
require 'volt/data_stores/base_adaptor_server'
|
2
2
|
|
3
3
|
module Volt
|
4
4
|
class DataStore
|
5
5
|
def self.fetch
|
6
6
|
# Cache the driver
|
7
|
-
return @
|
7
|
+
return @adaptor if @adaptor
|
8
8
|
|
9
9
|
database_name = Volt.config.db_driver
|
10
|
-
|
10
|
+
adaptor_name = database_name.camelize + 'AdaptorServer'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
root = Volt::DataStore
|
13
|
+
if root.const_defined?(adaptor_name)
|
14
|
+
adaptor_name = root.const_get(adaptor_name)
|
15
|
+
@adaptor = adaptor_name.new
|
16
|
+
else
|
16
17
|
raise "#{database_name} is not a supported database"
|
17
18
|
end
|
19
|
+
|
20
|
+
@adaptor
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.adaptor_client
|
24
|
+
# Load the client adaptor
|
25
|
+
@adaptor_client ||= begin
|
26
|
+
ds_name = Volt.config.public.datastore_name
|
27
|
+
unless ds_name
|
28
|
+
raise "No data store configured, please include volt-mongo or " +
|
29
|
+
"another similar gem."
|
30
|
+
end
|
31
|
+
adaptor_class_name = ds_name.capitalize + "AdaptorClient"
|
32
|
+
Volt::DataStore.const_get(adaptor_class_name)
|
33
|
+
end
|
18
34
|
end
|
19
35
|
end
|
20
36
|
end
|