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
|
@@ -30,7 +30,14 @@ module Volt
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def html
|
|
33
|
-
|
|
33
|
+
# Check for a precompiled
|
|
34
|
+
index_path = File.expand_path(File.join(Volt.root, 'public/index.html'))
|
|
35
|
+
|
|
36
|
+
# Next check for one in config/base
|
|
37
|
+
unless File.exists?(index_path)
|
|
38
|
+
index_path = File.expand_path(File.join(Volt.root, 'config/base/index.html'))
|
|
39
|
+
end
|
|
40
|
+
|
|
34
41
|
html = File.read(index_path)
|
|
35
42
|
|
|
36
43
|
ERB.new(html, nil, '-').result(binding)
|
|
@@ -82,7 +82,22 @@ module Volt
|
|
|
82
82
|
environment = @environment
|
|
83
83
|
|
|
84
84
|
builder.map(app_url) do
|
|
85
|
-
|
|
85
|
+
# This map block takes over all serving for /app, so here we serve out
|
|
86
|
+
# of public/app if the file isn't found in sprockets.
|
|
87
|
+
# TODO: We should really let Rack::Cascade run from the default
|
|
88
|
+
# middleware stack.
|
|
89
|
+
not_found = lambda { |env| [404, {}, []] }
|
|
90
|
+
opts = {
|
|
91
|
+
urls: [''],
|
|
92
|
+
root: ['public/app'],
|
|
93
|
+
header_rules: [
|
|
94
|
+
[:all, { 'Cache-Control' => 'public, max-age=86400' }]
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
static = Rack::Static.new(not_found, opts)
|
|
98
|
+
|
|
99
|
+
run Rack::Cascade.new([environment, static])
|
|
100
|
+
# run environment
|
|
86
101
|
end
|
|
87
102
|
|
|
88
103
|
# Remove dup paths
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'sprockets'
|
|
1
2
|
require 'sprockets-helpers'
|
|
2
3
|
|
|
3
4
|
module Volt
|
|
@@ -38,6 +39,7 @@ module Volt
|
|
|
38
39
|
# We "freedom-patch" sprockets-helpers asset_path method to
|
|
39
40
|
# automatically link assets.
|
|
40
41
|
define_method(:asset_path) do |source, options = {}|
|
|
42
|
+
# puts "AP: #{source.inspect}"
|
|
41
43
|
relative_path = source =~ /^[.][.]\//
|
|
42
44
|
if relative_path
|
|
43
45
|
component_root = logical_path.gsub(/\/[^\/]+$/, '')
|
|
@@ -103,4 +105,33 @@ module Volt
|
|
|
103
105
|
output.join('/')
|
|
104
106
|
end
|
|
105
107
|
end
|
|
106
|
-
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# module Sprockets
|
|
112
|
+
# class Context
|
|
113
|
+
# # We have to reinclude this because of some ```require``` thread saftey issues.
|
|
114
|
+
# def find_asset_path(uri, source, options = {})
|
|
115
|
+
# if Helpers.manifest && options[:manifest] != false
|
|
116
|
+
# manifest_path = Helpers.manifest.assets[uri.path]
|
|
117
|
+
# return Helpers::ManifestPath.new(uri, manifest_path, options) if manifest_path
|
|
118
|
+
# end
|
|
119
|
+
|
|
120
|
+
# if Sprockets::Helpers.are_using_sprockets_3
|
|
121
|
+
# resolved = assets_environment.resolve(uri.path)
|
|
122
|
+
|
|
123
|
+
# if resolved
|
|
124
|
+
# return Helpers::AssetPath.new(uri, assets_environment[uri.path], options)
|
|
125
|
+
# else
|
|
126
|
+
# return Helpers::FilePath.new(uri, options)
|
|
127
|
+
# end
|
|
128
|
+
# else
|
|
129
|
+
# assets_environment.resolve(uri.path) do |path|
|
|
130
|
+
# return Helpers::AssetPath.new(uri, assets_environment[path], options)
|
|
131
|
+
# end
|
|
132
|
+
|
|
133
|
+
# return Helpers::FilePath.new(uri, options)
|
|
134
|
+
# end
|
|
135
|
+
# end
|
|
136
|
+
# end
|
|
137
|
+
# end
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
require 'volt/utils/ejson'
|
|
2
2
|
require File.join(File.dirname(__FILE__), '../../../app/volt/tasks/query_tasks')
|
|
3
3
|
|
|
4
|
+
# SocketConnectionHandler is often referred to as "channel" when on the server
|
|
5
|
+
# side.
|
|
6
|
+
#
|
|
7
|
+
# NOTE: On the forking server, the "channel" is actually a DRb reference back
|
|
8
|
+
# to the main process. So we should try to put as few objects as possible
|
|
9
|
+
# in here.
|
|
4
10
|
module Volt
|
|
5
11
|
class SocketConnectionHandler
|
|
6
12
|
# Create one instance of the dispatcher
|
|
@@ -13,11 +19,14 @@ module Volt
|
|
|
13
19
|
def initialize(session, *args)
|
|
14
20
|
@session = session
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
@@
|
|
22
|
+
# Keep a list of all channels
|
|
23
|
+
@@all_channels ||= []
|
|
24
|
+
@@all_channels << self
|
|
18
25
|
|
|
19
26
|
# Trigger a client connect event
|
|
20
|
-
@@dispatcher.volt_app
|
|
27
|
+
if @@dispatcher.respond_to?(:volt_app)
|
|
28
|
+
@@dispatcher.volt_app.trigger!("client_connect")
|
|
29
|
+
end
|
|
21
30
|
|
|
22
31
|
end
|
|
23
32
|
|
|
@@ -49,13 +58,13 @@ module Volt
|
|
|
49
58
|
end
|
|
50
59
|
|
|
51
60
|
def self.channels
|
|
52
|
-
@@
|
|
61
|
+
@@all_channels
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
# Sends a message to all, optionally skipping a users channel
|
|
56
65
|
def self.send_message_all(skip_channel = nil, *args)
|
|
57
|
-
return unless defined?(@@
|
|
58
|
-
@@
|
|
66
|
+
return unless defined?(@@all_channels)
|
|
67
|
+
@@all_channels.each do |channel|
|
|
59
68
|
next if skip_channel && channel == skip_channel
|
|
60
69
|
channel.send_message(*args)
|
|
61
70
|
end
|
|
@@ -108,7 +117,7 @@ module Volt
|
|
|
108
117
|
unless @closed
|
|
109
118
|
@closed = true
|
|
110
119
|
# Remove ourself from the available channels
|
|
111
|
-
@@
|
|
120
|
+
@@all_channels.delete(self)
|
|
112
121
|
|
|
113
122
|
begin
|
|
114
123
|
@@dispatcher.close_channel(self)
|
|
@@ -54,10 +54,12 @@ module Sprockets
|
|
|
54
54
|
data = env.read_file(input[:filename], input[:content_type])
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
dependencies = Set.new(input[:metadata][:dependencies])
|
|
58
|
+
dependencies += [env.build_file_digest_uri(input[:filename])]
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
# For some reason this version doesn't work, even though its used
|
|
61
|
+
# elsewhere in sprockets
|
|
62
|
+
# dependencies = input[:metadata][:dependencies]
|
|
61
63
|
# dependencies.merge(env.build_file_digest_uri(input[:filename]))
|
|
62
64
|
|
|
63
65
|
{ data: data, dependencies: dependencies }
|
data/lib/volt/spec/capybara.rb
CHANGED
|
@@ -35,6 +35,10 @@ module Volt
|
|
|
35
35
|
require 'selenium-webdriver'
|
|
36
36
|
require 'volt/server'
|
|
37
37
|
|
|
38
|
+
|
|
39
|
+
# Setup server, use existing booted app
|
|
40
|
+
Capybara.app = Server.new(app_path, volt_app).app
|
|
41
|
+
|
|
38
42
|
case RUNNING_SERVER
|
|
39
43
|
when 'thin'
|
|
40
44
|
Capybara.server do |app, port|
|
|
@@ -48,9 +52,6 @@ module Volt
|
|
|
48
52
|
end.run.join
|
|
49
53
|
end
|
|
50
54
|
end
|
|
51
|
-
|
|
52
|
-
# Setup server, use existing booted app
|
|
53
|
-
Capybara.app = Server.new(app_path, volt_app).app
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
end
|
data/lib/volt/spec/setup.rb
CHANGED
|
@@ -56,6 +56,7 @@ module Volt
|
|
|
56
56
|
@__store_accessed = true
|
|
57
57
|
volt_app.store
|
|
58
58
|
end
|
|
59
|
+
let(:cleanup_db) { @__store_accessed = true }
|
|
59
60
|
let(:volt_app) { volt_app }
|
|
60
61
|
let(:params) { volt_app.params }
|
|
61
62
|
|
|
@@ -65,6 +66,10 @@ module Volt
|
|
|
65
66
|
if url.instance_variable_get('@params')
|
|
66
67
|
url.instance_variable_set('@params', nil)
|
|
67
68
|
end
|
|
69
|
+
|
|
70
|
+
# Reset volt_app
|
|
71
|
+
$volt_app = volt_app
|
|
72
|
+
Thread.current['volt_app'] = volt_app
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
if RUBY_PLATFORM != 'opal'
|
|
@@ -7,6 +7,8 @@ require 'timeout'
|
|
|
7
7
|
module Volt
|
|
8
8
|
# The task dispatcher is responsible for taking incoming messages
|
|
9
9
|
# from the socket channel and dispatching them to the proper handler.
|
|
10
|
+
#
|
|
11
|
+
# On the forking server, this runs in the child.
|
|
10
12
|
class Dispatcher
|
|
11
13
|
# When we pass the dispatcher over DRb, don't send a copy, just proxy.
|
|
12
14
|
include DRb::DRbUndumped
|
|
@@ -51,7 +53,7 @@ module Volt
|
|
|
51
53
|
# server, returning the result to the client.
|
|
52
54
|
# Tasks returning a promise will wait to return.
|
|
53
55
|
def dispatch(channel, message)
|
|
54
|
-
# Dispatch the task in the worker pool.
|
|
56
|
+
# Dispatch the task in the worker pool. Pass in the meta data
|
|
55
57
|
@worker_pool.post do
|
|
56
58
|
begin
|
|
57
59
|
dispatch_in_thread(channel, message)
|
|
@@ -9,16 +9,16 @@ module Volt
|
|
|
9
9
|
# the block the value. The return value from the block replaces the
|
|
10
10
|
# previous value.
|
|
11
11
|
# NOTE: This does not yield hashes or arrays.
|
|
12
|
-
def self.transform(data, &block)
|
|
12
|
+
def self.transform(data, transform_keys=true, &block)
|
|
13
13
|
if data.is_a?(Hash)
|
|
14
14
|
data.map do |key, value|
|
|
15
|
-
key = transform(key, &block)
|
|
16
|
-
value = transform(value, &block)
|
|
15
|
+
key = transform(key, transform_keys, &block) if transform_keys
|
|
16
|
+
value = transform(value, transform_keys, &block)
|
|
17
17
|
[key, value]
|
|
18
18
|
end.to_h
|
|
19
19
|
elsif data.is_a?(Array)
|
|
20
20
|
data.map do |value|
|
|
21
|
-
transform(value, &block)
|
|
21
|
+
transform(value, transform_keys, &block)
|
|
22
22
|
end
|
|
23
23
|
else
|
|
24
24
|
# yield to the trasnformer
|
data/lib/volt/utils/ejson.rb
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
require 'json'
|
|
2
2
|
|
|
3
|
+
# We auto require time on the server, so we can decode VoltTime's in the parent
|
|
4
|
+
# process on the ForkingServer. (Since no user code is booted in the forking
|
|
5
|
+
# server). On the client the user has to require it if they want to use it.
|
|
6
|
+
unless RUBY_PLATFORM == 'opal'
|
|
7
|
+
require 'volt/helpers/time'
|
|
8
|
+
end
|
|
9
|
+
|
|
3
10
|
module Volt
|
|
4
11
|
class EJSON
|
|
5
12
|
class NonEjsonType < Exception ; end
|
|
@@ -24,9 +31,15 @@ module Volt
|
|
|
24
31
|
return escape.map do |key, value|
|
|
25
32
|
[key, decode(value)]
|
|
26
33
|
end.to_h
|
|
27
|
-
elsif obj.size == 1
|
|
28
|
-
if time
|
|
29
|
-
|
|
34
|
+
elsif obj.size == 1
|
|
35
|
+
if (time = obj['$date'])
|
|
36
|
+
if defined?(VoltTime)
|
|
37
|
+
if time.is_a?(Numeric)
|
|
38
|
+
return VoltTime.at(time / 1000.0)
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
raise "VoltTime is not defined, be sure to require 'volt/helpers/time'."
|
|
42
|
+
end
|
|
30
43
|
end
|
|
31
44
|
end
|
|
32
45
|
|
|
@@ -44,15 +57,15 @@ module Volt
|
|
|
44
57
|
elsif Hash === obj
|
|
45
58
|
obj.map do |key, value|
|
|
46
59
|
if key == '$date'
|
|
60
|
+
value = {key => encode(value)}
|
|
47
61
|
key = '$escape'
|
|
48
|
-
value = {'$date' => encode(value)}
|
|
49
62
|
else
|
|
50
63
|
value = encode(value)
|
|
51
64
|
end
|
|
52
65
|
|
|
53
66
|
[key, value]
|
|
54
67
|
end.to_h
|
|
55
|
-
elsif
|
|
68
|
+
elsif (defined?(VoltTime) && VoltTime === obj)
|
|
56
69
|
{'$date' => obj.to_i * 1_000}
|
|
57
70
|
elsif OTHER_VALID_CLASSES.any? {|klass| obj.is_a?(klass) }
|
|
58
71
|
obj
|
|
@@ -62,4 +75,4 @@ module Volt
|
|
|
62
75
|
end
|
|
63
76
|
end
|
|
64
77
|
end
|
|
65
|
-
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
# Copied from https://github.com/opal/opal/blob/dd81fb4bfe337c0321ef1a8f7def2a3049a3fd83/opal/corelib/time.rb
|
|
2
|
+
# This patches time to the level in Opal 0.9.0 so that Volt can continue to run against Opal 0.8.0 and
|
|
3
|
+
# implement VoltTime
|
|
4
|
+
|
|
5
|
+
require 'corelib/comparable'
|
|
6
|
+
|
|
7
|
+
class Time
|
|
8
|
+
include Comparable
|
|
9
|
+
|
|
10
|
+
%x{
|
|
11
|
+
var days_of_week = #{%w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday]},
|
|
12
|
+
short_days = #{%w[Sun Mon Tue Wed Thu Fri Sat]},
|
|
13
|
+
short_months = #{%w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec]},
|
|
14
|
+
long_months = #{%w[January February March April May June July August September October November December]};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def self.at(seconds, frac = undefined)
|
|
18
|
+
%x{
|
|
19
|
+
var result;
|
|
20
|
+
|
|
21
|
+
if (#{Time === seconds}) {
|
|
22
|
+
if (frac !== undefined) {
|
|
23
|
+
#{raise TypeError, "can't convert Time into an exact number"}
|
|
24
|
+
}
|
|
25
|
+
result = new Date(seconds.getTime());
|
|
26
|
+
result.is_utc = seconds.is_utc;
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!seconds.$$is_number) {
|
|
31
|
+
seconds = #{Opal.coerce_to!(seconds, Integer, :to_int)};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (frac === undefined) {
|
|
35
|
+
return new Date(seconds * 1000);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!frac.$$is_number) {
|
|
39
|
+
frac = #{Opal.coerce_to!(frac, Integer, :to_int)};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new Date(seconds * 1000 + (frac / 1000));
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
%x{
|
|
47
|
+
function time_params(year, month, day, hour, min, sec) {
|
|
48
|
+
if (year.$$is_string) {
|
|
49
|
+
year = parseInt(year, 10);
|
|
50
|
+
} else {
|
|
51
|
+
year = #{Opal.coerce_to!(`year`, Integer, :to_int)};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (month === nil) {
|
|
55
|
+
month = 1;
|
|
56
|
+
} else if (!month.$$is_number) {
|
|
57
|
+
if (#{`month`.respond_to?(:to_str)}) {
|
|
58
|
+
month = #{`month`.to_str};
|
|
59
|
+
switch (month.toLowerCase()) {
|
|
60
|
+
case 'jan': month = 1; break;
|
|
61
|
+
case 'feb': month = 2; break;
|
|
62
|
+
case 'mar': month = 3; break;
|
|
63
|
+
case 'apr': month = 4; break;
|
|
64
|
+
case 'may': month = 5; break;
|
|
65
|
+
case 'jun': month = 6; break;
|
|
66
|
+
case 'jul': month = 7; break;
|
|
67
|
+
case 'aug': month = 8; break;
|
|
68
|
+
case 'sep': month = 9; break;
|
|
69
|
+
case 'oct': month = 10; break;
|
|
70
|
+
case 'nov': month = 11; break;
|
|
71
|
+
case 'dec': month = 12; break;
|
|
72
|
+
default: month = #{`month`.to_i};
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
month = #{Opal.coerce_to!(`month`, Integer, :to_int)};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (month < 1 || month > 12) {
|
|
80
|
+
#{raise ArgumentError, "month out of range: #{`month`}"}
|
|
81
|
+
}
|
|
82
|
+
month = month - 1;
|
|
83
|
+
|
|
84
|
+
if (day === nil) {
|
|
85
|
+
day = 1;
|
|
86
|
+
} else if (day.$$is_string) {
|
|
87
|
+
day = parseInt(day, 10);
|
|
88
|
+
} else {
|
|
89
|
+
day = #{Opal.coerce_to!(`day`, Integer, :to_int)};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (day < 1 || day > 31) {
|
|
93
|
+
#{raise ArgumentError, "day out of range: #{`day`}"}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (hour === nil) {
|
|
97
|
+
hour = 0;
|
|
98
|
+
} else if (hour.$$is_string) {
|
|
99
|
+
hour = parseInt(hour, 10);
|
|
100
|
+
} else {
|
|
101
|
+
hour = #{Opal.coerce_to!(`hour`, Integer, :to_int)};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (hour < 0 || hour > 24) {
|
|
105
|
+
#{raise ArgumentError, "hour out of range: #{`hour`}"}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (min === nil) {
|
|
109
|
+
min = 0;
|
|
110
|
+
} else if (min.$$is_string) {
|
|
111
|
+
min = parseInt(min, 10);
|
|
112
|
+
} else {
|
|
113
|
+
min = #{Opal.coerce_to!(`min`, Integer, :to_int)};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (min < 0 || min > 59) {
|
|
117
|
+
#{raise ArgumentError, "min out of range: #{`min`}"}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (sec === nil) {
|
|
121
|
+
sec = 0;
|
|
122
|
+
} else if (!sec.$$is_number) {
|
|
123
|
+
if (sec.$$is_string) {
|
|
124
|
+
sec = parseInt(sec, 10);
|
|
125
|
+
} else {
|
|
126
|
+
sec = #{Opal.coerce_to!(`sec`, Integer, :to_int)};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (sec < 0 || sec > 60) {
|
|
131
|
+
#{raise ArgumentError, "sec out of range: #{`sec`}"}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return [year, month, day, hour, min, sec];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
def self.new(year = undefined, month = nil, day = nil, hour = nil, min = nil, sec = nil, utc_offset = nil)
|
|
139
|
+
%x{
|
|
140
|
+
var args, result;
|
|
141
|
+
|
|
142
|
+
if (year === undefined) {
|
|
143
|
+
return new Date();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (utc_offset !== nil) {
|
|
147
|
+
#{raise ArgumentError, 'Opal does not support explicitly specifying UTC offset for Time'}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
args = time_params(year, month, day, hour, min, sec);
|
|
151
|
+
year = args[0];
|
|
152
|
+
month = args[1];
|
|
153
|
+
day = args[2];
|
|
154
|
+
hour = args[3];
|
|
155
|
+
min = args[4];
|
|
156
|
+
sec = args[5];
|
|
157
|
+
|
|
158
|
+
result = new Date(year, month, day, hour, min, 0, sec * 1000);
|
|
159
|
+
if (year < 100) {
|
|
160
|
+
result.setFullYear(year);
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def self.local(year, month = nil, day = nil, hour = nil, min = nil, sec = nil, millisecond = nil)
|
|
167
|
+
%x{
|
|
168
|
+
var args, result;
|
|
169
|
+
|
|
170
|
+
if (arguments.length === 10) {
|
|
171
|
+
args = $slice.call(arguments);
|
|
172
|
+
year = args[5];
|
|
173
|
+
month = args[4];
|
|
174
|
+
day = args[3];
|
|
175
|
+
hour = args[2];
|
|
176
|
+
min = args[1];
|
|
177
|
+
sec = args[0];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
args = time_params(year, month, day, hour, min, sec);
|
|
181
|
+
year = args[0];
|
|
182
|
+
month = args[1];
|
|
183
|
+
day = args[2];
|
|
184
|
+
hour = args[3];
|
|
185
|
+
min = args[4];
|
|
186
|
+
sec = args[5];
|
|
187
|
+
|
|
188
|
+
result = new Date(year, month, day, hour, min, 0, sec * 1000);
|
|
189
|
+
if (year < 100) {
|
|
190
|
+
result.setFullYear(year);
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def self.gm(year, month = nil, day = nil, hour = nil, min = nil, sec = nil, millisecond = nil)
|
|
197
|
+
%x{
|
|
198
|
+
var args, result;
|
|
199
|
+
|
|
200
|
+
if (arguments.length === 10) {
|
|
201
|
+
args = $slice.call(arguments);
|
|
202
|
+
year = args[5];
|
|
203
|
+
month = args[4];
|
|
204
|
+
day = args[3];
|
|
205
|
+
hour = args[2];
|
|
206
|
+
min = args[1];
|
|
207
|
+
sec = args[0];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
args = time_params(year, month, day, hour, min, sec);
|
|
211
|
+
year = args[0];
|
|
212
|
+
month = args[1];
|
|
213
|
+
day = args[2];
|
|
214
|
+
hour = args[3];
|
|
215
|
+
min = args[4];
|
|
216
|
+
sec = args[5];
|
|
217
|
+
|
|
218
|
+
result = new Date(Date.UTC(year, month, day, hour, min, 0, sec * 1000));
|
|
219
|
+
if (year < 100) {
|
|
220
|
+
result.setUTCFullYear(year);
|
|
221
|
+
}
|
|
222
|
+
result.is_utc = true;
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
class << self
|
|
228
|
+
alias mktime local
|
|
229
|
+
alias utc gm
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.now
|
|
233
|
+
new
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def +(other)
|
|
237
|
+
if Time === other
|
|
238
|
+
raise TypeError, "time + time?"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
%x{
|
|
242
|
+
if (!other.$$is_number) {
|
|
243
|
+
other = #{Opal.coerce_to!(other, Integer, :to_int)};
|
|
244
|
+
}
|
|
245
|
+
var result = new Date(self.getTime() + (other * 1000));
|
|
246
|
+
result.is_utc = self.is_utc;
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def -(other)
|
|
252
|
+
if Time === other
|
|
253
|
+
return `(self.getTime() - other.getTime()) / 1000`
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
%x{
|
|
257
|
+
if (!other.$$is_number) {
|
|
258
|
+
other = #{Opal.coerce_to!(other, Integer, :to_int)};
|
|
259
|
+
}
|
|
260
|
+
var result = new Date(self.getTime() - (other * 1000));
|
|
261
|
+
result.is_utc = self.is_utc;
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def <=>(other)
|
|
267
|
+
if Time === other
|
|
268
|
+
to_f <=> other.to_f
|
|
269
|
+
else
|
|
270
|
+
r = other <=> self
|
|
271
|
+
if r.nil?
|
|
272
|
+
nil
|
|
273
|
+
elsif r > 0
|
|
274
|
+
-1
|
|
275
|
+
elsif r < 0
|
|
276
|
+
1
|
|
277
|
+
else
|
|
278
|
+
0
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def ==(other)
|
|
284
|
+
`#{to_f} === #{other.to_f}`
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def asctime
|
|
288
|
+
strftime '%a %b %e %H:%M:%S %Y'
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
alias ctime asctime
|
|
292
|
+
|
|
293
|
+
def day
|
|
294
|
+
`self.is_utc ? self.getUTCDate() : self.getDate()`
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def yday
|
|
298
|
+
%x{
|
|
299
|
+
// http://javascript.about.com/library/bldayyear.htm
|
|
300
|
+
var onejan = new Date(self.getFullYear(), 0, 1);
|
|
301
|
+
return Math.ceil((self - onejan) / 86400000);
|
|
302
|
+
}
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def isdst
|
|
306
|
+
%x{
|
|
307
|
+
var jan = new Date(self.getFullYear(), 0, 1),
|
|
308
|
+
jul = new Date(self.getFullYear(), 6, 1);
|
|
309
|
+
return self.getTimezoneOffset() < Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
alias dst? isdst
|
|
314
|
+
|
|
315
|
+
def dup
|
|
316
|
+
copy = `new Date(self.getTime())`
|
|
317
|
+
|
|
318
|
+
copy.copy_instance_variables(self)
|
|
319
|
+
copy.initialize_dup(self)
|
|
320
|
+
|
|
321
|
+
copy
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def eql?(other)
|
|
325
|
+
other.is_a?(Time) && (self <=> other).zero?
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def friday?
|
|
329
|
+
`#{wday} == 5`
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def hash
|
|
333
|
+
`'Time:' + self.getTime()`
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def hour
|
|
337
|
+
`self.is_utc ? self.getUTCHours() : self.getHours()`
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def inspect
|
|
341
|
+
if utc?
|
|
342
|
+
strftime '%Y-%m-%d %H:%M:%S UTC'
|
|
343
|
+
else
|
|
344
|
+
strftime '%Y-%m-%d %H:%M:%S %z'
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
alias mday day
|
|
349
|
+
|
|
350
|
+
def min
|
|
351
|
+
`self.is_utc ? self.getUTCMinutes() : self.getMinutes()`
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def mon
|
|
355
|
+
`(self.is_utc ? self.getUTCMonth() : self.getMonth()) + 1`
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def monday?
|
|
359
|
+
`#{wday} == 1`
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
alias month mon
|
|
363
|
+
|
|
364
|
+
def saturday?
|
|
365
|
+
`#{wday} == 6`
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def sec
|
|
369
|
+
`self.is_utc ? self.getUTCSeconds() : self.getSeconds()`
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def succ
|
|
373
|
+
%x{
|
|
374
|
+
var result = new Date(self.getTime() + 1000);
|
|
375
|
+
result.is_utc = self.is_utc;
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def usec
|
|
381
|
+
`self.getMilliseconds() * 1000`
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def zone
|
|
385
|
+
%x{
|
|
386
|
+
var string = self.toString(),
|
|
387
|
+
result;
|
|
388
|
+
|
|
389
|
+
if (string.indexOf('(') == -1) {
|
|
390
|
+
result = string.match(/[A-Z]{3,4}/)[0];
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
result = string.match(/\([^)]+\)/)[0].match(/[A-Z]/g).join('');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (result == "GMT" && /(GMT\W*\d{4})/.test(string)) {
|
|
397
|
+
return RegExp.$1;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def getgm
|
|
406
|
+
%x{
|
|
407
|
+
var result = new Date(self.getTime());
|
|
408
|
+
result.is_utc = true;
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
alias getutc getgm
|
|
414
|
+
|
|
415
|
+
def gmtime
|
|
416
|
+
%x{
|
|
417
|
+
self.is_utc = true;
|
|
418
|
+
return self;
|
|
419
|
+
}
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
alias utc gmtime
|
|
423
|
+
|
|
424
|
+
def gmt?
|
|
425
|
+
`self.is_utc === true`
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def gmt_offset
|
|
429
|
+
`-self.getTimezoneOffset() * 60`
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def strftime(format)
|
|
433
|
+
%x{
|
|
434
|
+
return format.replace(/%([\-_#^0]*:{0,2})(\d+)?([EO]*)(.)/g, function(full, flags, width, _, conv) {
|
|
435
|
+
var result = "",
|
|
436
|
+
zero = flags.indexOf('0') !== -1,
|
|
437
|
+
pad = flags.indexOf('-') === -1,
|
|
438
|
+
blank = flags.indexOf('_') !== -1,
|
|
439
|
+
upcase = flags.indexOf('^') !== -1,
|
|
440
|
+
invert = flags.indexOf('#') !== -1,
|
|
441
|
+
colons = (flags.match(':') || []).length;
|
|
442
|
+
|
|
443
|
+
width = parseInt(width, 10);
|
|
444
|
+
|
|
445
|
+
if (zero && blank) {
|
|
446
|
+
if (flags.indexOf('0') < flags.indexOf('_')) {
|
|
447
|
+
zero = false;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
blank = false;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
switch (conv) {
|
|
455
|
+
case 'Y':
|
|
456
|
+
result += #{year};
|
|
457
|
+
break;
|
|
458
|
+
|
|
459
|
+
case 'C':
|
|
460
|
+
zero = !blank;
|
|
461
|
+
result += Math.round(#{year} / 100);
|
|
462
|
+
break;
|
|
463
|
+
|
|
464
|
+
case 'y':
|
|
465
|
+
zero = !blank;
|
|
466
|
+
result += (#{year} % 100);
|
|
467
|
+
break;
|
|
468
|
+
|
|
469
|
+
case 'm':
|
|
470
|
+
zero = !blank;
|
|
471
|
+
result += #{mon};
|
|
472
|
+
break;
|
|
473
|
+
|
|
474
|
+
case 'B':
|
|
475
|
+
result += long_months[#{mon} - 1];
|
|
476
|
+
break;
|
|
477
|
+
|
|
478
|
+
case 'b':
|
|
479
|
+
case 'h':
|
|
480
|
+
blank = !zero;
|
|
481
|
+
result += short_months[#{mon} - 1];
|
|
482
|
+
break;
|
|
483
|
+
|
|
484
|
+
case 'd':
|
|
485
|
+
zero = !blank
|
|
486
|
+
result += #{day};
|
|
487
|
+
break;
|
|
488
|
+
|
|
489
|
+
case 'e':
|
|
490
|
+
blank = !zero
|
|
491
|
+
result += #{day};
|
|
492
|
+
break;
|
|
493
|
+
|
|
494
|
+
case 'j':
|
|
495
|
+
result += #{yday};
|
|
496
|
+
break;
|
|
497
|
+
|
|
498
|
+
case 'H':
|
|
499
|
+
zero = !blank;
|
|
500
|
+
result += #{hour};
|
|
501
|
+
break;
|
|
502
|
+
|
|
503
|
+
case 'k':
|
|
504
|
+
blank = !zero;
|
|
505
|
+
result += #{hour};
|
|
506
|
+
break;
|
|
507
|
+
|
|
508
|
+
case 'I':
|
|
509
|
+
zero = !blank;
|
|
510
|
+
result += (#{hour} % 12 || 12);
|
|
511
|
+
break;
|
|
512
|
+
|
|
513
|
+
case 'l':
|
|
514
|
+
blank = !zero;
|
|
515
|
+
result += (#{hour} % 12 || 12);
|
|
516
|
+
break;
|
|
517
|
+
|
|
518
|
+
case 'P':
|
|
519
|
+
result += (#{hour} >= 12 ? "pm" : "am");
|
|
520
|
+
break;
|
|
521
|
+
|
|
522
|
+
case 'p':
|
|
523
|
+
result += (#{hour} >= 12 ? "PM" : "AM");
|
|
524
|
+
break;
|
|
525
|
+
|
|
526
|
+
case 'M':
|
|
527
|
+
zero = !blank;
|
|
528
|
+
result += #{min};
|
|
529
|
+
break;
|
|
530
|
+
|
|
531
|
+
case 'S':
|
|
532
|
+
zero = !blank;
|
|
533
|
+
result += #{sec}
|
|
534
|
+
break;
|
|
535
|
+
|
|
536
|
+
case 'L':
|
|
537
|
+
zero = !blank;
|
|
538
|
+
width = isNaN(width) ? 3 : width;
|
|
539
|
+
result += self.getMilliseconds();
|
|
540
|
+
break;
|
|
541
|
+
|
|
542
|
+
case 'N':
|
|
543
|
+
width = isNaN(width) ? 9 : width;
|
|
544
|
+
result += #{`self.getMilliseconds().toString()`.rjust(3, '0')};
|
|
545
|
+
result = #{`result`.ljust(`width`, '0')};
|
|
546
|
+
break;
|
|
547
|
+
|
|
548
|
+
case 'z':
|
|
549
|
+
var offset = self.getTimezoneOffset(),
|
|
550
|
+
hours = Math.floor(Math.abs(offset) / 60),
|
|
551
|
+
minutes = Math.abs(offset) % 60;
|
|
552
|
+
|
|
553
|
+
result += offset < 0 ? "+" : "-";
|
|
554
|
+
result += hours < 10 ? "0" : "";
|
|
555
|
+
result += hours;
|
|
556
|
+
|
|
557
|
+
if (colons > 0) {
|
|
558
|
+
result += ":";
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
result += minutes < 10 ? "0" : "";
|
|
562
|
+
result += minutes;
|
|
563
|
+
|
|
564
|
+
if (colons > 1) {
|
|
565
|
+
result += ":00";
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
break;
|
|
569
|
+
|
|
570
|
+
case 'Z':
|
|
571
|
+
result += #{zone};
|
|
572
|
+
break;
|
|
573
|
+
|
|
574
|
+
case 'A':
|
|
575
|
+
result += days_of_week[#{wday}];
|
|
576
|
+
break;
|
|
577
|
+
|
|
578
|
+
case 'a':
|
|
579
|
+
result += short_days[#{wday}];
|
|
580
|
+
break;
|
|
581
|
+
|
|
582
|
+
case 'u':
|
|
583
|
+
result += (#{wday} + 1);
|
|
584
|
+
break;
|
|
585
|
+
|
|
586
|
+
case 'w':
|
|
587
|
+
result += #{wday};
|
|
588
|
+
break;
|
|
589
|
+
|
|
590
|
+
case 'V':
|
|
591
|
+
result += #{cweek_cyear[0].to_s.rjust(2, "0")};
|
|
592
|
+
break;
|
|
593
|
+
|
|
594
|
+
case 'G':
|
|
595
|
+
result += #{cweek_cyear[1]};
|
|
596
|
+
break;
|
|
597
|
+
|
|
598
|
+
case 'g':
|
|
599
|
+
result += #{cweek_cyear[1][-2..-1]};
|
|
600
|
+
break;
|
|
601
|
+
|
|
602
|
+
case 's':
|
|
603
|
+
result += #{to_i};
|
|
604
|
+
break;
|
|
605
|
+
|
|
606
|
+
case 'n':
|
|
607
|
+
result += "\n";
|
|
608
|
+
break;
|
|
609
|
+
|
|
610
|
+
case 't':
|
|
611
|
+
result += "\t";
|
|
612
|
+
break;
|
|
613
|
+
|
|
614
|
+
case '%':
|
|
615
|
+
result += "%";
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
case 'c':
|
|
619
|
+
result += #{strftime('%a %b %e %T %Y')};
|
|
620
|
+
break;
|
|
621
|
+
|
|
622
|
+
case 'D':
|
|
623
|
+
case 'x':
|
|
624
|
+
result += #{strftime('%m/%d/%y')};
|
|
625
|
+
break;
|
|
626
|
+
|
|
627
|
+
case 'F':
|
|
628
|
+
result += #{strftime('%Y-%m-%d')};
|
|
629
|
+
break;
|
|
630
|
+
|
|
631
|
+
case 'v':
|
|
632
|
+
result += #{strftime('%e-%^b-%4Y')};
|
|
633
|
+
break;
|
|
634
|
+
|
|
635
|
+
case 'r':
|
|
636
|
+
result += #{strftime('%I:%M:%S %p')};
|
|
637
|
+
break;
|
|
638
|
+
|
|
639
|
+
case 'R':
|
|
640
|
+
result += #{strftime('%H:%M')};
|
|
641
|
+
break;
|
|
642
|
+
|
|
643
|
+
case 'T':
|
|
644
|
+
case 'X':
|
|
645
|
+
result += #{strftime('%H:%M:%S')};
|
|
646
|
+
break;
|
|
647
|
+
|
|
648
|
+
default:
|
|
649
|
+
return full;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (upcase) {
|
|
653
|
+
result = result.toUpperCase();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (invert) {
|
|
657
|
+
result = result.replace(/[A-Z]/, function(c) { c.toLowerCase() }).
|
|
658
|
+
replace(/[a-z]/, function(c) { c.toUpperCase() });
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (pad && (zero || blank)) {
|
|
662
|
+
result = #{`result`.rjust(`isNaN(width) ? 2 : width`, `blank ? " " : "0"`)};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return result;
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
def sunday?
|
|
671
|
+
`#{wday} == 0`
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
def thursday?
|
|
675
|
+
`#{wday} == 4`
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def to_a
|
|
679
|
+
[sec, min, hour, day, month, year, wday, yday, isdst, zone]
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
def to_f
|
|
683
|
+
`self.getTime() / 1000`
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def to_i
|
|
687
|
+
`parseInt(self.getTime() / 1000, 10)`
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
alias to_s inspect
|
|
691
|
+
|
|
692
|
+
def tuesday?
|
|
693
|
+
`#{wday} == 2`
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
alias tv_sec sec
|
|
697
|
+
|
|
698
|
+
alias tv_usec usec
|
|
699
|
+
|
|
700
|
+
alias utc? gmt?
|
|
701
|
+
|
|
702
|
+
alias gmtoff gmt_offset
|
|
703
|
+
alias utc_offset gmt_offset
|
|
704
|
+
|
|
705
|
+
def wday
|
|
706
|
+
`self.is_utc ? self.getUTCDay() : self.getDay()`
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
def wednesday?
|
|
710
|
+
`#{wday} == 3`
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def year
|
|
714
|
+
`self.is_utc ? self.getUTCFullYear() : self.getFullYear()`
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
def cweek_cyear
|
|
718
|
+
jan01 = Time.new(self.year, 1, 1)
|
|
719
|
+
jan01_wday = jan01.wday
|
|
720
|
+
first_monday = 0
|
|
721
|
+
year = self.year
|
|
722
|
+
if jan01_wday <= 4 && jan01_wday != 0
|
|
723
|
+
#Jan 01 is in the first week of the year
|
|
724
|
+
offset = jan01_wday-1
|
|
725
|
+
else
|
|
726
|
+
#Jan 01 is in the last week of the previous year
|
|
727
|
+
offset = jan01_wday-7-1
|
|
728
|
+
offset = -1 if offset == -8 #Adjust if Jan 01 is a Sunday
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
week = ((self.yday+offset)/7.00).ceil
|
|
732
|
+
|
|
733
|
+
if week <= 0
|
|
734
|
+
#Get the last week of the previous year
|
|
735
|
+
return Time.new(self.year-1, 12, 31).cweek_cyear
|
|
736
|
+
elsif week == 53
|
|
737
|
+
#Find out whether this is actually week 53 or already week 01 of the following year
|
|
738
|
+
dec31 = Time.new(self.year, 12, 31)
|
|
739
|
+
dec31_wday = dec31.wday
|
|
740
|
+
if dec31_wday <= 3 && dec31_wday != 0
|
|
741
|
+
week = 1
|
|
742
|
+
year += 1
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
[week, year]
|
|
747
|
+
|
|
748
|
+
end
|
|
749
|
+
end
|