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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/CONTRIBUTING.md +4 -0
  4. data/Gemfile +3 -0
  5. data/app/volt/assets/js/volt_js_polyfills.js +0 -1
  6. data/app/volt/assets/js/volt_watch.js +217 -0
  7. data/app/volt/models/user.rb +7 -2
  8. data/app/volt/tasks/query_tasks.rb +6 -0
  9. data/lib/volt/boot.rb +1 -1
  10. data/lib/volt/cli/generate.rb +1 -1
  11. data/lib/volt/config.rb +12 -2
  12. data/lib/volt/controllers/model_controller.rb +1 -1
  13. data/lib/volt/data_stores/base_adaptor_client.rb +34 -0
  14. data/lib/volt/data_stores/{base.rb → base_adaptor_server.rb} +1 -1
  15. data/lib/volt/data_stores/data_store.rb +23 -7
  16. data/lib/volt/models/array_model.rb +3 -2
  17. data/lib/volt/models/model.rb +29 -91
  18. data/lib/volt/models/{dirty.rb → model_helpers/dirty.rb} +0 -0
  19. data/lib/volt/models/{listener_tracker.rb → model_helpers/listener_tracker.rb} +0 -0
  20. data/lib/volt/models/model_helpers/model_change_helpers.rb +76 -0
  21. data/lib/volt/models/{model_helpers.rb → model_helpers/model_helpers.rb} +0 -0
  22. data/lib/volt/models/persistors/array_store.rb +2 -23
  23. data/lib/volt/models/persistors/query/normalizer.rb +0 -44
  24. data/{templates/project/lib/.empty_directory → lib/volt/models/validations/errors.rb} +0 -0
  25. data/lib/volt/models/{validations.rb → validations/validations.rb} +80 -26
  26. data/lib/volt/page/bindings/attribute_binding.rb +17 -3
  27. data/lib/volt/page/page.rb +1 -0
  28. data/lib/volt/reactive/eventable.rb +1 -0
  29. data/lib/volt/server.rb +2 -1
  30. data/lib/volt/server/component_templates.rb +66 -16
  31. data/lib/volt/server/forking_server.rb +16 -14
  32. data/lib/volt/server/html_parser/sandlebars_parser.rb +2 -0
  33. data/lib/volt/server/html_parser/view_scope.rb +2 -0
  34. data/lib/volt/server/rack/component_paths.rb +4 -2
  35. data/lib/volt/server/rack/opal_files.rb +4 -2
  36. data/lib/volt/server/socket_connection_handler.rb +5 -1
  37. data/lib/volt/server/template_handlers/handlers.rb +0 -0
  38. data/lib/volt/spec/setup.rb +23 -8
  39. data/lib/volt/tasks/dispatcher.rb +4 -0
  40. data/lib/volt/utils/promise_patch.rb +3 -0
  41. data/lib/volt/version.rb +1 -1
  42. data/spec/apps/kitchen_sink/Gemfile +5 -0
  43. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  44. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +10 -0
  45. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +20 -0
  46. data/spec/apps/kitchen_sink/app/main/views/main/form.html +73 -0
  47. data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
  48. data/spec/integration/bindings_spec.rb +33 -0
  49. data/spec/integration/user_spec.rb +51 -21
  50. data/spec/models/associations_spec.rb +8 -0
  51. data/spec/models/model_spec.rb +7 -0
  52. data/spec/models/user_spec.rb +20 -0
  53. data/spec/models/validations_spec.rb +2 -1
  54. data/spec/models/validators/block_validations_spec.rb +53 -0
  55. data/spec/page/bindings/template_binding/view_lookup_for_path_spec.rb +10 -0
  56. data/spec/page/path_string_renderer_spec.rb +6 -0
  57. data/spec/reactive/eventable_spec.rb +24 -6
  58. data/spec/server/component_templates_spec.rb +21 -0
  59. data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -13
  60. data/spec/server/html_parser/view_parser_spec.rb +3 -0
  61. data/spec/server/rack/asset_files_spec.rb +2 -2
  62. data/spec/server/rack/http_resource_spec.rb +10 -0
  63. data/spec/tasks/dispatcher_spec.rb +5 -0
  64. data/spec/tasks/user_tasks_spec.rb +59 -0
  65. data/spec/utils/task_argument_filtererer_spec.rb +6 -0
  66. data/templates/newgem/app/newgem/config/initializers/boot.rb +10 -0
  67. data/templates/newgem/lib/newgem.rb.tt +13 -0
  68. data/templates/project/Gemfile.tt +3 -0
  69. data/templates/project/app/main/lib/.empty_directory +0 -0
  70. data/volt.gemspec +3 -1
  71. metadata +24 -25
  72. 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: c57d60611600de9f132243981453e12dd14d7d2b
4
- data.tar.gz: e14a1bbead84d93c8cd03bcac1bdaf54d315d2ff
3
+ metadata.gz: d6e09afcd5fa1abe6f17a9c2912bc79cf4cf74aa
4
+ data.tar.gz: f0ee7b0085bddf48461122bfccec1b21b06b2e3e
5
5
  SHA512:
6
- metadata.gz: 4fc5686a5121b45d4186acd8c59e0b46e45ad9796752a1bc0b915234fe864cb90bc8f6ad58954e4f757516a744e07cfe3da20d22e7283b49c12aab7bf59b12f6
7
- data.tar.gz: 5b1b6ac243e17387a564b42be1341d378282883766a7e3002f21faa2396bd6c2006b53333ad39936194d5c84ba6ca29fb34c9dd63043f241251d5727481691b7
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
@@ -2,6 +2,9 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ # volt-mongo gem for testing
6
+ gem 'volt-mongo'
7
+
5
8
  group :development do
6
9
  # For testing the kitchen sink app
7
10
  # Twitter bootstrap
@@ -10,7 +10,6 @@ if (!Function.prototype.bind) {
10
10
  };
11
11
  }
12
12
 
13
-
14
13
  // setImmediate
15
14
  (function (global, undefined) {
16
15
  "use strict";
@@ -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);
@@ -23,8 +23,13 @@ module Volt
23
23
  end
24
24
 
25
25
  if RUBY_PLATFORM == 'opal'
26
- # Don't validate on the server
27
- validate :password, length: 8
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
 
@@ -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 + '_tasks'
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 passed to the client on initial page render.
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
- c.from_h(Volt.defaults)
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
@@ -190,7 +190,7 @@ module Volt
190
190
  def require_login(message = 'You must login to access this area.')
191
191
  unless Volt.current_user_id
192
192
  flash._notices << message
193
- go '/login'
193
+ redirect_to '/login'
194
194
 
195
195
  stop_chain
196
196
  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,6 +1,6 @@
1
1
  module Volt
2
2
  class DataStore
3
- class Base
3
+ class BaseAdaptorServer
4
4
  end
5
5
  end
6
6
  end
@@ -1,20 +1,36 @@
1
- require 'volt/data_stores/mongo_driver'
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 @driver if @driver
7
+ return @adaptor if @adaptor
8
8
 
9
9
  database_name = Volt.config.db_driver
10
- driver_name = database_name.camelize + 'Driver'
10
+ adaptor_name = database_name.camelize + 'AdaptorServer'
11
11
 
12
- begin
13
- driver = const_get(driver_name)
14
- @driver = MongoDriver.new
15
- rescue NameError => e
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