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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +6 -1
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/app/volt/models/active_volt_instance.rb +8 -6
- data/app/volt/models/user.rb +11 -2
- data/app/volt/models/volt_app_property.rb +8 -0
- data/app/volt/tasks/query_tasks.rb +23 -31
- data/app/volt/tasks/store_tasks.rb +2 -2
- data/app/volt/tasks/volt_admin_tasks.rb +24 -0
- data/docs/UPGRADE_GUIDE.md +6 -0
- data/lib/volt.rb +19 -12
- data/lib/volt/boot.rb +1 -0
- data/lib/volt/cli.rb +19 -8
- data/lib/volt/cli/console.rb +0 -1
- data/lib/volt/cli/generators.rb +14 -3
- data/lib/volt/cli/migrate.rb +26 -0
- data/lib/volt/config.rb +17 -4
- data/lib/volt/controllers/http_controller.rb +12 -0
- data/lib/volt/data_stores/base_adaptor_client.rb +2 -2
- data/lib/volt/data_stores/base_adaptor_server.rb +2 -0
- data/lib/volt/data_stores/data_store.rb +20 -14
- data/lib/volt/extra_core/class.rb +28 -14
- data/lib/volt/extra_core/hash.rb +5 -0
- data/lib/volt/extra_core/string.rb +3 -1
- data/lib/volt/helpers/time.rb +9 -43
- data/lib/volt/helpers/time/calculations.rb +204 -0
- data/lib/volt/helpers/time/distance.rb +63 -0
- data/lib/volt/helpers/time/duration.rb +71 -0
- data/lib/volt/helpers/time/local_calculations.rb +49 -0
- data/lib/volt/helpers/time/local_volt_time.rb +23 -0
- data/lib/volt/helpers/time/numeric.rb +59 -0
- data/lib/volt/helpers/time/volt_time.rb +170 -0
- data/lib/volt/models.rb +5 -0
- data/lib/volt/models/array_model.rb +33 -6
- data/lib/volt/models/associations.rb +146 -23
- data/lib/volt/models/buffer.rb +38 -41
- data/lib/volt/models/cursor.rb +15 -0
- data/lib/volt/models/errors.rb +11 -0
- data/lib/volt/models/field_helpers.rb +108 -68
- data/lib/volt/models/helpers/array_model.rb +4 -0
- data/lib/volt/models/helpers/base.rb +8 -1
- data/lib/volt/models/helpers/change_helpers.rb +31 -12
- data/lib/volt/models/helpers/defaults.rb +15 -0
- data/lib/volt/models/location.rb +20 -6
- data/lib/volt/models/migrations/migration.rb +23 -0
- data/lib/volt/models/migrations/migration_runner.rb +146 -0
- data/lib/volt/models/model.rb +38 -1
- data/lib/volt/models/permissions.rb +8 -1
- data/lib/volt/models/persistors/array_store.rb +87 -8
- data/lib/volt/models/persistors/base.rb +19 -0
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/page.rb +4 -1
- data/lib/volt/models/persistors/query/query_identifier.rb +102 -0
- data/lib/volt/models/persistors/query/query_listener.rb +57 -12
- data/lib/volt/models/root_models/root_models.rb +19 -0
- data/lib/volt/models/url.rb +11 -2
- data/lib/volt/models/validations/validations.rb +5 -2
- data/lib/volt/models/validators/type_validator.rb +11 -0
- data/lib/volt/models/validators/unique_validator.rb +2 -2
- data/lib/volt/page/bindings/attribute_binding.rb +23 -1
- data/lib/volt/page/targets/attribute_section.rb +7 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +44 -18
- data/lib/volt/page/targets/binding_document/tag_node.rb +41 -0
- data/lib/volt/page/tasks.rb +16 -8
- data/lib/volt/queries/live_query.rb +109 -0
- data/lib/volt/queries/live_query_pool.rb +58 -0
- data/lib/volt/queries/live_subquery.rb +0 -0
- data/lib/volt/queries/query_association_splitter.rb +31 -0
- data/lib/volt/queries/query_diff.rb +100 -0
- data/lib/volt/queries/query_runner.rb +110 -0
- data/lib/volt/queries/query_subscription.rb +80 -0
- data/lib/volt/queries/query_subscription_pool.rb +37 -0
- data/lib/volt/reactive/eventable.rb +8 -0
- data/lib/volt/reactive/reactive_array.rb +0 -4
- data/lib/volt/router/routes.rb +81 -31
- data/lib/volt/server/message_bus/base_message_bus.rb +9 -3
- data/lib/volt/server/message_bus/peer_to_peer.rb +6 -6
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
- data/lib/volt/server/middleware/default_middleware_stack.rb +12 -8
- data/lib/volt/server/rack/component_paths.rb +31 -4
- data/lib/volt/server/rack/http_content_types.rb +62 -0
- data/lib/volt/server/rack/http_resource.rb +1 -1
- data/lib/volt/server/rack/index_files.rb +8 -1
- data/lib/volt/server/rack/opal_files.rb +16 -1
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +32 -1
- data/lib/volt/server/socket_connection_handler.rb +16 -7
- data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -3
- data/lib/volt/spec/capybara.rb +4 -3
- data/lib/volt/spec/setup.rb +5 -0
- data/lib/volt/tasks/dispatcher.rb +3 -1
- data/lib/volt/utils/data_transformer.rb +4 -4
- data/lib/volt/utils/ejson.rb +19 -6
- data/lib/volt/utils/promise_extensions.rb +1 -1
- data/lib/volt/utils/time_opal_patch.rb +749 -0
- data/lib/volt/utils/time_patch.rb +11 -4
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +19 -11
- data/lib/volt/volt/properties.rb +24 -0
- data/lib/volt/volt/server_setup/app.rb +30 -7
- data/lib/volt/volt/users.rb +15 -3
- data/spec/apps/kitchen_sink/Gemfile +5 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/save_controller.rb +1 -1
- data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +4 -2
- data/spec/apps/kitchen_sink/app/main/models/post.rb +0 -1
- data/spec/apps/kitchen_sink/app/main/models/todo.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/views/mailers/reset_password.html +10 -0
- data/spec/apps/kitchen_sink/app/main/views/todos/index.html +2 -0
- data/spec/apps/kitchen_sink/config/app.rb +2 -0
- data/spec/apps/migrations/config/db/migrations/1445111704_migration1.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445113517_migration2.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445115200_migration3.rb +7 -0
- data/spec/extra_core/class_spec.rb +10 -0
- data/spec/helpers/distance_spec.rb +35 -0
- data/spec/helpers/duration_spec.rb +160 -0
- data/spec/helpers/volt_time_spec.rb +275 -0
- data/spec/integration/callbacks_spec.rb +2 -1
- data/spec/integration/http_endpoints_spec.rb +4 -0
- data/spec/integration/save_spec.rb +1 -1
- data/spec/integration/todos_spec.rb +7 -5
- data/spec/models/array_model_spec.rb +17 -3
- data/spec/models/associations_spec.rb +48 -1
- data/spec/models/field_helpers_spec.rb +7 -3
- data/spec/models/migrations/migration_runner_spec.rb +69 -0
- data/spec/models/model_spec.rb +42 -8
- data/spec/models/permissions_spec.rb +20 -8
- data/spec/models/persistors/array_store_spec.rb +18 -0
- data/spec/models/persistors/page_spec.rb +15 -10
- data/spec/models/persistors/store_spec.rb +13 -3
- data/spec/models/url_spec.rb +4 -3
- data/spec/models/user_spec.rb +6 -3
- data/spec/models/user_validation_spec.rb +3 -3
- data/spec/models/validations_spec.rb +4 -0
- data/spec/models/validators/block_validations_spec.rb +9 -5
- data/spec/models/validators/email_validator_spec.rb +2 -0
- data/spec/models/validators/lifecycle_callbacks_spec.rb +86 -0
- data/spec/models/validators/unique_validator_spec.rb +1 -0
- data/spec/page/path_string_renderer_spec.rb +5 -0
- data/spec/queries/live_query_spec.rb +16 -0
- data/spec/queries/query_association_splitter_spec.rb +14 -0
- data/spec/queries/query_diff_spec.rb +132 -0
- data/spec/queries/query_identifier_spec.rb +98 -0
- data/spec/queries/query_runner_spec.rb +63 -0
- data/spec/queries/query_tracker_spec.rb +141 -0
- data/spec/router/routes_spec.rb +52 -21
- data/spec/server/middleware/rack_content_types_spec.rb +78 -0
- data/spec/server/rack/asset_files_spec.rb +38 -30
- data/spec/spec_helper.rb +8 -0
- data/spec/utils/ejson_spec.rb +9 -8
- data/spec/utils/ejson_volt_time_spec.rb +65 -0
- data/templates/migration/migration.rb.tt +9 -0
- data/templates/newgem/gitignore.tt +1 -0
- data/templates/project/Gemfile.tt +19 -2
- data/templates/project/README.md.tt +6 -1
- data/templates/project/app/main/config/dependencies.rb +6 -0
- data/templates/project/config/app.rb.tt +18 -4
- data/volt.gemspec +2 -2
- metadata +73 -16
- data/app/volt/tasks/live_query/live_query_pool.rb +0 -48
- data/app/volt/tasks/live_query/query_tracker.rb +0 -92
- data/spec/tasks/live_query_spec.rb +0 -18
- data/spec/tasks/query_tasks.rb +0 -7
- 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
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, :
|
|
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
|
-
|
|
94
|
+
reset_query_pool!
|
|
91
95
|
|
|
92
|
-
|
|
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/
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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.
|
|
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
|
-
|
|
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
|
data/lib/volt/volt/users.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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-
|
|
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'
|
|
@@ -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
|
|
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
|
-
|
|
17
|
+
todos.count(&:completed)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
end
|
|
@@ -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>
|
|
@@ -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
|