ruby-plsql 0.9.0 → 0.9.9
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/README.md +19 -3
- data/VERSION +1 -1
- data/lib/plsql/jdbc_connection.rb +59 -19
- data/lib/plsql/oci_connection.rb +2 -6
- data/lib/plsql/procedure.rb +25 -5
- data/lib/plsql/schema.rb +10 -7
- data/lib/plsql/variable.rb +2 -1
- metadata +2 -3
- data/lib/plsql/oci8_patches.rb +0 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ce42b7f40a83d55130a51e1acae222dbb1361a7d4a43b712873e2cddae24571
|
|
4
|
+
data.tar.gz: 7cd295463301ff322ed0312cabd73f08337068a8b3b3c57e724a5dae8efb51a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '08dabe0856244a3b1e487df17664d996a204f026ce6c19c50dec721bcc42be7936ce41b90ddecfc7d2b798351e624df068602d8da4c1e091e1096f534d8b96c6'
|
|
7
|
+
data.tar.gz: 5b25be049a7575a74911028db9b82986a692af4aa895d1561e1de3600bcfd418429c969bb45832536782ef84c70cf60bb8549207470d0cac962203666f7c035d
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://github.com/rsim/ruby-plsql/actions/workflows/test.yml)
|
|
2
2
|
|
|
3
3
|
ruby-plsql
|
|
4
4
|
==========
|
|
@@ -120,6 +120,22 @@ plsql.activerecord_class = ActiveRecord::Base
|
|
|
120
120
|
and then you do not need to specify plsql.connection (this is also safer when ActiveRecord reestablishes connection to database).
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
### JRuby JDBC connection:
|
|
124
|
+
|
|
125
|
+
When using JRuby, the `connect!` method with `:host` and `:database` options uses the thin-style service name syntax by default:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# Connects using service name syntax: jdbc:oracle:thin:@//localhost:1521/MYSERVICENAME
|
|
129
|
+
plsql.connect! username: "hr", password: "hr", host: "localhost", database: "MYSERVICENAME"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If you need to connect using the legacy SID syntax (for Oracle databases older than 12c), prefix the database name with a colon:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# Connects using SID syntax: jdbc:oracle:thin:@localhost:1521:MYSID
|
|
136
|
+
plsql.connect! username: "hr", password: "hr", host: "localhost", database: ":MYSID"
|
|
137
|
+
```
|
|
138
|
+
|
|
123
139
|
### Cheat Sheet:
|
|
124
140
|
|
|
125
141
|
You may have a look at this [Cheat Sheet](http://cheatography.com/jgebal/cheat-sheets/ruby-plsql-cheat-sheet/) for instructions on how to use ruby-plsql
|
|
@@ -135,10 +151,10 @@ or include gem in Gemfile if using bundler.
|
|
|
135
151
|
|
|
136
152
|
In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver to $JRUBY_HOME/lib (for JRuby).
|
|
137
153
|
|
|
138
|
-
If you are using MRI Ruby implementation then you need to install ruby-oci8 gem (version 2.
|
|
154
|
+
If you are using MRI Ruby implementation then you need to install ruby-oci8 gem (version 2.1 or higher)
|
|
139
155
|
as well as Oracle client, e.g. [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html).
|
|
140
156
|
|
|
141
|
-
If you are using JRuby then you need to download
|
|
157
|
+
If you are using JRuby then you need to download the appropriate [Oracle JDBC driver](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html) for your Java version - ojdbc17.jar for Java 17+, ojdbc11.jar for Java 11+, ojdbc8.jar for Java 8+, ojdbc7.jar for Java 7, ojdbc6.jar for Java 6, or ojdbc5.jar for Java 5.
|
|
142
158
|
|
|
143
159
|
And copy this file to one of these locations. JDBC driver will be searched in this order:
|
|
144
160
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.9
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
ojdbc_jars = []
|
|
2
|
+
|
|
1
3
|
begin
|
|
2
4
|
require "java"
|
|
3
5
|
require "jruby"
|
|
4
6
|
|
|
5
|
-
#
|
|
7
|
+
# Oracle JDBC driver jar should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
|
|
6
8
|
|
|
7
9
|
java_version = java.lang.System.getProperty("java.version")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
elsif java_version =~ /^1.6/
|
|
11
|
-
%w(ojdbc6.jar)
|
|
12
|
-
elsif java_version >= "1.7"
|
|
13
|
-
# Oracle 11g client ojdbc6.jar is also compatible with Java 1.7
|
|
14
|
-
# Oracle 12c client provides new ojdbc7.jar
|
|
15
|
-
%w(ojdbc7.jar ojdbc6.jar)
|
|
10
|
+
java_major = if java_version =~ /^1\.(\d+)/
|
|
11
|
+
$1.to_i
|
|
16
12
|
else
|
|
17
|
-
|
|
13
|
+
java_version.to_i
|
|
18
14
|
end
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
ojdbc_jars << "ojdbc17.jar" if java_major >= 17
|
|
17
|
+
ojdbc_jars << "ojdbc11.jar" if java_major >= 11
|
|
18
|
+
ojdbc_jars << "ojdbc8.jar" if java_major >= 8
|
|
19
|
+
ojdbc_jars << "ojdbc7.jar" if java_major >= 7
|
|
20
|
+
ojdbc_jars << "ojdbc6.jar" if java_major >= 6
|
|
21
|
+
ojdbc_jars << "ojdbc5.jar" if java_major == 5
|
|
22
|
+
|
|
23
|
+
if ENV_JAVA["java.class.path"] !~ Regexp.union(ojdbc_jars)
|
|
21
24
|
# On Unix environment variable should be PATH, on Windows it is sometimes Path
|
|
22
25
|
env_path = (ENV["PATH"] || ENV["Path"] || "").split(File::PATH_SEPARATOR)
|
|
23
26
|
# Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
|
|
@@ -25,7 +28,7 @@ begin
|
|
|
25
28
|
["./lib"].concat($LOAD_PATH).concat(env_path).detect do |dir|
|
|
26
29
|
# check any compatible JDBC driver in the priority order
|
|
27
30
|
ojdbc_jars.any? do |ojdbc_jar|
|
|
28
|
-
if File.
|
|
31
|
+
if File.exist?(file_path = File.join(dir, ojdbc_jar))
|
|
29
32
|
require file_path
|
|
30
33
|
true
|
|
31
34
|
end
|
|
@@ -33,29 +36,66 @@ begin
|
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
36
|
-
java.sql.DriverManager.registerDriver Java::oracle.jdbc.OracleDriver.new
|
|
37
|
-
|
|
38
39
|
# set tns_admin property from TNS_ADMIN environment variable
|
|
39
40
|
if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
|
|
40
41
|
java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
rescue LoadError
|
|
44
|
+
rescue LoadError
|
|
44
45
|
# JDBC driver is unavailable.
|
|
45
46
|
raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. Please install #{ojdbc_jars.empty? ? "Oracle JDBC" : ojdbc_jars.join(' or ') } library."
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
module PLSQL
|
|
49
50
|
class JDBCConnection < Connection # :nodoc:
|
|
51
|
+
begin
|
|
52
|
+
ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
|
|
53
|
+
java.sql.DriverManager.registerDriver ORACLE_DRIVER
|
|
54
|
+
rescue NameError
|
|
55
|
+
raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. " \
|
|
56
|
+
"Please install the appropriate Oracle JDBC driver. " \
|
|
57
|
+
"See https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html"
|
|
58
|
+
end
|
|
59
|
+
|
|
50
60
|
def self.create_raw(params)
|
|
61
|
+
url = jdbc_connection_url(params)
|
|
62
|
+
conn = begin
|
|
63
|
+
java.sql.DriverManager.getConnection(url, params[:username], params[:password])
|
|
64
|
+
rescue Java::JavaSql::SQLException => e
|
|
65
|
+
raise unless e.message =~ /no suitable driver/i
|
|
66
|
+
# bypass DriverManager to work in cases where ojdbc*.jar
|
|
67
|
+
# is added to the load path at runtime and not on the
|
|
68
|
+
# system classpath
|
|
69
|
+
ORACLE_DRIVER.connect(url, java.util.Properties.new.tap do |props|
|
|
70
|
+
props.setProperty("user", params[:username])
|
|
71
|
+
props.setProperty("password", params[:password])
|
|
72
|
+
end)
|
|
73
|
+
end
|
|
74
|
+
conn.setAutoCommit(false)
|
|
75
|
+
new(conn)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.jdbc_connection_url(params)
|
|
51
79
|
database = params[:database]
|
|
52
|
-
|
|
80
|
+
if ENV["TNS_ADMIN"] && database && database !~ %r{\A[:/]} && !params[:host] && !params[:url]
|
|
53
81
|
"jdbc:oracle:thin:@#{database}"
|
|
54
82
|
else
|
|
55
|
-
|
|
56
|
-
|
|
83
|
+
return params[:url] if params[:url]
|
|
84
|
+
|
|
85
|
+
raise ArgumentError, "database or url option is required" if database.nil? || database.empty?
|
|
86
|
+
|
|
87
|
+
host = params[:host] || "localhost"
|
|
88
|
+
port = params[:port] || 1521
|
|
89
|
+
|
|
90
|
+
if database =~ /^:/
|
|
91
|
+
# SID syntax: jdbc:oracle:thin:@host:port:SID
|
|
92
|
+
"jdbc:oracle:thin:@#{host}:#{port}#{database}"
|
|
93
|
+
else
|
|
94
|
+
# service name syntax: jdbc:oracle:thin:@//host:port/service_name
|
|
95
|
+
database = "/#{database}" unless database =~ /^\//
|
|
96
|
+
"jdbc:oracle:thin:@//#{host}:#{port}#{database}"
|
|
97
|
+
end
|
|
57
98
|
end
|
|
58
|
-
new(java.sql.DriverManager.getConnection(url, params[:username], params[:password]))
|
|
59
99
|
end
|
|
60
100
|
|
|
61
101
|
def set_time_zone(time_zone = nil)
|
data/lib/plsql/oci_connection.rb
CHANGED
|
@@ -14,13 +14,9 @@ rescue LoadError
|
|
|
14
14
|
raise LoadError, "ERROR: ruby-plsql could not load ruby-oci8 library. #{msg}"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
require "plsql/oci8_patches"
|
|
18
|
-
|
|
19
17
|
# check ruby-oci8 version
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (oci8_version_ints <=> required_oci8_version) < 0
|
|
23
|
-
raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
|
|
18
|
+
if Gem::Version.new(OCI8::VERSION) < Gem::Version.new("2.1.0")
|
|
19
|
+
raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version 2.1.0 or later."
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
module PLSQL
|
data/lib/plsql/procedure.rb
CHANGED
|
@@ -361,7 +361,7 @@ module PLSQL
|
|
|
361
361
|
WHERE t.OWNER = :owner AND t.type_name = :type_name AND t.package_name = :package_name
|
|
362
362
|
AND ta.OWNER = t.owner AND ta.TYPE_NAME = t.TYPE_NAME AND ta.PACKAGE_NAME = t.PACKAGE_NAME
|
|
363
363
|
ORDER BY attr_no",
|
|
364
|
-
|
|
364
|
+
argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
|
|
365
365
|
|
|
366
366
|
attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, attr_length, attr_precision, attr_scale, attr_char_used = r
|
|
367
367
|
|
|
@@ -391,12 +391,15 @@ module PLSQL
|
|
|
391
391
|
when "TABLE", "VIEW"
|
|
392
392
|
@schema.select_all(
|
|
393
393
|
"SELECT column_id, column_name, data_type, data_length, data_precision, data_scale, char_length, char_used
|
|
394
|
-
FROM
|
|
394
|
+
FROM ALL_TAB_COLUMNS WHERE OWNER = :owner AND TABLE_NAME = :type_name
|
|
395
395
|
ORDER BY column_id",
|
|
396
|
-
|
|
396
|
+
argument_metadata[:type_owner], argument_metadata[:type_name]) do |r|
|
|
397
397
|
|
|
398
398
|
col_no, col_name, col_type_name, col_length, col_precision, col_scale, col_char_length, col_char_used = r
|
|
399
399
|
|
|
400
|
+
# remove precision (n) from data_type (returned for TIMESTAMPs and INTERVALs)
|
|
401
|
+
col_type_name = col_type_name.sub(/\(\d+\)/, "")
|
|
402
|
+
|
|
400
403
|
fields[col_name.downcase.to_sym] = {
|
|
401
404
|
position: col_no.to_i,
|
|
402
405
|
data_type: col_type_name,
|
|
@@ -426,7 +429,7 @@ module PLSQL
|
|
|
426
429
|
"SELECT elem_type_owner, elem_type_name, elem_type_package, length, precision, scale, char_used, index_by
|
|
427
430
|
FROM ALL_PLSQL_COLL_TYPES t
|
|
428
431
|
WHERE t.OWNER = :owner AND t.TYPE_NAME = :type_name AND t.PACKAGE_NAME = :package_name",
|
|
429
|
-
|
|
432
|
+
argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname])
|
|
430
433
|
|
|
431
434
|
elem_type_owner, elem_type_name, elem_type_package, elem_length, elem_precision, elem_scale, elem_char_used, index_by = r
|
|
432
435
|
|
|
@@ -460,13 +463,30 @@ module PLSQL
|
|
|
460
463
|
|
|
461
464
|
if elem_type_package != nil
|
|
462
465
|
element_metadata[:fields] = get_field_definitions(element_metadata)
|
|
466
|
+
elsif elem_type_name && elem_type_name =~ /\A(.+)%ROWTYPE\z/
|
|
467
|
+
# TABLE OF table%ROWTYPE: Oracle stores elem_type_name as "TABLE_NAME%ROWTYPE"
|
|
468
|
+
rowtype_table_name = $1
|
|
469
|
+
check_owner = elem_type_owner || @schema_name
|
|
470
|
+
object_type_row = @schema.select_first(
|
|
471
|
+
"SELECT object_type FROM ALL_OBJECTS WHERE owner = :owner AND object_name = :name AND object_type IN ('TABLE', 'VIEW')",
|
|
472
|
+
check_owner, rowtype_table_name)
|
|
473
|
+
if object_type_row
|
|
474
|
+
element_metadata[:type_owner] ||= check_owner
|
|
475
|
+
element_metadata[:type_name] = rowtype_table_name
|
|
476
|
+
element_metadata[:sql_type_name] = build_sql_type_name(check_owner, nil, rowtype_table_name)
|
|
477
|
+
element_metadata[:data_type] = "PL/SQL RECORD"
|
|
478
|
+
element_metadata[:type_object_type] = object_type_row[0]
|
|
479
|
+
element_metadata[:fields] = get_field_definitions(element_metadata)
|
|
480
|
+
else
|
|
481
|
+
raise ArgumentError, "Could not resolve #{check_owner}.#{rowtype_table_name} to a table or view for #{elem_type_name}"
|
|
482
|
+
end
|
|
463
483
|
end
|
|
464
484
|
when "TYPE"
|
|
465
485
|
r = @schema.select_first(
|
|
466
486
|
"SELECT elem_type_owner, elem_type_name, length, precision, scale, char_used
|
|
467
487
|
FROM ALL_COLL_TYPES t
|
|
468
488
|
WHERE t.owner = :owner AND t.TYPE_NAME = :type_name",
|
|
469
|
-
|
|
489
|
+
argument_metadata[:type_owner], argument_metadata[:type_name]
|
|
470
490
|
)
|
|
471
491
|
elem_type_owner, elem_type_name, elem_length, elem_precision, elem_scale, elem_char_used = r
|
|
472
492
|
|
data/lib/plsql/schema.rb
CHANGED
|
@@ -41,7 +41,7 @@ module PLSQL
|
|
|
41
41
|
# or
|
|
42
42
|
#
|
|
43
43
|
# plsql.connection = java.sql.DriverManager.getConnection(
|
|
44
|
-
# "jdbc:oracle:thin
|
|
44
|
+
# "jdbc:oracle:thin:@//#{database_host}:#{database_port}/#{database_service_name}",
|
|
45
45
|
# database_user, database_password)
|
|
46
46
|
#
|
|
47
47
|
def connection=(conn)
|
|
@@ -99,12 +99,15 @@ module PLSQL
|
|
|
99
99
|
@original_schema.default_timezone
|
|
100
100
|
else
|
|
101
101
|
@default_timezone ||
|
|
102
|
-
# Use ActiveRecord default_timezone when ActiveRecord connection is used
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
# Use ActiveRecord default_timezone when ActiveRecord connection is used.
|
|
103
|
+
# Prefer the module-level accessor (AR 7.0+; the only one in AR 8.0+) so
|
|
104
|
+
# that AR 7.0/7.1's deprecation warning for ActiveRecord::Base.default_timezone
|
|
105
|
+
# is not emitted. Fall back to the per-class accessor only on pre-7.0 AR,
|
|
106
|
+
# where ActiveRecord.default_timezone does not exist.
|
|
107
|
+
(@connection && @connection.activerecord_class &&
|
|
108
|
+
(ActiveRecord.respond_to?(:default_timezone) ?
|
|
109
|
+
ActiveRecord.default_timezone :
|
|
110
|
+
@connection.activerecord_class.default_timezone)) ||
|
|
108
111
|
# default to local timezone
|
|
109
112
|
:local
|
|
110
113
|
end
|
data/lib/plsql/variable.rb
CHANGED
|
@@ -9,7 +9,8 @@ module PLSQL
|
|
|
9
9
|
AND type = 'PACKAGE'
|
|
10
10
|
AND UPPER(text) LIKE :variable_name",
|
|
11
11
|
override_schema_name || schema.schema_name, package, "%#{variable_upcase}%").each do |row|
|
|
12
|
-
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
|
|
12
|
+
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i ||
|
|
13
|
+
row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*(:=|DEFAULT)\s*$/i
|
|
13
14
|
return new(schema, variable, package, $2.strip, override_schema_name)
|
|
14
15
|
end
|
|
15
16
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-plsql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raimonds Simanovskis
|
|
@@ -72,7 +72,7 @@ dependencies:
|
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: '2.1'
|
|
75
|
-
type: :
|
|
75
|
+
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
@@ -96,7 +96,6 @@ files:
|
|
|
96
96
|
- lib/plsql/connection.rb
|
|
97
97
|
- lib/plsql/helpers.rb
|
|
98
98
|
- lib/plsql/jdbc_connection.rb
|
|
99
|
-
- lib/plsql/oci8_patches.rb
|
|
100
99
|
- lib/plsql/oci_connection.rb
|
|
101
100
|
- lib/plsql/package.rb
|
|
102
101
|
- lib/plsql/procedure.rb
|
data/lib/plsql/oci8_patches.rb
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# apply TIMESTAMP fractional seconds patch to ruby-oci8 2.0.3
|
|
2
|
-
# see http://rubyforge.org/forum/forum.php?thread_id=46576&forum_id=1078
|
|
3
|
-
if OCI8::VERSION == "2.0.3" &&
|
|
4
|
-
!OCI8::BindType::Util.method_defined?(:datetime_to_array_without_timestamp_patch)
|
|
5
|
-
|
|
6
|
-
OCI8::BindType::Util.module_eval do
|
|
7
|
-
alias :datetime_to_array_without_timestamp_patch :datetime_to_array
|
|
8
|
-
def datetime_to_array(val, full)
|
|
9
|
-
result = datetime_to_array_without_timestamp_patch(val, full)
|
|
10
|
-
if result && result[6] == 0
|
|
11
|
-
if val.respond_to? :nsec
|
|
12
|
-
fsec = val.nsec
|
|
13
|
-
elsif val.respond_to? :usec
|
|
14
|
-
fsec = val.usec * 1000
|
|
15
|
-
else
|
|
16
|
-
fsec = 0
|
|
17
|
-
end
|
|
18
|
-
result[6] = fsec
|
|
19
|
-
end
|
|
20
|
-
result
|
|
21
|
-
end
|
|
22
|
-
private :datetime_to_array_without_timestamp_patch, :datetime_to_array
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
end
|