volt 0.9.6 → 0.9.7.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +26 -0
  4. data/Gemfile +6 -1
  5. data/README.md +2 -0
  6. data/Rakefile +1 -0
  7. data/app/volt/models/active_volt_instance.rb +8 -6
  8. data/app/volt/models/user.rb +11 -2
  9. data/app/volt/models/volt_app_property.rb +8 -0
  10. data/app/volt/tasks/query_tasks.rb +23 -31
  11. data/app/volt/tasks/store_tasks.rb +2 -2
  12. data/app/volt/tasks/volt_admin_tasks.rb +24 -0
  13. data/docs/UPGRADE_GUIDE.md +6 -0
  14. data/lib/volt.rb +19 -12
  15. data/lib/volt/boot.rb +1 -0
  16. data/lib/volt/cli.rb +19 -8
  17. data/lib/volt/cli/console.rb +0 -1
  18. data/lib/volt/cli/generators.rb +14 -3
  19. data/lib/volt/cli/migrate.rb +26 -0
  20. data/lib/volt/config.rb +17 -4
  21. data/lib/volt/controllers/http_controller.rb +12 -0
  22. data/lib/volt/data_stores/base_adaptor_client.rb +2 -2
  23. data/lib/volt/data_stores/base_adaptor_server.rb +2 -0
  24. data/lib/volt/data_stores/data_store.rb +20 -14
  25. data/lib/volt/extra_core/class.rb +28 -14
  26. data/lib/volt/extra_core/hash.rb +5 -0
  27. data/lib/volt/extra_core/string.rb +3 -1
  28. data/lib/volt/helpers/time.rb +9 -43
  29. data/lib/volt/helpers/time/calculations.rb +204 -0
  30. data/lib/volt/helpers/time/distance.rb +63 -0
  31. data/lib/volt/helpers/time/duration.rb +71 -0
  32. data/lib/volt/helpers/time/local_calculations.rb +49 -0
  33. data/lib/volt/helpers/time/local_volt_time.rb +23 -0
  34. data/lib/volt/helpers/time/numeric.rb +59 -0
  35. data/lib/volt/helpers/time/volt_time.rb +170 -0
  36. data/lib/volt/models.rb +5 -0
  37. data/lib/volt/models/array_model.rb +33 -6
  38. data/lib/volt/models/associations.rb +146 -23
  39. data/lib/volt/models/buffer.rb +38 -41
  40. data/lib/volt/models/cursor.rb +15 -0
  41. data/lib/volt/models/errors.rb +11 -0
  42. data/lib/volt/models/field_helpers.rb +108 -68
  43. data/lib/volt/models/helpers/array_model.rb +4 -0
  44. data/lib/volt/models/helpers/base.rb +8 -1
  45. data/lib/volt/models/helpers/change_helpers.rb +31 -12
  46. data/lib/volt/models/helpers/defaults.rb +15 -0
  47. data/lib/volt/models/location.rb +20 -6
  48. data/lib/volt/models/migrations/migration.rb +23 -0
  49. data/lib/volt/models/migrations/migration_runner.rb +146 -0
  50. data/lib/volt/models/model.rb +38 -1
  51. data/lib/volt/models/permissions.rb +8 -1
  52. data/lib/volt/models/persistors/array_store.rb +87 -8
  53. data/lib/volt/models/persistors/base.rb +19 -0
  54. data/lib/volt/models/persistors/model_store.rb +1 -1
  55. data/lib/volt/models/persistors/page.rb +4 -1
  56. data/lib/volt/models/persistors/query/query_identifier.rb +102 -0
  57. data/lib/volt/models/persistors/query/query_listener.rb +57 -12
  58. data/lib/volt/models/root_models/root_models.rb +19 -0
  59. data/lib/volt/models/url.rb +11 -2
  60. data/lib/volt/models/validations/validations.rb +5 -2
  61. data/lib/volt/models/validators/type_validator.rb +11 -0
  62. data/lib/volt/models/validators/unique_validator.rb +2 -2
  63. data/lib/volt/page/bindings/attribute_binding.rb +23 -1
  64. data/lib/volt/page/targets/attribute_section.rb +7 -0
  65. data/lib/volt/page/targets/binding_document/component_node.rb +44 -18
  66. data/lib/volt/page/targets/binding_document/tag_node.rb +41 -0
  67. data/lib/volt/page/tasks.rb +16 -8
  68. data/lib/volt/queries/live_query.rb +109 -0
  69. data/lib/volt/queries/live_query_pool.rb +58 -0
  70. data/lib/volt/queries/live_subquery.rb +0 -0
  71. data/lib/volt/queries/query_association_splitter.rb +31 -0
  72. data/lib/volt/queries/query_diff.rb +100 -0
  73. data/lib/volt/queries/query_runner.rb +110 -0
  74. data/lib/volt/queries/query_subscription.rb +80 -0
  75. data/lib/volt/queries/query_subscription_pool.rb +37 -0
  76. data/lib/volt/reactive/eventable.rb +8 -0
  77. data/lib/volt/reactive/reactive_array.rb +0 -4
  78. data/lib/volt/router/routes.rb +81 -31
  79. data/lib/volt/server/message_bus/base_message_bus.rb +9 -3
  80. data/lib/volt/server/message_bus/peer_to_peer.rb +6 -6
  81. data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
  82. data/lib/volt/server/middleware/default_middleware_stack.rb +12 -8
  83. data/lib/volt/server/rack/component_paths.rb +31 -4
  84. data/lib/volt/server/rack/http_content_types.rb +62 -0
  85. data/lib/volt/server/rack/http_resource.rb +1 -1
  86. data/lib/volt/server/rack/index_files.rb +8 -1
  87. data/lib/volt/server/rack/opal_files.rb +16 -1
  88. data/lib/volt/server/rack/sprockets_helpers_setup.rb +32 -1
  89. data/lib/volt/server/socket_connection_handler.rb +16 -7
  90. data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -3
  91. data/lib/volt/spec/capybara.rb +4 -3
  92. data/lib/volt/spec/setup.rb +5 -0
  93. data/lib/volt/tasks/dispatcher.rb +3 -1
  94. data/lib/volt/utils/data_transformer.rb +4 -4
  95. data/lib/volt/utils/ejson.rb +19 -6
  96. data/lib/volt/utils/promise_extensions.rb +1 -1
  97. data/lib/volt/utils/time_opal_patch.rb +749 -0
  98. data/lib/volt/utils/time_patch.rb +11 -4
  99. data/lib/volt/version.rb +1 -1
  100. data/lib/volt/volt/app.rb +19 -11
  101. data/lib/volt/volt/properties.rb +24 -0
  102. data/lib/volt/volt/server_setup/app.rb +30 -7
  103. data/lib/volt/volt/users.rb +15 -3
  104. data/spec/apps/kitchen_sink/Gemfile +5 -1
  105. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  106. data/spec/apps/kitchen_sink/app/main/controllers/save_controller.rb +1 -1
  107. data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +4 -0
  108. data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +4 -2
  109. data/spec/apps/kitchen_sink/app/main/models/post.rb +0 -1
  110. data/spec/apps/kitchen_sink/app/main/models/todo.rb +4 -0
  111. data/spec/apps/kitchen_sink/app/main/views/mailers/reset_password.html +10 -0
  112. data/spec/apps/kitchen_sink/app/main/views/todos/index.html +2 -0
  113. data/spec/apps/kitchen_sink/config/app.rb +2 -0
  114. data/spec/apps/migrations/config/db/migrations/1445111704_migration1.rb +7 -0
  115. data/spec/apps/migrations/config/db/migrations/1445113517_migration2.rb +7 -0
  116. data/spec/apps/migrations/config/db/migrations/1445115200_migration3.rb +7 -0
  117. data/spec/extra_core/class_spec.rb +10 -0
  118. data/spec/helpers/distance_spec.rb +35 -0
  119. data/spec/helpers/duration_spec.rb +160 -0
  120. data/spec/helpers/volt_time_spec.rb +275 -0
  121. data/spec/integration/callbacks_spec.rb +2 -1
  122. data/spec/integration/http_endpoints_spec.rb +4 -0
  123. data/spec/integration/save_spec.rb +1 -1
  124. data/spec/integration/todos_spec.rb +7 -5
  125. data/spec/models/array_model_spec.rb +17 -3
  126. data/spec/models/associations_spec.rb +48 -1
  127. data/spec/models/field_helpers_spec.rb +7 -3
  128. data/spec/models/migrations/migration_runner_spec.rb +69 -0
  129. data/spec/models/model_spec.rb +42 -8
  130. data/spec/models/permissions_spec.rb +20 -8
  131. data/spec/models/persistors/array_store_spec.rb +18 -0
  132. data/spec/models/persistors/page_spec.rb +15 -10
  133. data/spec/models/persistors/store_spec.rb +13 -3
  134. data/spec/models/url_spec.rb +4 -3
  135. data/spec/models/user_spec.rb +6 -3
  136. data/spec/models/user_validation_spec.rb +3 -3
  137. data/spec/models/validations_spec.rb +4 -0
  138. data/spec/models/validators/block_validations_spec.rb +9 -5
  139. data/spec/models/validators/email_validator_spec.rb +2 -0
  140. data/spec/models/validators/lifecycle_callbacks_spec.rb +86 -0
  141. data/spec/models/validators/unique_validator_spec.rb +1 -0
  142. data/spec/page/path_string_renderer_spec.rb +5 -0
  143. data/spec/queries/live_query_spec.rb +16 -0
  144. data/spec/queries/query_association_splitter_spec.rb +14 -0
  145. data/spec/queries/query_diff_spec.rb +132 -0
  146. data/spec/queries/query_identifier_spec.rb +98 -0
  147. data/spec/queries/query_runner_spec.rb +63 -0
  148. data/spec/queries/query_tracker_spec.rb +141 -0
  149. data/spec/router/routes_spec.rb +52 -21
  150. data/spec/server/middleware/rack_content_types_spec.rb +78 -0
  151. data/spec/server/rack/asset_files_spec.rb +38 -30
  152. data/spec/spec_helper.rb +8 -0
  153. data/spec/utils/ejson_spec.rb +9 -8
  154. data/spec/utils/ejson_volt_time_spec.rb +65 -0
  155. data/templates/migration/migration.rb.tt +9 -0
  156. data/templates/newgem/gitignore.tt +1 -0
  157. data/templates/project/Gemfile.tt +19 -2
  158. data/templates/project/README.md.tt +6 -1
  159. data/templates/project/app/main/config/dependencies.rb +6 -0
  160. data/templates/project/config/app.rb.tt +18 -4
  161. data/volt.gemspec +2 -2
  162. metadata +73 -16
  163. data/app/volt/tasks/live_query/live_query_pool.rb +0 -48
  164. data/app/volt/tasks/live_query/query_tracker.rb +0 -92
  165. data/spec/tasks/live_query_spec.rb +0 -18
  166. data/spec/tasks/query_tasks.rb +0 -7
  167. data/spec/tasks/query_tracker_spec.rb +0 -145
@@ -1,12 +1,19 @@
1
- # Patchs a bug in the Time class, where two instances of time with the same
2
- # value do not hash to the same hash as they do in MRI.
3
- # https://github.com/opal/opal/issues/963
4
1
  if RUBY_PLATFORM == 'opal'
5
2
  require 'time'
6
3
 
7
4
  class Time
5
+ # Patchs a bug in the Time class, where two instances of time with the same
6
+ # value do not hash to the same hash as they do in MRI.
7
+ # https://github.com/opal/opal/issues/963
8
8
  def hash
9
9
  "Time:#{to_i}"
10
10
  end
11
+
12
+ # Patches backported Opal Time because it's missing
13
+ # this method
14
+ def getlocal
15
+ s = self.to_f - self.utc_offset
16
+ ::Time.at(self.to_f)
17
+ end
11
18
  end
12
- end
19
+ end
data/lib/volt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Volt
2
2
  module Version
3
- STRING = '0.9.6'
3
+ STRING = '0.9.7.pre2'
4
4
  end
5
5
  end
data/lib/volt/volt/app.rb CHANGED
@@ -47,9 +47,7 @@ module Volt
47
47
  include Volt::ServerSetup::App
48
48
  end
49
49
 
50
- attr_reader :component_paths, :router, :live_query_pool,
51
- :channel_live_queries, :app_path, :database, :message_bus,
52
- :middleware, :browser
50
+ attr_reader :component_paths, :router, :app_path, :database, :message_bus, :middleware, :browser
53
51
  attr_accessor :sprockets, :opal_files
54
52
 
55
53
  def initialize(app_path=nil)
@@ -77,6 +75,12 @@ module Volt
77
75
  # middleware.
78
76
  run_config
79
77
 
78
+ # abort_on_exception is a useful debugging tool, and in my opinion something
79
+ # you probbaly want on. That said you can disable it if you need.
80
+ unless RUBY_PLATFORM == 'opal'
81
+ Thread.abort_on_exception = Volt.config.abort_on_exception
82
+ end
83
+
80
84
  # Setup all of the middleware we can before we load the users components
81
85
  # since the users components might want to add middleware during boot.
82
86
  setup_preboot_middleware
@@ -87,13 +91,9 @@ module Volt
87
91
  # Require in app and initializers
88
92
  run_app_and_initializers unless RUBY_PLATFORM == 'opal'
89
93
 
90
- require_components
94
+ reset_query_pool!
91
95
 
92
- # abort_on_exception is a useful debugging tool, and in my opinion something
93
- # you probbaly want on. That said you can disable it if you need.
94
- unless RUBY_PLATFORM == 'opal'
95
- Thread.abort_on_exception = Volt.config.abort_on_exception
96
- end
96
+ require_components
97
97
 
98
98
  load_app_code
99
99
 
@@ -104,14 +104,18 @@ module Volt
104
104
  AssetFiles.from_cache(app_url, 'main', component_paths)
105
105
  end
106
106
 
107
- reset_query_pool!
108
-
109
107
  # Setup the middleware that we can only setup after all components boot.
110
108
  setup_postboot_middleware
111
109
 
112
110
  setup_routes
113
111
 
112
+ # Try to load the db to make sure its connected. Also will run
113
+ # migrations on volt-sql in dev
114
+ trigger!('boot')
115
+
114
116
  start_message_bus
117
+
118
+ trigger!('post_boot')
115
119
  end
116
120
  end
117
121
 
@@ -149,6 +153,10 @@ module Volt
149
153
  def setup_browser
150
154
  @browser = Browser.new(self)
151
155
  end
156
+
157
+ def inspect
158
+ "#<#{self.class.to_s}:#{object_id}>"
159
+ end
152
160
  end
153
161
  end
154
162
 
@@ -0,0 +1,24 @@
1
+ # Volt.current_app.properties provides a hash like collection that backs to
2
+ # the database. This is useful for storing global app property. (A common use
3
+ # might be checking if something like an S3 bucket has been created without
4
+ # the need to make an external API request.)
5
+ class Properties
6
+ def [](key)
7
+ prop = Volt.current_app.store.volt_app_properties.where(name: key).first.sync
8
+
9
+ if prop
10
+ prop.value
11
+ else
12
+ nil
13
+ end
14
+ end
15
+
16
+ def []=(key, value)
17
+ unless value.is_a?(String)
18
+ raise "property values must be a string, you passed #{value.inspect}"
19
+ end
20
+
21
+ Volt.current_app.store.volt_app_properties.
22
+ update_or_create({name: key}, {value: value}).sync
23
+ end
24
+ end
@@ -4,15 +4,19 @@ unless RUBY_PLATFORM == 'opal'
4
4
  require 'volt/server/middleware/middleware_stack'
5
5
  require 'volt/server/middleware/default_middleware_stack'
6
6
  require 'volt/volt/core'
7
+ require 'volt/volt/properties'
7
8
 
8
9
  end
9
10
 
10
11
  module Volt
11
12
  module ServerSetup
12
13
  module App
14
+ attr_reader :live_query_pool, :query_subscription_pool,
15
+ :channel_query_subscriptions
16
+
13
17
  # Include Eventable to allow for lifecycle callbacks
14
18
  include Eventable
15
-
19
+
16
20
  # The root url is where the volt app is mounted
17
21
  attr_reader :root_url
18
22
  # The app url is where the app folder (and sprockets) is mounted
@@ -56,6 +60,10 @@ module Volt
56
60
 
57
61
  end
58
62
 
63
+ def properties
64
+ @properties ||= Properties.new
65
+ end
66
+
59
67
  def setup_preboot_middleware
60
68
  @middleware = MiddlewareStack.new
61
69
  DefaultMiddlewareStack.preboot_setup(self, @middleware)
@@ -106,12 +114,22 @@ module Volt
106
114
  def reset_query_pool!
107
115
  if RUBY_PLATFORM != 'opal'
108
116
  # The load path isn't setup at the top of app.rb, so we wait to require
109
- require 'volt/tasks/live_query/live_query_pool'
117
+ require 'volt/queries/live_query_pool'
118
+ require 'volt/queries/query_subscription_pool'
119
+
120
+ # Try to reset the previous
121
+ @database.try(:reset!)
110
122
 
111
123
  # Setup LiveQueryPool for the app
112
- @database = Volt::DataStore.fetch
124
+ @query_subscription_pool.clear if @query_subscription_pool
125
+ @database = Volt::DataStore.fetch(self)
113
126
  @live_query_pool = LiveQueryPool.new(@database, self)
114
- @channel_live_queries = {}
127
+ @query_subscription_pool = QuerySubscriptionPool.new(self)
128
+
129
+ # The channel_query_subscriptions, keeps a list of query subscriptions
130
+ # for each channel. We store this here instead of on the channel
131
+ # because in ForkingServer, channel is a DrbObject instance.
132
+ @channel_query_subscriptions = {}
115
133
  end
116
134
  end
117
135
 
@@ -125,12 +143,17 @@ module Volt
125
143
  # updating each other.
126
144
  unless Volt.env.test?
127
145
  # Start the message bus
128
- bus_name = Volt.config.message_bus.try(:bus_name) || 'peer_to_peer'
146
+ bus_name = if ((bus = Volt.config.message_bus) && name = bus.bus_name)
147
+ name
148
+ else
149
+ 'peer_to_peer'
150
+ end
151
+
129
152
  begin
130
153
  message_bus_class = MessageBus.const_get(bus_name.camelize)
131
154
  rescue NameError => e
132
155
  raise "message bus name #{bus_name} was not found, be sure its "
133
- + "gem is included in the gemfile."
156
+ + "gem is included in the gemfile."
134
157
  end
135
158
 
136
159
  @message_bus = message_bus_class.new(self)
@@ -148,4 +171,4 @@ module Volt
148
171
  end
149
172
  end
150
173
  end
151
- end
174
+ end
@@ -47,9 +47,21 @@ module Volt
47
47
  previous_id = Thread.current['with_user_id']
48
48
  Thread.current['with_user_id'] = user_id
49
49
 
50
- yield
50
+ # In the console, you can use without a block
51
+ if block_given?
52
+ begin
53
+ result = yield
54
+ ensure
55
+ Thread.current['with_user_id'] = previous_id
56
+ end
57
+ else
58
+ # No block given, only allowed in the console
59
+ unless Volt.try(:console?)
60
+ raise "as_user requires a block, except in the console."
61
+ end
62
+ end
51
63
 
52
- Thread.current['with_user_id'] = previous_id
64
+ result
53
65
  end
54
66
 
55
67
  unless RUBY_PLATFORM == 'opal'
@@ -116,7 +128,7 @@ module Volt
116
128
  def logout
117
129
  # Notify the backend so we can remove the user_id from the user's channel
118
130
  UserTasks.logout
119
-
131
+
120
132
  # Remove the cookie so user is no longer logged in
121
133
  Volt.current_app.cookies.delete(:user_id)
122
134
  end
@@ -3,7 +3,11 @@ source 'https://rubygems.org'
3
3
  gem 'volt', path: '../../../'
4
4
 
5
5
  # volt uses mongo as the default data store.
6
- gem 'volt-mongo'
6
+ gem 'volt-sql'
7
+ gem 'sqlite3'
8
+ gem 'pg', '~> 0.18.2'
9
+ gem 'pg_json', '~> 0.1.29'
10
+
7
11
 
8
12
  # The following gem's are optional for themeing
9
13
  # Twitter bootstrap
@@ -29,6 +29,7 @@ client '/login', component: 'user_templates', controller: 'login'
29
29
  get '/simple_http', controller: 'simple_http', action: 'index'
30
30
  get '/simple_http/store', controller: 'simple_http', action: 'show'
31
31
  post '/simple_http/upload', controller: 'simple_http', action: 'upload'
32
+ post '/simple_http', controller: 'simple_http', action: 'create'
32
33
 
33
34
  # Route for file uploads
34
35
  client '/upload', controller: 'upload', action: 'index'
@@ -2,7 +2,7 @@ module Main
2
2
  class SaveController < Volt::ModelController
3
3
  def add_post
4
4
  store.posts.create(page._new_post.to_h).fail do |err|
5
- flash._notices << err.inspect
5
+ flash._notices << err.message
6
6
  end
7
7
  end
8
8
  end
@@ -8,6 +8,10 @@ module Main
8
8
  render text: "You had me at #{store._simple_http_tests.first._name.sync}"
9
9
  end
10
10
 
11
+ def create
12
+ render text: params.inspect
13
+ end
14
+
11
15
  def upload
12
16
  uploaded = params._file._tempfile
13
17
  File.open('tmp/uploaded_file', 'wb') { |f| f.write(uploaded.read) }
@@ -1,9 +1,11 @@
1
+ require 'volt/helpers/time'
2
+
1
3
  module Main
2
4
  class TodosController < Volt::ModelController
3
5
  model :store
4
6
 
5
7
  def add_todo
6
- _todos << { name: page._new_todo }
8
+ _todos.create({ name: page._new_todo, created_at: VoltTime.new })
7
9
  page._new_todo = ''
8
10
  end
9
11
 
@@ -12,7 +14,7 @@ module Main
12
14
  end
13
15
 
14
16
  def completed
15
- _todos.count(&:_completed)
17
+ todos.count(&:completed)
16
18
  end
17
19
  end
18
20
  end
@@ -2,5 +2,4 @@ class Post < Volt::Model
2
2
  field :title, String
3
3
 
4
4
  validate :title, length: 5
5
-
6
5
  end
@@ -0,0 +1,4 @@
1
+ class Todo < Volt::Model
2
+ field :name, String
3
+ field :completed, Volt::Boolean
4
+ end
@@ -0,0 +1,10 @@
1
+ <:Subject>
2
+ Password Reset
3
+
4
+ <:Html>
5
+ Reset your password <a href="{{ reset_url }}" target="_new">here</a>.
6
+
7
+ <a href="{{ reset_url }}">Reset Password</a>
8
+
9
+ <:Text>
10
+ Password Reset
@@ -12,6 +12,7 @@
12
12
  <td>{{ idx+1 }}.</td>
13
13
  <td><input type="checkbox" checked="{{ todo._completed }}"></td>
14
14
  <td class="name {{ if todo._completed }}complete{{ end }}">{{todo._name}}</td>
15
+ <td>{{ todo._created_at.time_distance_in_words }}</td>
15
16
  <td><button e-click="remove_todo(todo)">X</button></td>
16
17
  </tr>
17
18
  {{ end }}
@@ -21,5 +22,6 @@
21
22
  <div class="form-group">
22
23
  <label>Todo</label>
23
24
  <input class="form-control" id="newtodo" type="text" value="{{ page._new_todo }}" />
25
+
24
26
  </div>
25
27
  </form>
@@ -1,3 +1,5 @@
1
1
  Volt.setup do |config|
2
2
  config.app_secret = 'vFYUzMiPIdMw1Iox0ggDpBYorLm_d55YtRPc0eGrdCpoN4h9E5FcWySIT_D8JIEOllU'
3
+
4
+ # config.db.uri = 'postgres://ryanstout:@localhost:5432/kitchen_sink'
3
5
  end
@@ -0,0 +1,7 @@
1
+ class Migration1 < Volt::Migration
2
+ def up
3
+ end
4
+
5
+ def down
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Migration2 < Volt::Migration
2
+ def up
3
+ end
4
+
5
+ def down
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Migration3 < Volt::Migration
2
+ def up
3
+ end
4
+
5
+ def down
6
+ end
7
+ end
@@ -4,6 +4,7 @@ require 'volt/extra_core/array'
4
4
  class TestClassAttributes
5
5
  class_attribute :some_data
6
6
  class_attribute :attr2
7
+ class_attribute :dup_test
7
8
  end
8
9
 
9
10
  class TestSubClassAttributes < TestClassAttributes
@@ -41,4 +42,13 @@ describe 'extra_core class addons' do
41
42
  expect(TestSubClassAttributes.attr2).to eq(2)
42
43
  expect(TestSubClassAttributes2.attr2).to eq(1)
43
44
  end
45
+
46
+ it 'should dup the class_attribute when you read it from a child class' do
47
+ TestClassAttributes.dup_test = {one: 1}
48
+
49
+ TestSubClassAttributes.dup_test[:two] = 2
50
+
51
+ expect(TestClassAttributes.dup_test).to eq({one: 1})
52
+ expect(TestSubClassAttributes.dup_test).to eq({one: 1, two: 2})
53
+ end
44
54
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'volt/helpers/time'
3
+
4
+ describe Volt::Duration do
5
+ it 'should return a sentence describing the time' do
6
+ dist = 1.hour + 2.days + 1.month + 305.seconds + 1.5.days
7
+
8
+ expect(dist.duration_in_words(nil, :seconds)).to eq('1 month, 3 days, 13 hours, 5 minutes, and 5 seconds')
9
+ end
10
+
11
+ it 'should show a recent_message and' do
12
+ expect(0.seconds.duration_in_words).to eq('just now')
13
+ expect(1.minute.duration_in_words).to eq('1 minute')
14
+ expect(0.seconds.duration_in_words(nil)).to eq('just now')
15
+ end
16
+
17
+ it 'should trim to the unit count' do
18
+ dist = 1.hour + 2.days + 1.month + 305.seconds + 1.5.days
19
+ expect(dist.duration_in_words(3)).to eq('1 month, 3 days, and 13 hours')
20
+ expect(dist.duration_in_words(2)).to eq('1 month and 3 days')
21
+ end
22
+
23
+ it 'should return distance in words' do
24
+ time1 = (1.hours + 5.minutes).ago
25
+ expect(time1.time_distance_in_words).to eq('1 hour and 5 minutes ago')
26
+
27
+ time2 = VoltTime.now
28
+ time3 = time2 - (3.hours + 1.day + 2.seconds)
29
+
30
+ expect(time3.time_distance_in_words(time2, nil, :seconds)).to eq('1 day, 3 hours, and 2 seconds ago')
31
+
32
+ time4 = 1.second.ago
33
+ expect(time4.time_distance_in_words).to eq('just now')
34
+ end
35
+ end