emery 0.0.3 → 0.0.14
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/emery.rb +0 -1
- data/lib/emery/dataclass.rb +4 -0
- data/lib/emery/jsoner.rb +11 -4
- data/test/jsoner_test.rb +30 -2
- metadata +2 -4
- data/lib/emery/tod.rb +0 -260
- data/test/tod_test.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bda1937694f9f1e76788bdab5f20ff4d80ba41be117cf324850f36c73ee875d0
|
4
|
+
data.tar.gz: 50e1a054e8882b8c0f2d62b0a860a2ec9d286056f4b5c08e81f33bce76795b68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d785c11bd404df22387441c72715ead3087a62bd7e01700e8204a5ad415519a00227ff0f01973688c6acd935aa6b37c68b64d1862eb09a27e7da1576334b1f0d
|
7
|
+
data.tar.gz: 8943ebbf92cd0e94a8ef479f893cda0bb6e9af7e11c8af4167130afbf12fbcd75e39b63c56c5ca0633d8562e29b33bf0590d7225c1a9403f2108f60ade0c485c
|
data/lib/emery.rb
CHANGED
data/lib/emery/dataclass.rb
CHANGED
data/lib/emery/jsoner.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require "json"
|
2
2
|
require "date"
|
3
3
|
|
4
|
-
require "emery/type"
|
5
|
-
|
6
4
|
class JsonerError < StandardError
|
7
5
|
end
|
8
6
|
|
@@ -16,6 +14,15 @@ module Jsoner
|
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
17
|
+
T::UntypedType.class_eval do
|
18
|
+
def jsoner_deserialize(json_value)
|
19
|
+
json_value
|
20
|
+
end
|
21
|
+
def jsoner_serialize(value)
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
19
26
|
T::ArrayType.class_eval do
|
20
27
|
def jsoner_deserialize(json_value)
|
21
28
|
T.check_not_nil(self, json_value)
|
@@ -63,7 +70,7 @@ module Jsoner
|
|
63
70
|
types.each do |type|
|
64
71
|
begin
|
65
72
|
return Jsoner.deserialize(type, json_value)
|
66
|
-
rescue
|
73
|
+
rescue JsonerError
|
67
74
|
end
|
68
75
|
end
|
69
76
|
raise JsonerError.new("Value '#{json_value.inspect.to_s}' can not be deserialized as any of #{@types.map { |t| t.to_s}.join(', ')}")
|
@@ -208,4 +215,4 @@ module Jsoner
|
|
208
215
|
raise JsonerError.new(error.message)
|
209
216
|
end
|
210
217
|
end
|
211
|
-
end
|
218
|
+
end
|
data/test/jsoner_test.rb
CHANGED
@@ -33,10 +33,16 @@ class PlainTypesDeserialization < Test::Unit::TestCase
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def test_deserialize_true
|
37
37
|
data = Jsoner.from_json(Boolean, 'true')
|
38
38
|
T.check(Boolean, data)
|
39
|
-
assert_equal true, data, "Boolean should be parsable from JSON"
|
39
|
+
assert_equal true, data, "Boolean true should be parsable from JSON"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_deserialize_false
|
43
|
+
data = Jsoner.from_json(Boolean, 'false')
|
44
|
+
T.check(Boolean, data)
|
45
|
+
assert_equal false, data, "Boolean false should be parsable from JSON"
|
40
46
|
end
|
41
47
|
|
42
48
|
def test_deserialize_boolean_fail
|
@@ -213,6 +219,28 @@ class HashSerialization < Test::Unit::TestCase
|
|
213
219
|
end
|
214
220
|
end
|
215
221
|
|
222
|
+
class UntypedDeserialization < Test::Unit::TestCase
|
223
|
+
def test_deserialize_hash
|
224
|
+
data = Jsoner.from_json(Untyped, '{"one":123,"two":"some string"}')
|
225
|
+
assert_equal ({"one" => 123, "two" => "some string"}), data
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_deserialize_array
|
229
|
+
data = Jsoner.from_json(Untyped, '[123,"some string"]')
|
230
|
+
assert_equal [123, "some string"], data
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class UntypedSerialization < Test::Unit::TestCase
|
235
|
+
def test_serialize_hash
|
236
|
+
assert_equal '{"one":123,"two":"some string"}', Jsoner.to_json(Untyped, {"one" => 123, "two" => "some string"})
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_serialize_array
|
240
|
+
assert_equal '[123,"some string"]', Jsoner.to_json(Untyped, [123, "some string"])
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
216
244
|
class AnyDeserialization < Test::Unit::TestCase
|
217
245
|
def test_deserialize
|
218
246
|
data = Jsoner.from_json(T.any(String, Integer), '"the string"')
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Sapronov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -34,12 +34,10 @@ files:
|
|
34
34
|
- lib/emery/dataclass.rb
|
35
35
|
- lib/emery/enum.rb
|
36
36
|
- lib/emery/jsoner.rb
|
37
|
-
- lib/emery/tod.rb
|
38
37
|
- lib/emery/type.rb
|
39
38
|
- test/dataclass_test.rb
|
40
39
|
- test/enum_test.rb
|
41
40
|
- test/jsoner_test.rb
|
42
|
-
- test/tod_test.rb
|
43
41
|
- test/type_test.rb
|
44
42
|
homepage: https://github.com/vsapronov/emery
|
45
43
|
licenses:
|
data/lib/emery/tod.rb
DELETED
@@ -1,260 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
|
3
|
-
source: https://github.com/jackc/tod
|
4
|
-
|
5
|
-
Copyright (c) 2010-2015 Jack Christensen
|
6
|
-
|
7
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
-
a copy of this software and associated documentation files (the
|
9
|
-
"Software"), to deal in the Software without restriction, including
|
10
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
-
permit persons to whom the Software is furnished to do so, subject to
|
13
|
-
the following conditions:
|
14
|
-
|
15
|
-
The above copyright notice and this permission notice shall be
|
16
|
-
included in all copies or substantial portions of the Software.
|
17
|
-
|
18
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
-
|
26
|
-
=end
|
27
|
-
|
28
|
-
class TimeOfDay
|
29
|
-
include Comparable
|
30
|
-
|
31
|
-
def self.jsoner_deserialize(json_value)
|
32
|
-
TimeOfDay.parse(T.check(String, json_value))
|
33
|
-
end
|
34
|
-
def self.jsoner_serialize(value)
|
35
|
-
T.check(TimeOfDay, value).to_s
|
36
|
-
end
|
37
|
-
|
38
|
-
attr_reader :hour, :minute, :second, :second_of_day
|
39
|
-
alias_method :min, :minute
|
40
|
-
alias_method :sec, :second
|
41
|
-
alias_method :to_i, :second_of_day
|
42
|
-
|
43
|
-
PARSE_24H_REGEX = /
|
44
|
-
\A
|
45
|
-
([01]?\d|2[0-4])
|
46
|
-
:?
|
47
|
-
([0-5]\d)?
|
48
|
-
:?
|
49
|
-
([0-5]\d)?
|
50
|
-
\z
|
51
|
-
/x
|
52
|
-
|
53
|
-
PARSE_12H_REGEX = /
|
54
|
-
\A
|
55
|
-
(0?\d|1[0-2])
|
56
|
-
:?
|
57
|
-
([0-5]\d)?
|
58
|
-
:?
|
59
|
-
([0-5]\d)?
|
60
|
-
\s*
|
61
|
-
([ap])
|
62
|
-
\.?
|
63
|
-
\s*
|
64
|
-
m?
|
65
|
-
\.?
|
66
|
-
\z
|
67
|
-
/x
|
68
|
-
|
69
|
-
WORDS = {
|
70
|
-
"noon" => "12pm".freeze,
|
71
|
-
"midnight" => "12am".freeze
|
72
|
-
}
|
73
|
-
|
74
|
-
NUM_SECONDS_IN_DAY = 86400
|
75
|
-
NUM_SECONDS_IN_HOUR = 3600
|
76
|
-
NUM_SECONDS_IN_MINUTE = 60
|
77
|
-
|
78
|
-
FORMATS = {
|
79
|
-
short: "%-l:%M %P".freeze,
|
80
|
-
medium: "%-l:%M:%S %P".freeze,
|
81
|
-
time: "%H:%M".freeze
|
82
|
-
}
|
83
|
-
|
84
|
-
def initialize(h, m=0, s=0)
|
85
|
-
@hour = Integer(h)
|
86
|
-
@minute = Integer(m)
|
87
|
-
@second = Integer(s)
|
88
|
-
|
89
|
-
raise ArgumentError, "hour must be between 0 and 24" unless (0..24).include?(@hour)
|
90
|
-
if @hour == 24 && (@minute != 0 || @second != 0)
|
91
|
-
raise ArgumentError, "hour can only be 24 when minute and second are 0"
|
92
|
-
end
|
93
|
-
raise ArgumentError, "minute must be between 0 and 59" unless (0..59).include?(@minute)
|
94
|
-
raise ArgumentError, "second must be between 0 and 59" unless (0..59).include?(@second)
|
95
|
-
|
96
|
-
@second_of_day = @hour * 60 * 60 + @minute * 60 + @second
|
97
|
-
|
98
|
-
freeze # TimeOfDay instances are value objects
|
99
|
-
end
|
100
|
-
|
101
|
-
def <=>(other)
|
102
|
-
return unless other.respond_to?(:second_of_day)
|
103
|
-
@second_of_day <=> other.second_of_day
|
104
|
-
end
|
105
|
-
|
106
|
-
# Rounding to the given nearest number of seconds
|
107
|
-
def round(round_sec = 1)
|
108
|
-
down = self - (self.to_i % round_sec)
|
109
|
-
up = down + round_sec
|
110
|
-
|
111
|
-
difference_down = self - down
|
112
|
-
difference_up = up - self
|
113
|
-
|
114
|
-
if (difference_down < difference_up)
|
115
|
-
return down
|
116
|
-
else
|
117
|
-
return up
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Formats identically to Time#strftime
|
122
|
-
def strftime(format_string)
|
123
|
-
# Special case 2400 because strftime will load TimeOfDay into Time which
|
124
|
-
# will convert 24 to 0
|
125
|
-
format_string = format_string.gsub(/%H|%k/, '24') if @hour == 24
|
126
|
-
Time.local(2000,1,1, @hour, @minute, @second).strftime(format_string)
|
127
|
-
end
|
128
|
-
|
129
|
-
def to_formatted_s(format = :default)
|
130
|
-
if formatter = FORMATS[format]
|
131
|
-
if formatter.respond_to?(:call)
|
132
|
-
formatter.call(self).to_s
|
133
|
-
else
|
134
|
-
strftime(formatter)
|
135
|
-
end
|
136
|
-
else
|
137
|
-
strftime "%H:%M:%S"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
alias_method :to_s, :to_formatted_s
|
141
|
-
|
142
|
-
def value_for_database
|
143
|
-
to_s
|
144
|
-
end
|
145
|
-
|
146
|
-
# Return a new TimeOfDay num_seconds greater than self. It will wrap around
|
147
|
-
# at midnight.
|
148
|
-
def +(num_seconds)
|
149
|
-
TimeOfDay.from_second_of_day @second_of_day + num_seconds
|
150
|
-
end
|
151
|
-
|
152
|
-
# Return a new TimeOfDay num_seconds less than self. It will wrap around
|
153
|
-
# at midnight.
|
154
|
-
def -(other)
|
155
|
-
if other.instance_of?(TimeOfDay)
|
156
|
-
TimeOfDay.from_second_of_day @second_of_day - other.second_of_day
|
157
|
-
else
|
158
|
-
TimeOfDay.from_second_of_day @second_of_day - other
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns a Time instance on date using self as the time of day
|
163
|
-
# Optional time_zone will build time in that zone
|
164
|
-
def on(date, time_zone=Tod::TimeOfDay.time_zone)
|
165
|
-
time_zone.local date.year, date.month, date.day, @hour, @minute, @second
|
166
|
-
end
|
167
|
-
|
168
|
-
# Build a new TimeOfDay instance from second_of_day
|
169
|
-
#
|
170
|
-
# TimeOfDay.from_second_of_day(3600) == TimeOfDay.new(1) # => true
|
171
|
-
def self.from_second_of_day(second_of_day)
|
172
|
-
second_of_day = Integer(second_of_day)
|
173
|
-
return new 24 if second_of_day == NUM_SECONDS_IN_DAY
|
174
|
-
remaining_seconds = second_of_day % NUM_SECONDS_IN_DAY
|
175
|
-
hour = remaining_seconds / NUM_SECONDS_IN_HOUR
|
176
|
-
remaining_seconds -= hour * NUM_SECONDS_IN_HOUR
|
177
|
-
minute = remaining_seconds / NUM_SECONDS_IN_MINUTE
|
178
|
-
remaining_seconds -= minute * NUM_SECONDS_IN_MINUTE
|
179
|
-
new hour, minute, remaining_seconds
|
180
|
-
end
|
181
|
-
class << self
|
182
|
-
alias :from_i :from_second_of_day
|
183
|
-
end
|
184
|
-
|
185
|
-
# Build a TimeOfDay instance from string
|
186
|
-
#
|
187
|
-
# Strings only need to contain an hour. Minutes, seconds, AM or PM, and colons
|
188
|
-
# are all optional.
|
189
|
-
# TimeOfDay.parse "8" # => 08:00:00
|
190
|
-
# TimeOfDay.parse "8am" # => 08:00:00
|
191
|
-
# TimeOfDay.parse "8pm" # => 20:00:00
|
192
|
-
# TimeOfDay.parse "8p" # => 20:00:00
|
193
|
-
# TimeOfDay.parse "9:30" # => 09:30:00
|
194
|
-
# TimeOfDay.parse "15:30" # => 15:30:00
|
195
|
-
# TimeOfDay.parse "3:30pm" # => 15:30:00
|
196
|
-
# TimeOfDay.parse "1230" # => 12:30:00
|
197
|
-
# TimeOfDay.parse "3:25:58" # => 03:25:58
|
198
|
-
# TimeOfDay.parse "515p" # => 17:15:00
|
199
|
-
# TimeOfDay.parse "151253" # => 15:12:53
|
200
|
-
# You can give a block, that is called with the input if the string is not parsable.
|
201
|
-
# If no block is given an ArgumentError is raised if try_parse returns nil.
|
202
|
-
def self.parse(tod_string)
|
203
|
-
try_parse(tod_string) || (block_given? ? yield(tod_string) : raise(ArgumentError, "Invalid time of day string"))
|
204
|
-
end
|
205
|
-
|
206
|
-
# Same as parse(), but return nil if not parsable (instead of raising an error)
|
207
|
-
# TimeOfDay.try_parse "8am" # => 08:00:00
|
208
|
-
# TimeOfDay.try_parse "" # => nil
|
209
|
-
# TimeOfDay.try_parse "abc" # => nil
|
210
|
-
def self.try_parse(tod_string)
|
211
|
-
tod_string = tod_string.to_s
|
212
|
-
tod_string = tod_string.strip
|
213
|
-
tod_string = tod_string.downcase
|
214
|
-
tod_string = WORDS[tod_string] || tod_string
|
215
|
-
if PARSE_24H_REGEX =~ tod_string || PARSE_12H_REGEX =~ tod_string
|
216
|
-
hour, minute, second, a_or_p = $1.to_i, $2.to_i, $3.to_i, $4
|
217
|
-
if hour == 12 && a_or_p == "a"
|
218
|
-
hour = 0
|
219
|
-
elsif hour < 12 && a_or_p == "p"
|
220
|
-
hour += 12
|
221
|
-
end
|
222
|
-
|
223
|
-
new hour, minute, second
|
224
|
-
else
|
225
|
-
nil
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
# Determine if a string is parsable into a TimeOfDay instance
|
230
|
-
# TimeOfDay.parsable? "8am" # => true
|
231
|
-
# TimeOfDay.parsable? "abc" # => false
|
232
|
-
def self.parsable?(tod_string)
|
233
|
-
!!try_parse(tod_string)
|
234
|
-
end
|
235
|
-
|
236
|
-
# If ActiveSupport TimeZone is available and set use current time zone else return Time
|
237
|
-
def self.time_zone
|
238
|
-
(Time.respond_to?(:zone) && Time.zone) || Time
|
239
|
-
end
|
240
|
-
|
241
|
-
def self.dump(time_of_day)
|
242
|
-
time_of_day =
|
243
|
-
if time_of_day.is_a? Hash
|
244
|
-
# rails multiparam attribute
|
245
|
-
# get hour, minute and second and construct new TimeOfDay object
|
246
|
-
::Tod::TimeOfDay.new(time_of_day[4], time_of_day[5], time_of_day[6])
|
247
|
-
else
|
248
|
-
# return nil, if input is not parsable
|
249
|
-
Tod::TimeOfDay(time_of_day){}
|
250
|
-
end
|
251
|
-
time_of_day.to_s if time_of_day
|
252
|
-
end
|
253
|
-
|
254
|
-
def self.load(time)
|
255
|
-
if time && !time.to_s.empty?
|
256
|
-
return ::Tod::TimeOfDay.new(24) if time.respond_to?(:day) && time.day == 2 && time.hour == 0 && time.min == 0 && time.sec == 0
|
257
|
-
::Tod::TimeOfDay(time)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
data/test/tod_test.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require "test/unit/runner/junitxml"
|
2
|
-
|
3
|
-
require 'emery'
|
4
|
-
|
5
|
-
class TypeCheckTimeOfDay < Test::Unit::TestCase
|
6
|
-
def test_success
|
7
|
-
assert_equal TimeOfDay.parse("10:40:50"), T.check(TimeOfDay, TimeOfDay.parse("10:40:50"))
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_fail
|
11
|
-
assert_raise TypeError do
|
12
|
-
T.check(DateTime, TimeOfDay.parse("10:40:50"))
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class TimeOfDayDeserialization < Test::Unit::TestCase
|
18
|
-
def test_deserialize
|
19
|
-
data = Jsoner.from_json(TimeOfDay, '"10:40:50"')
|
20
|
-
T.check(TimeOfDay, data)
|
21
|
-
assert_equal TimeOfDay.parse("10:40:50"), data
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_deserialize_fail
|
25
|
-
assert_raise JsonerError do
|
26
|
-
Jsoner.from_json(TimeOfDay, '"abc"')
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class TimeOfDaySerialization < Test::Unit::TestCase
|
32
|
-
def test_serialize
|
33
|
-
json_str = Jsoner.to_json(TimeOfDay, TimeOfDay.parse("10:40:50"))
|
34
|
-
assert_equal '"10:40:50"', json_str
|
35
|
-
end
|
36
|
-
end
|