volt 0.9.6 → 0.9.7.pre2

Sign up to get free protection for your applications and to get access to all the features.
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