safrano 0.5.3 → 0.6.0
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/lib/core_ext/Date/format.rb +47 -0
- data/lib/core_ext/DateTime/format.rb +54 -0
- data/lib/core_ext/Dir/iter.rb +7 -5
- data/lib/core_ext/Hash/transform.rb +14 -6
- data/lib/core_ext/Numeric/convert.rb +25 -0
- data/lib/core_ext/Time/format.rb +71 -0
- data/lib/core_ext/date.rb +5 -0
- data/lib/core_ext/datetime.rb +5 -0
- data/lib/core_ext/numeric.rb +3 -0
- data/lib/core_ext/time.rb +5 -0
- data/lib/odata/attribute.rb +8 -6
- data/lib/odata/batch.rb +3 -3
- data/lib/odata/collection.rb +9 -9
- data/lib/odata/collection_filter.rb +3 -1
- data/lib/odata/collection_media.rb +4 -27
- data/lib/odata/collection_order.rb +1 -1
- data/lib/odata/common_logger.rb +5 -27
- data/lib/odata/complex_type.rb +61 -67
- data/lib/odata/edm/primitive_types.rb +110 -42
- data/lib/odata/entity.rb +14 -47
- data/lib/odata/error.rb +7 -7
- data/lib/odata/expand.rb +2 -2
- data/lib/odata/filter/base.rb +10 -1
- data/lib/odata/filter/error.rb +2 -2
- data/lib/odata/filter/parse.rb +16 -2
- data/lib/odata/filter/sequel.rb +31 -4
- data/lib/odata/filter/sequel_datetime_adapter.rb +21 -0
- data/lib/odata/filter/token.rb +18 -5
- data/lib/odata/filter/tree.rb +83 -9
- data/lib/odata/function_import.rb +19 -18
- data/lib/odata/model_ext.rb +96 -38
- data/lib/odata/request/json.rb +171 -0
- data/lib/odata/transition.rb +13 -9
- data/lib/odata/url_parameters.rb +3 -3
- data/lib/odata/walker.rb +9 -9
- data/lib/safrano/multipart.rb +1 -3
- data/lib/safrano/rack_app.rb +2 -14
- data/lib/safrano/rack_builder.rb +0 -15
- data/lib/safrano/request.rb +3 -3
- data/lib/safrano/response.rb +3 -3
- data/lib/safrano/service.rb +43 -12
- data/lib/safrano/type_mapping.rb +149 -0
- data/lib/safrano/version.rb +1 -2
- data/lib/safrano.rb +3 -0
- metadata +54 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26b1634021c6ab2117b0b2fe4fb8c49242d4a045b6afafec604ef61e26c3bc48
|
4
|
+
data.tar.gz: 86adac4fb2ab8ad02d570f373325b7e1c4945941a01e2328fc50ff10c9460abd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c19b30f23c22127742d8c557a23feec6de75a91d20b1b8b0033fb85e4f1549e92de300e17cb3d80564f92ed0dcd81096b5b4aca1a268eba803b6bbd6a4c0a75c
|
7
|
+
data.tar.gz: ee3a9d73e9e1d5862cb1a699786e9f81c5a58d7937678b5a138eecae08ccc10e49248f70066ede55e270fb361744978ca4b5b9a75bbd6e62a2c4685e6fc2ae46
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Safrano
|
4
|
+
module CoreExt
|
5
|
+
module Date
|
6
|
+
module Format
|
7
|
+
# Date is a DateTime with time 00:00:00 thus the milliseconds are ALWAYS 000
|
8
|
+
# excepted when we get Date(0) = Epoch date
|
9
|
+
REGEX = /\/Date\((?:(-?\d+)000|0+)\)\//.freeze
|
10
|
+
# Input: /Date(86400000)/
|
11
|
+
# Match groups
|
12
|
+
# 1. 86400
|
13
|
+
# Input: /Date(0)/
|
14
|
+
# Match groups
|
15
|
+
# 1.
|
16
|
+
|
17
|
+
def from_edm_json(instr)
|
18
|
+
return unless (md = instr.match(REGEX))
|
19
|
+
|
20
|
+
if md[1].nil? # no offset relative to Epoch
|
21
|
+
::Date.new(1970, 1, 1).freeze
|
22
|
+
else
|
23
|
+
# parse as seconds since epoch
|
24
|
+
::Date.strptime(md[1], '%s')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
module CoreIncl
|
31
|
+
module Date
|
32
|
+
module Format
|
33
|
+
# https://www.odata.org/documentation/odata-version-2-0/json-format/
|
34
|
+
# Edm.DateTime
|
35
|
+
# "/Date(<ticks>["+" | "-" <offset>)/"
|
36
|
+
# <ticks> = number of milliseconds since midnight Jan 1, 1970
|
37
|
+
# <offset> = number of minutes to add or subtract
|
38
|
+
# https://stackoverflow.com/questions/10286204/what-is-the-right-json-date-format/10286228#10286228
|
39
|
+
def to_edm_json
|
40
|
+
# no offset
|
41
|
+
# --> %Q milliseconds since epoch
|
42
|
+
strftime('/Date(%Q)/')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Safrano
|
4
|
+
module CoreExt
|
5
|
+
module DateTime
|
6
|
+
module Format
|
7
|
+
REGEX = /\/Date\((-?\d+)(?:([-+])(\d+))?\)\//.freeze
|
8
|
+
# Input: /Date(120000+150)/
|
9
|
+
# Match groups
|
10
|
+
# 1. 120000
|
11
|
+
# 2. +
|
12
|
+
# 3. 150
|
13
|
+
|
14
|
+
def from_edm_json(instr)
|
15
|
+
return unless (md = instr.match(REGEX))
|
16
|
+
|
17
|
+
if md[3].nil? # no offset
|
18
|
+
::DateTime.strptime(md[1], '%Q')
|
19
|
+
else
|
20
|
+
# offset in hours / mins
|
21
|
+
off_h, off_m = md[3].to_i.divmod(60)
|
22
|
+
# DateTime.strptime("120000+2:30", '%Q%z') milliseconds since epoch with a z-offset
|
23
|
+
# --> #<DateTime: 1970-01-01T02:32:00+02:30 ((2440588j,120s,0n),+9000s,2299161j)>
|
24
|
+
::DateTime.strptime("#{md[1]}#{md[2]}#{off_h}:#{off_m}", '%Q%z')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
module CoreIncl
|
31
|
+
module DateTime
|
32
|
+
module Format
|
33
|
+
# https://www.odata.org/documentation/odata-version-2-0/json-format/
|
34
|
+
# Edm.DateTime
|
35
|
+
# "/Date(<ticks>["+" | "-" <offset>)/"
|
36
|
+
# <ticks> = number of milliseconds since midnight Jan 1, 1970
|
37
|
+
# <offset> = number of minutes to add or subtract
|
38
|
+
# https://stackoverflow.com/questions/10286204/what-is-the-right-json-date-format/10286228#10286228
|
39
|
+
def to_edm_json
|
40
|
+
if offset.zero?
|
41
|
+
# no offset
|
42
|
+
# --> %Q milliseconds since epoch
|
43
|
+
strftime('/Date(%Q)/')
|
44
|
+
else
|
45
|
+
# same as above with GMT offset in minutes
|
46
|
+
# DateTime offset is Rational ; fraction of hours per Day --> *24*60
|
47
|
+
min_off_s = (min_off = (offset * 60 * 24).to_i) > 0 ? "+#{min_off}" : min_off.to_s
|
48
|
+
"/Date(#{strftime('%Q')}#{min_off_s})/"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/core_ext/Dir/iter.rb
CHANGED
@@ -5,13 +5,15 @@ module Safrano
|
|
5
5
|
module CoreExt
|
6
6
|
module Dir
|
7
7
|
module Iter
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
unless ::Dir.respond_to? :each_child
|
9
|
+
def each_child(dir)
|
10
|
+
::Dir.foreach(dir) do |x|
|
11
|
+
next if (x == '.') || (x == '..')
|
11
12
|
|
12
|
-
|
13
|
+
yield x
|
14
|
+
end
|
13
15
|
end
|
14
|
-
end
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
@@ -5,15 +5,23 @@ module Safrano
|
|
5
5
|
module CoreIncl
|
6
6
|
module Hash
|
7
7
|
module Transform
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
unless method_defined? :transform_keys!
|
9
|
+
def transform_keys!
|
10
|
+
keys.each do |key|
|
11
|
+
self[yield(key)] = delete(key)
|
12
|
+
end
|
13
|
+
self
|
11
14
|
end
|
12
|
-
|
13
|
-
end unless method_defined? :transform_keys!
|
15
|
+
end
|
14
16
|
|
15
17
|
def symbolize_keys!
|
16
|
-
transform_keys!
|
18
|
+
transform_keys! do |key|
|
19
|
+
begin
|
20
|
+
key.to_sym
|
21
|
+
rescue StandardError
|
22
|
+
key
|
23
|
+
end
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'bigdecimal/util'
|
4
|
+
|
5
|
+
module Safrano
|
6
|
+
module CoreIncl
|
7
|
+
module Numeric
|
8
|
+
module Convert
|
9
|
+
def toDecimalString
|
10
|
+
BigDecimal(to_s).to_s('F')
|
11
|
+
end
|
12
|
+
|
13
|
+
def toDecimalPrecisionString(precision)
|
14
|
+
p = Integer(precision)
|
15
|
+
BigDecimal(self, p).to_s('F')
|
16
|
+
end
|
17
|
+
|
18
|
+
def toDecimalPrecisionScaleString(precision, scale)
|
19
|
+
p = Integer(precision)
|
20
|
+
format("%#{p + 2}.#{scale}f", self).strip
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Safrano
|
4
|
+
module CoreExt
|
5
|
+
module Time
|
6
|
+
module Format
|
7
|
+
REGEX = /\/Date\((-?\d+)(?:([-+])(\d+))?\)\//.freeze
|
8
|
+
# Input: /Date(120000+150)/
|
9
|
+
# Match groups
|
10
|
+
# 1. 120000
|
11
|
+
# 2. +
|
12
|
+
# 3. 150
|
13
|
+
|
14
|
+
def from_edm_json(instr)
|
15
|
+
return unless (md = instr.match(REGEX))
|
16
|
+
|
17
|
+
sec, milli = md[1].to_i.divmod(1000)
|
18
|
+
secm = milli.zero? ? sec : sec + Float(milli) / 1000
|
19
|
+
if md[3].nil? # no offset
|
20
|
+
# ::Time.at(sec, milli, :millisecond) # not supported in ruby 2.4
|
21
|
+
::Time.gm(1970, 1, 1) + secm
|
22
|
+
else
|
23
|
+
# offset in hours / mins
|
24
|
+
off_h, off_m = md[3].to_i.divmod(60)
|
25
|
+
# add leading 0 because Time.at is expecting "+HH:MM", "-HH:MM"
|
26
|
+
off_h = off_h < 10 ? "0#{off_h}" : off_h.to_s
|
27
|
+
off_m = off_m < 10 ? "0#{off_m}" : off_m.to_s
|
28
|
+
# Time.strptime("120000+2:30", '%Q%z') milliseconds since epoch with a z-offset
|
29
|
+
# --> #<DateTime: 1970-01-01T02:32:00+02:30 ((2440588j,120s,0n),+9000s,2299161j)>
|
30
|
+
# ::Time.at(sec, milli, :millisecond, in: "#{md[2]}#{off_h}:#{off_m}") # not supported in ruby 2.4
|
31
|
+
offset_str = "#{md[2]}#{off_h}:#{off_m}"
|
32
|
+
::Time.new(1970, 1, 1, 0, 0, 0, offset_str) + secm + ::Time.zone_offset(offset_str)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module CoreIncl
|
40
|
+
module Time
|
41
|
+
module Format
|
42
|
+
# Welcome to Hell
|
43
|
+
# https://www.odata.org/documentation/odata-version-2-0/json-format/
|
44
|
+
# Edm.DateTime
|
45
|
+
# "/Date(<ticks>["+" | "-" <offset>)/"
|
46
|
+
# <ticks> = number of milliseconds since midnight Jan 1, 1970
|
47
|
+
# <offset> = number of minutes to add or subtract
|
48
|
+
# https://stackoverflow.com/questions/10286204/what-is-the-right-json-date-format/10286228#10286228
|
49
|
+
# also https://www.hanselman.com/blog/on-the-nightmare-that-is-json-dates-plus-jsonnet-and-aspnet-web-api
|
50
|
+
# and https://itecnote.com/tecnote/c-use-json-net-to-parse-json-date-of-format-dateepochtime-offset/
|
51
|
+
# https://docs.microsoft.com/de-de/dotnet/standard/datetime/system-text-json-support
|
52
|
+
# https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/stand-alone-json-serialization#datestimes-and-json
|
53
|
+
# https://blogs.sap.com/2017/01/05/date-and-time-in-sap-gateway-foundation/
|
54
|
+
def to_edm_json
|
55
|
+
if utc? || (gmt_offset == 0)
|
56
|
+
# no offset
|
57
|
+
# %s : seconds since unix epoch
|
58
|
+
# %L : milliseconds 000-999
|
59
|
+
# --> %S%L milliseconds since epoch
|
60
|
+
strftime('/Date(%s%L)/')
|
61
|
+
else
|
62
|
+
# same as above with GMT offset in minutes
|
63
|
+
|
64
|
+
min_off_s = (min_off = gmt_offset / 60) > 0 ? "+#{min_off}" : min_off.to_s
|
65
|
+
"/Date(#{strftime('%s%L')}#{min_off_s})/"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/odata/attribute.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
-
require_relative '../safrano/core
|
5
|
-
require_relative './entity
|
4
|
+
require_relative '../safrano/core'
|
5
|
+
require_relative './entity'
|
6
6
|
|
7
7
|
module Safrano
|
8
8
|
# Represents a named and valued attribute of an Entity
|
@@ -18,12 +18,14 @@ module Safrano
|
|
18
18
|
def value
|
19
19
|
# WARNING ... this require more work to handle the timezones topci
|
20
20
|
# currently it is just set to make some minimal testcase work
|
21
|
+
# See also model_ext.rb
|
21
22
|
case (v = @entity.values[@name.to_sym])
|
23
|
+
when Date
|
24
|
+
v.to_edm_json
|
22
25
|
when Time
|
23
|
-
|
24
|
-
|
25
|
-
v.
|
26
|
-
|
26
|
+
v.to_edm_json
|
27
|
+
when DateTime
|
28
|
+
v.to_edm_json
|
27
29
|
else
|
28
30
|
v
|
29
31
|
end
|
data/lib/odata/batch.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../safrano/rack_app
|
4
|
-
require_relative '../safrano/core
|
3
|
+
require_relative '../safrano/rack_app'
|
4
|
+
require_relative '../safrano/core'
|
5
5
|
require 'rack/body_proxy'
|
6
|
-
require_relative './common_logger
|
6
|
+
require_relative './common_logger'
|
7
7
|
|
8
8
|
module Safrano
|
9
9
|
# Support for OData multipart $batch Requests
|
data/lib/odata/collection.rb
CHANGED
@@ -60,13 +60,13 @@ module Safrano
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def initialize_dataset(dtset = nil)
|
63
|
-
@cx = @cx ||
|
63
|
+
@cx = @cx || dtset || @modelk
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
def initialize_uparms
|
67
67
|
@uparms = UrlParameters4Coll.new(@cx, @params)
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
def odata_get_apply_params
|
71
71
|
@uparms.apply_to_dataset(@cx).map_result! do |dataset|
|
72
72
|
@cx = dataset
|
@@ -134,14 +134,14 @@ module Safrano
|
|
134
134
|
|
135
135
|
# on model class level we return the collection
|
136
136
|
def odata_get(req)
|
137
|
-
@params
|
138
|
-
initialize_dataset
|
137
|
+
@params ||= req.params
|
138
|
+
initialize_dataset
|
139
139
|
initialize_uparms
|
140
|
-
@uparms.check_all.if_valid
|
141
|
-
odata_get_apply_params.if_valid
|
140
|
+
@uparms.check_all.if_valid do |_ret|
|
141
|
+
odata_get_apply_params.if_valid do |_ret|
|
142
142
|
odata_get_output(req)
|
143
|
-
|
144
|
-
|
143
|
+
end
|
144
|
+
end.tap_error { |e| return e.odata_get(req) }.result
|
145
145
|
end
|
146
146
|
|
147
147
|
def odata_post(req)
|
@@ -143,8 +143,8 @@ module Safrano
|
|
143
143
|
# with directory Tree structure
|
144
144
|
|
145
145
|
class StaticTree < Static
|
146
|
-
SEP = '/00/'
|
147
|
-
VERS = '/v'
|
146
|
+
SEP = '/00/'
|
147
|
+
VERS = '/v'
|
148
148
|
|
149
149
|
def self.path_builder(ids)
|
150
150
|
ids.map { |id| id.to_s.chars.join('/') }.join(SEP) << VERS
|
@@ -276,7 +276,7 @@ module Safrano
|
|
276
276
|
|
277
277
|
media_handler.check_before_create(data: data,
|
278
278
|
entity: new_entity,
|
279
|
-
filename: filename).if_valid
|
279
|
+
filename: filename).if_valid do |_ret|
|
280
280
|
# to_one rels are create with FK data set on the parent entity
|
281
281
|
if parent
|
282
282
|
odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
|
@@ -302,7 +302,7 @@ module Safrano
|
|
302
302
|
# Contract.valid([201, EMPTY_HASH, new_entity.to_odata_post_json(service: req.service)])
|
303
303
|
# TODO quirks array mode !
|
304
304
|
Contract.valid([201, EMPTY_HASH, new_entity.to_odata_create_json(request: req)])
|
305
|
-
|
305
|
+
end.tap_error { |e| return e.odata_get(req) }.result
|
306
306
|
|
307
307
|
else # TODO: other formats
|
308
308
|
415
|
@@ -311,26 +311,3 @@ module Safrano
|
|
311
311
|
end
|
312
312
|
end
|
313
313
|
end
|
314
|
-
|
315
|
-
# deprecated
|
316
|
-
# REMOVE 0.6
|
317
|
-
module OData
|
318
|
-
module Media
|
319
|
-
class Static < ::Safrano::Media::Static
|
320
|
-
def initialize(root: nil, mediaklass:)
|
321
|
-
::Safrano::Deprecation.deprecate('OData::Media::Static',
|
322
|
-
'Use Safrano::Media::Static instead')
|
323
|
-
|
324
|
-
super
|
325
|
-
end
|
326
|
-
end
|
327
|
-
class StaticTree < ::Safrano::Media::StaticTree
|
328
|
-
def initialize(root: nil, mediaklass:)
|
329
|
-
::Safrano::Deprecation.deprecate('OData::Media::StaticTree',
|
330
|
-
'Use Safrano::Media::StaticTree instead')
|
331
|
-
|
332
|
-
super
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|
data/lib/odata/common_logger.rb
CHANGED
@@ -10,36 +10,14 @@ module Rack
|
|
10
10
|
# Handle https://github.com/rack/rack/pull/1526
|
11
11
|
# new in Rack 2.2.2 : Format has now 11 placeholders instead of 10
|
12
12
|
|
13
|
-
MSG_FUNC =
|
13
|
+
MSG_FUNC = case FORMAT.count('%')
|
14
|
+
when 10
|
14
15
|
lambda { |env, length, status, began_at|
|
15
|
-
FORMAT % [
|
16
|
-
env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-',
|
17
|
-
env['REMOTE_USER'] || '-',
|
18
|
-
Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'),
|
19
|
-
env[REQUEST_METHOD],
|
20
|
-
env[SCRIPT_NAME] + env[PATH_INFO],
|
21
|
-
env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}",
|
22
|
-
env[SERVER_PROTOCOL],
|
23
|
-
status.to_s[0..3],
|
24
|
-
length,
|
25
|
-
Utils.clock_time - began_at
|
26
|
-
]
|
16
|
+
format(FORMAT, env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-', env['REMOTE_USER'] || '-', Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'), env[REQUEST_METHOD], env[SCRIPT_NAME] + env[PATH_INFO], env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}", env[SERVER_PROTOCOL], status.to_s[0..3], length, Utils.clock_time - began_at)
|
27
17
|
}
|
28
|
-
|
18
|
+
when 11
|
29
19
|
lambda { |env, length, status, began_at|
|
30
|
-
FORMAT % [
|
31
|
-
env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-',
|
32
|
-
env['REMOTE_USER'] || '-',
|
33
|
-
Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'),
|
34
|
-
env[REQUEST_METHOD],
|
35
|
-
env[SCRIPT_NAME],
|
36
|
-
env[PATH_INFO],
|
37
|
-
env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}",
|
38
|
-
env[SERVER_PROTOCOL],
|
39
|
-
status.to_s[0..3],
|
40
|
-
length,
|
41
|
-
Utils.clock_time - began_at
|
42
|
-
]
|
20
|
+
format(FORMAT, env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR'] || '-', env['REMOTE_USER'] || '-', Time.now.strftime('%d/%b/%Y:%H:%M:%S %z'), env[REQUEST_METHOD], env[SCRIPT_NAME], env[PATH_INFO], env[QUERY_STRING].empty? ? '' : "?#{env[QUERY_STRING]}", env[SERVER_PROTOCOL], status.to_s[0..3], length, Utils.clock_time - began_at)
|
43
21
|
}
|
44
22
|
end
|
45
23
|
|