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.
- 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
|