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
data/lib/volt/cli/generators.rb
CHANGED
|
@@ -28,9 +28,9 @@ module Generators
|
|
|
28
28
|
method_option :bin, type: :boolean, default: false, aliases: '-b', banner: 'Generate a binary for your library.'
|
|
29
29
|
method_option :test, type: :string, lazy_default: 'rspec', aliases: '-t', banner: "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
|
|
30
30
|
method_option :edit, type: :string, aliases: '-e',
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
lazy_default: [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find { |e| !e.nil? && !e.empty? },
|
|
32
|
+
required: false, banner: '/path/to/your/editor',
|
|
33
|
+
desc: 'Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)'
|
|
34
34
|
method_option :coc, type: :boolean, desc: "Generate a code of conduct file. Set a default with `bundle config gem.coc true`."
|
|
35
35
|
method_option :mit, type: :boolean, desc: "Generate an MIT license file"
|
|
36
36
|
|
|
@@ -100,6 +100,17 @@ module Generators
|
|
|
100
100
|
controller(name, component) unless controller_exists?(name, component)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
desc 'migration NAME', 'creates a migration with the name specified'
|
|
104
|
+
method_option :name, type: :string, banner: 'The name of the migration file'
|
|
105
|
+
def migration(name)
|
|
106
|
+
timestamp = Time.now.to_i
|
|
107
|
+
file_name = "#{timestamp}_#{name.underscore}"
|
|
108
|
+
class_name = name.camelize
|
|
109
|
+
output_file = "#{Dir.pwd}/config/db/migrations/#{file_name}.rb"
|
|
110
|
+
|
|
111
|
+
template('migration/migration.rb.tt', output_file, class_name: class_name)
|
|
112
|
+
end
|
|
113
|
+
|
|
103
114
|
private
|
|
104
115
|
|
|
105
116
|
def controller_exists?(name, component = 'main')
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Volt
|
|
2
|
+
module CliSubclasses
|
|
3
|
+
class Migrate < Thor
|
|
4
|
+
desc 'migrate up', 'migrate up'
|
|
5
|
+
method_option :version, type: :string, banner: 'Migrate up to the specified VERSION'
|
|
6
|
+
def up(version=nil)
|
|
7
|
+
Volt.boot(Dir.pwd)
|
|
8
|
+
require 'volt/models/migrations/migration_runner'
|
|
9
|
+
|
|
10
|
+
Volt::MigrationRunner.new.run(:up, version)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc 'migrate down', 'migrate down'
|
|
14
|
+
method_option :version, type: :string, banner: 'Migrate down to the specified VERSION'
|
|
15
|
+
def down(version=nil)
|
|
16
|
+
Volt.boot(Dir.pwd)
|
|
17
|
+
require 'volt/models/migrations/migration_runner'
|
|
18
|
+
|
|
19
|
+
Volt::MigrationRunner.new.run(:down, version)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
default_task :up
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/volt/config.rb
CHANGED
|
@@ -48,12 +48,25 @@ else
|
|
|
48
48
|
class << self
|
|
49
49
|
def defaults
|
|
50
50
|
app_name = File.basename(Dir.pwd)
|
|
51
|
+
|
|
52
|
+
db_opts = {
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# The user can use any DB_KEY to assign a value into the database
|
|
56
|
+
# config.
|
|
57
|
+
ENV.keys.grep(/^DB_/).each do |db_key|
|
|
58
|
+
db_opts[db_key.gsub(/^DB_/, '').downcase] = ENV[db_key]
|
|
59
|
+
end
|
|
60
|
+
|
|
51
61
|
opts = {
|
|
52
62
|
app_name: app_name,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
db: db_opts,
|
|
64
|
+
# db_name: (ENV['DB_NAME'] || (app_name + '_' + Volt.env.to_s)).gsub('.', '_'),
|
|
65
|
+
# db_host: ENV['DB_HOST'] || 'localhost',
|
|
66
|
+
# db_port: (ENV['DB_PORT'] || 27_017).to_i,
|
|
67
|
+
# db_driver: ENV['DB_DRIVER'] || 'postgres',
|
|
68
|
+
# ## TEMP
|
|
69
|
+
# db_uri: "postgres://ryanstout:@localhost:5432/#{(ENV['DB_NAME'] || (app_name + '_' + Volt.env.to_s)).gsub('.', '_')}",
|
|
57
70
|
|
|
58
71
|
# a list of components which should be included in all components
|
|
59
72
|
default_components: ['volt'],
|
|
@@ -65,6 +65,18 @@ module Volt
|
|
|
65
65
|
response_body << body
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
def send_file(path)
|
|
69
|
+
if File.exists?(path)
|
|
70
|
+
head(200, {})
|
|
71
|
+
else
|
|
72
|
+
raise "Invalid SendFile: #{path}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# You can pass back a File to rack and it will stream from it and close it
|
|
76
|
+
# when its done.
|
|
77
|
+
@response_body = File.open(path, 'rb')
|
|
78
|
+
end
|
|
79
|
+
|
|
68
80
|
def respond
|
|
69
81
|
unless @response_status
|
|
70
82
|
# render was not called, show an error
|
|
@@ -24,8 +24,8 @@ module Volt
|
|
|
24
24
|
Volt::ArrayModel.proxy_to_persistor(*method_names)
|
|
25
25
|
|
|
26
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)
|
|
27
|
+
Volt::Persistors::ArrayStore.send(:define_method, method_name) do |*args, &block|
|
|
28
|
+
add_query_part(method_name, *args, &block)
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
require 'volt/data_stores/base_adaptor_server'
|
|
2
|
+
require 'thread'
|
|
2
3
|
|
|
3
4
|
module Volt
|
|
4
5
|
class DataStore
|
|
5
|
-
|
|
6
|
-
# Cache the driver
|
|
7
|
-
return @adaptor if @adaptor
|
|
6
|
+
@@data_store_mutex = Mutex.new
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
# TODO: cache based on volt_app
|
|
9
|
+
def self.fetch(volt_app)
|
|
10
|
+
@@data_store_mutex.synchronize do
|
|
11
|
+
# Cache the driver
|
|
12
|
+
return @adaptor if @adaptor
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
database_name = Volt.config.db_driver
|
|
15
|
+
adaptor_name = database_name.camelize + 'AdaptorServer'
|
|
16
|
+
|
|
17
|
+
root = Volt::DataStore
|
|
18
|
+
if root.const_defined?(adaptor_name)
|
|
19
|
+
adaptor_name = root.const_get(adaptor_name)
|
|
20
|
+
@adaptor = adaptor_name.new(volt_app)
|
|
21
|
+
else
|
|
22
|
+
raise "#{database_name} is not a supported database (as configured by Volt.config.db_driver), you might be missing a volt-#{database_name} gem"
|
|
23
|
+
end
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
@adaptor
|
|
26
|
+
end
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
def self.adaptor_client
|
|
@@ -26,7 +32,7 @@ module Volt
|
|
|
26
32
|
ds_name = Volt.config.public.datastore_name
|
|
27
33
|
unless ds_name
|
|
28
34
|
raise "No data store configured, please include volt-mongo or " +
|
|
29
|
-
|
|
35
|
+
"another similar gem."
|
|
30
36
|
end
|
|
31
37
|
adaptor_class_name = ds_name.capitalize + "AdaptorClient"
|
|
32
38
|
Volt::DataStore.const_get(adaptor_class_name)
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
class Class
|
|
2
2
|
# Provides a way to make class attributes that inherit. Pass
|
|
3
|
-
# in symbols for attribute names
|
|
3
|
+
# in symbols for attribute names. When the class attribute is accessed from
|
|
4
|
+
# a sublcass, it will be duped. This allows the children to receive the value
|
|
5
|
+
# from their parent, but then change it only in the child.
|
|
6
|
+
#
|
|
7
|
+
# NOTE: This does not do a deep clone, so multi-nested values may be changed.
|
|
4
8
|
def class_attribute(*attrs)
|
|
5
9
|
attrs.each do |name|
|
|
6
|
-
|
|
10
|
+
name = name.to_sym
|
|
11
|
+
ivar = :"@#{name}"
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
assigner = :"#{name}="
|
|
14
|
+
define_singleton_method(assigner) do |val|
|
|
15
|
+
instance_variable_set(ivar, val)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
define_singleton_method(name) do
|
|
19
|
+
if instance_variable_defined?(ivar)
|
|
20
|
+
# Get the value from the instance variable
|
|
21
|
+
val = instance_variable_get(ivar)
|
|
22
|
+
else
|
|
23
|
+
# Fetch from parent and dup
|
|
24
|
+
if superclass.respond_to?(name)
|
|
25
|
+
val = superclass.send(name)
|
|
26
|
+
else
|
|
27
|
+
val = nil
|
|
28
|
+
end
|
|
29
|
+
# We need the numeric check because of: https://github.com/opal/opal/issues/1122
|
|
30
|
+
unless val.is_a?(Numeric)
|
|
31
|
+
val = val.dup rescue val
|
|
32
|
+
end
|
|
9
33
|
|
|
10
|
-
|
|
11
|
-
singleton_class.class_eval do
|
|
12
|
-
remove_possible_method(name)
|
|
13
|
-
define_method(name) { val }
|
|
34
|
+
instance_variable_set(ivar, val)
|
|
14
35
|
end
|
|
15
36
|
|
|
16
37
|
val
|
|
17
38
|
end
|
|
18
39
|
end
|
|
19
40
|
end
|
|
20
|
-
|
|
21
|
-
# Removes a method if it is defined.
|
|
22
|
-
def remove_possible_method(method)
|
|
23
|
-
if method_defined?(method) || private_method_defined?(method)
|
|
24
|
-
undef_method(method)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
41
|
end
|
data/lib/volt/extra_core/hash.rb
CHANGED
|
@@ -16,7 +16,9 @@ class String
|
|
|
16
16
|
# Returns the underscore version of a string. If it is already underscore, it should
|
|
17
17
|
# return the same string.
|
|
18
18
|
def underscore
|
|
19
|
-
gsub(
|
|
19
|
+
gsub('::', '/')
|
|
20
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
21
|
+
.downcase
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def dasherize
|
data/lib/volt/helpers/time.rb
CHANGED
|
@@ -1,43 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
|
|
11
|
-
# <tt>:sec</tt>, <tt>:usec</tt>.
|
|
12
|
-
#
|
|
13
|
-
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
|
|
14
|
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
|
|
15
|
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
|
|
16
|
-
def change(options)
|
|
17
|
-
new_year = options.fetch(:year, year)
|
|
18
|
-
new_month = options.fetch(:month, month)
|
|
19
|
-
new_day = options.fetch(:day, day)
|
|
20
|
-
new_hour = options.fetch(:hour, hour)
|
|
21
|
-
new_min = options.fetch(:min, options[:hour] ? 0 : min)
|
|
22
|
-
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
|
|
23
|
-
# new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
|
|
24
|
-
|
|
25
|
-
# TODO: Opal doesn't have rational yet, so usec's don't get added in right yet
|
|
26
|
-
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec, utc_offset)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def beginning_of_day
|
|
30
|
-
#(self - seconds_since_midnight).change(usec: 0)
|
|
31
|
-
change(hour: 0, min: 0, sec: 0)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
|
|
35
|
-
def end_of_day
|
|
36
|
-
change(
|
|
37
|
-
hour: 23,
|
|
38
|
-
min: 59,
|
|
39
|
-
sec: 59,
|
|
40
|
-
# usec: Rational(999999999, 1000)
|
|
41
|
-
)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
1
|
+
require 'time'
|
|
2
|
+
require 'date'
|
|
3
|
+
require 'volt/helpers/time/volt_time'
|
|
4
|
+
require 'volt/helpers/time/local_volt_time' if RUBY_PLATFORM == 'opal'
|
|
5
|
+
require 'volt/helpers/time/calculations'
|
|
6
|
+
require 'volt/helpers/time/local_calculations' if RUBY_PLATFORM == 'opal'
|
|
7
|
+
require 'volt/helpers/time/duration'
|
|
8
|
+
require 'volt/helpers/time/numeric'
|
|
9
|
+
require 'volt/helpers/time/distance'
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Much of this class was borrowed from ActiveSupport:
|
|
2
|
+
# https://github.com/rails/rails/blob/ca9736e78ca9348e785a5c78c8cc085c0c2d4731/activesupport/lib/active_support/core_ext/time/calculations.rb
|
|
3
|
+
|
|
4
|
+
class VoltTime
|
|
5
|
+
|
|
6
|
+
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
# Returns the number of days in a month. If no year is provided assumes
|
|
10
|
+
# the current year
|
|
11
|
+
def days_in_month(month, year = now.year)
|
|
12
|
+
if month == 2 && leap?(year)
|
|
13
|
+
29
|
|
14
|
+
else
|
|
15
|
+
COMMON_YEAR_DAYS_IN_MONTH[month]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns true if the year is a leap year, otherwise false
|
|
20
|
+
def leap?(year)
|
|
21
|
+
if year%400 == 0 || (year%4 == 0 && year%100 != 0)
|
|
22
|
+
true
|
|
23
|
+
else
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns a new Time where one or more of the elements have been changed according
|
|
30
|
+
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
|
|
31
|
+
# <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
|
|
32
|
+
# then minute, sec, and usec is set to 0. If the hour and minute is passed, then
|
|
33
|
+
# sec and usec is set to 0. The +options+ parameter takes a hash with any of these
|
|
34
|
+
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
|
|
35
|
+
# <tt>:sec</tt>
|
|
36
|
+
#
|
|
37
|
+
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
|
|
38
|
+
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
|
|
39
|
+
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
|
|
40
|
+
def change(options)
|
|
41
|
+
new_year = options.fetch(:year, year)
|
|
42
|
+
new_month = options.fetch(:month, month)
|
|
43
|
+
new_day = options.fetch(:day, day)
|
|
44
|
+
new_hour = options.fetch(:hour, hour)
|
|
45
|
+
new_min = options.fetch(:min, options[:hour] ? 0 : min)
|
|
46
|
+
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
|
|
47
|
+
VoltTime.new(:utc, new_year, new_month, new_day, new_hour, new_min, new_sec)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Returns a new time that has been advanced according to the +options+
|
|
51
|
+
# parameter. The +options+ parameter is a hash with any of these keys:
|
|
52
|
+
# <tt>:years</tt>, <tt>:months</tt>, <tt>:days</tt>, <tt>:hours</tt>, <tt>:mins</tt>,
|
|
53
|
+
# <tt>:secs</tt>
|
|
54
|
+
def advance(options)
|
|
55
|
+
t = self
|
|
56
|
+
t = advance_years(options[:years]) if options[:years]
|
|
57
|
+
t = advance_months(options[:months]) if options[:months]
|
|
58
|
+
t = advance_days(options[:days]) if options[:days]
|
|
59
|
+
t = advance_hours(options[:hours]) if options[:hours]
|
|
60
|
+
t = advance_minutes(options[:mins]) if options[:mins]
|
|
61
|
+
t = advance_seconds(options[:secs]) if options[:secs]
|
|
62
|
+
t
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Compares two VoltTime object to the given accuracy
|
|
66
|
+
# The +accuracy+ parameter can be <tt>:year</tt>, <tt>:month</tt>
|
|
67
|
+
# <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>
|
|
68
|
+
# Returns 0 if the two dates are the same to the required accuracy
|
|
69
|
+
# (e.g. the same year or the same day), 1 if the date called on is later
|
|
70
|
+
# than the parameter, or -1 if the date called on is earlier than the parameter
|
|
71
|
+
def compare(other, accuracy)
|
|
72
|
+
if accuracy == :year
|
|
73
|
+
year <=> other.year
|
|
74
|
+
elsif accuracy == :month
|
|
75
|
+
compare_date_components(other, :month, :year)
|
|
76
|
+
elsif accuracy == :day
|
|
77
|
+
compare_date_components(other, :day, :month)
|
|
78
|
+
elsif [:hour, :min, :sec].include? accuracy
|
|
79
|
+
change(accuracy => send(accuracy)) <=> other.change(accuracy => other.send(accuracy))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def compare?(other, accuracy)
|
|
84
|
+
compare(other, accuracy) == 0 ? true : false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Advances time by a number of years
|
|
88
|
+
def advance_years(years)
|
|
89
|
+
advance_to_date(to_date >> (years*12))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Advances time by a number of months
|
|
93
|
+
def advance_months(months)
|
|
94
|
+
advance_to_date(to_date >> months)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Advances time by a number of days
|
|
98
|
+
def advance_days(days)
|
|
99
|
+
advance_to_date(to_date + days)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Advances time by a number of seconds
|
|
103
|
+
def advance_seconds(secs)
|
|
104
|
+
self + secs
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Advances time by a number of hours
|
|
108
|
+
def advance_hours(hours)
|
|
109
|
+
self + (hours * 60 * 60)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Advances time by a number of minutes
|
|
113
|
+
def advance_minutes(mins)
|
|
114
|
+
self + (mins * 60)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Converts the time to a date
|
|
118
|
+
def to_date
|
|
119
|
+
Date.new(year, month, day)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Advances the time to the given date
|
|
123
|
+
def advance_to_date(date)
|
|
124
|
+
VoltTime.new(:utc, date.year, date.month, date.day, hour, min, sec + (usec/1.0e6))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Adds a duration to the time
|
|
128
|
+
def plus_with_duration(other)
|
|
129
|
+
if other.is_a?(Volt::Duration)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Returns a new VoltTime representing the beginning of the day, 00:00:00
|
|
134
|
+
def beginning_of_day
|
|
135
|
+
change(hour: 0, min: 0, sec: 0)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Returns a new VoltTime representing the end of the day, 23:59:59.999
|
|
139
|
+
# Only milliseconds are supported in Opal
|
|
140
|
+
def end_of_day
|
|
141
|
+
VoltTime.new(:utc, year, month, day, 23, 59, 59.999)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Returns a new Time for the middle of the day i.e. 12:00:00
|
|
145
|
+
def middle_of_day
|
|
146
|
+
change(hour: 12)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns the number of seconds since 00:00:00 of the current day
|
|
150
|
+
def seconds_since_midnight
|
|
151
|
+
to_f - change(hour: 0).to_f
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns the number of seconds to the 23:59:59 of the current day
|
|
155
|
+
def seconds_until_end_of_day
|
|
156
|
+
end_of_day.to_f - to_f
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Returns a new VoltTime for the number of seconds ago
|
|
160
|
+
def ago(seconds)
|
|
161
|
+
since(-seconds)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Returns a new VoltTime for the number of seconds since the current time
|
|
165
|
+
def since(seconds)
|
|
166
|
+
VoltTime.new.set_time(@time + seconds)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Returns a new Time for the beginning of the current hour
|
|
170
|
+
def beginning_of_hour
|
|
171
|
+
change(min: 0)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Returns a new Time for the end of the current hour
|
|
175
|
+
# only milliseconds are supported in Opal
|
|
176
|
+
def end_of_hour
|
|
177
|
+
change(min: 59, sec: 59.999)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Returns a new Time for beginning of the current minute
|
|
181
|
+
def beginning_of_minute
|
|
182
|
+
change(sec: 0)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns a new Time for the end of the current minute
|
|
186
|
+
def end_of_minute
|
|
187
|
+
change(sec: 59.999)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Returns a Range for the start to end of day
|
|
191
|
+
def all_day
|
|
192
|
+
beginning_of_day..end_of_day
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
def compare_date_components(other, component, higher_component)
|
|
197
|
+
if compare(other, higher_component) == 0
|
|
198
|
+
send(component) <=> other.send(component)
|
|
199
|
+
else
|
|
200
|
+
compare(other, higher_component)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
end
|