flat_kit 0.3.0 → 1.0.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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +1 -2
  3. data/HISTORY.md +9 -0
  4. data/Manifest.txt +3 -42
  5. data/{bin → exe}/fk +2 -1
  6. data/flat_kit.gemspec +33 -0
  7. data/lib/flat_kit/cli.rb +46 -32
  8. data/lib/flat_kit/command/cat.rb +34 -32
  9. data/lib/flat_kit/command/merge.rb +37 -36
  10. data/lib/flat_kit/command/sort.rb +37 -37
  11. data/lib/flat_kit/command/stats.rb +41 -39
  12. data/lib/flat_kit/command.rb +10 -11
  13. data/lib/flat_kit/descendant_tracker.rb +9 -6
  14. data/lib/flat_kit/error.rb +4 -0
  15. data/lib/flat_kit/event_emitter.rb +5 -2
  16. data/lib/flat_kit/field_stats.rb +31 -26
  17. data/lib/flat_kit/field_type/boolean_type.rb +9 -5
  18. data/lib/flat_kit/field_type/date_type.rb +19 -17
  19. data/lib/flat_kit/field_type/float_type.rb +15 -9
  20. data/lib/flat_kit/field_type/guess_type.rb +9 -6
  21. data/lib/flat_kit/field_type/integer_type.rb +6 -4
  22. data/lib/flat_kit/field_type/null_type.rb +5 -1
  23. data/lib/flat_kit/field_type/string_type.rb +8 -6
  24. data/lib/flat_kit/field_type/timestamp_type.rb +11 -10
  25. data/lib/flat_kit/field_type/unknown_type.rb +12 -8
  26. data/lib/flat_kit/field_type.rb +52 -44
  27. data/lib/flat_kit/format.rb +11 -5
  28. data/lib/flat_kit/input/file.rb +11 -9
  29. data/lib/flat_kit/input/io.rb +18 -21
  30. data/lib/flat_kit/input.rb +8 -7
  31. data/lib/flat_kit/internal_node.rb +22 -19
  32. data/lib/flat_kit/jsonl/format.rb +6 -2
  33. data/lib/flat_kit/jsonl/reader.rb +7 -4
  34. data/lib/flat_kit/jsonl/record.rb +15 -18
  35. data/lib/flat_kit/jsonl/writer.rb +8 -10
  36. data/lib/flat_kit/jsonl.rb +8 -4
  37. data/lib/flat_kit/leaf_node.rb +6 -5
  38. data/lib/flat_kit/log_formatter.rb +20 -0
  39. data/lib/flat_kit/logger.rb +12 -19
  40. data/lib/flat_kit/merge.rb +21 -18
  41. data/lib/flat_kit/merge_tree.rb +5 -6
  42. data/lib/flat_kit/output/file.rb +13 -9
  43. data/lib/flat_kit/output/io.rb +40 -35
  44. data/lib/flat_kit/output.rb +8 -7
  45. data/lib/flat_kit/position.rb +3 -4
  46. data/lib/flat_kit/reader.rb +8 -8
  47. data/lib/flat_kit/record.rb +12 -12
  48. data/lib/flat_kit/sentinel_internal_node.rb +6 -5
  49. data/lib/flat_kit/sentinel_leaf_node.rb +4 -1
  50. data/lib/flat_kit/sort.rb +8 -9
  51. data/lib/flat_kit/stat_type/nominal_stats.rb +13 -7
  52. data/lib/flat_kit/stat_type/numerical_stats.rb +18 -18
  53. data/lib/flat_kit/stat_type/ordinal_stats.rb +8 -13
  54. data/lib/flat_kit/stat_type.rb +18 -13
  55. data/lib/flat_kit/stats.rb +12 -14
  56. data/lib/flat_kit/writer.rb +5 -6
  57. data/lib/flat_kit/xsv/format.rb +6 -2
  58. data/lib/flat_kit/xsv/reader.rb +8 -6
  59. data/lib/flat_kit/xsv/record.rb +21 -15
  60. data/lib/flat_kit/xsv/writer.rb +13 -10
  61. data/lib/flat_kit/xsv.rb +7 -4
  62. data/lib/flat_kit.rb +31 -26
  63. metadata +20 -158
  64. data/Rakefile +0 -21
  65. data/examples/stream-active-record-to-csv.rb +0 -42
  66. data/tasks/default.rake +0 -242
  67. data/tasks/extension.rake +0 -38
  68. data/tasks/man.rake +0 -7
  69. data/tasks/this.rb +0 -208
  70. data/test/device_dataset.rb +0 -117
  71. data/test/field_type/test_boolean_type.rb +0 -65
  72. data/test/field_type/test_date_type.rb +0 -71
  73. data/test/field_type/test_float_type.rb +0 -56
  74. data/test/field_type/test_guess_type.rb +0 -14
  75. data/test/field_type/test_integer_type.rb +0 -52
  76. data/test/field_type/test_null_type.rb +0 -41
  77. data/test/field_type/test_string_type.rb +0 -18
  78. data/test/field_type/test_timestamp_type.rb +0 -108
  79. data/test/field_type/test_unknown_type.rb +0 -35
  80. data/test/input/test_file.rb +0 -73
  81. data/test/input/test_io.rb +0 -93
  82. data/test/jsonl/test_format.rb +0 -22
  83. data/test/jsonl/test_reader.rb +0 -49
  84. data/test/jsonl/test_record.rb +0 -61
  85. data/test/jsonl/test_writer.rb +0 -86
  86. data/test/output/test_file.rb +0 -60
  87. data/test/output/test_io.rb +0 -104
  88. data/test/run +0 -23
  89. data/test/stat_type/test_nominal_stats.rb +0 -69
  90. data/test/stat_type/test_numerical_stats.rb +0 -118
  91. data/test/stat_type/test_ordinal_stats.rb +0 -92
  92. data/test/test_conversions.rb +0 -45
  93. data/test/test_event_emitter.rb +0 -89
  94. data/test/test_field_stats.rb +0 -134
  95. data/test/test_field_type.rb +0 -34
  96. data/test/test_format.rb +0 -24
  97. data/test/test_helper.rb +0 -26
  98. data/test/test_merge.rb +0 -40
  99. data/test/test_merge_tree.rb +0 -64
  100. data/test/test_version.rb +0 -11
  101. data/test/xsv/test_format.rb +0 -22
  102. data/test/xsv/test_reader.rb +0 -61
  103. data/test/xsv/test_record.rb +0 -69
  104. data/test/xsv/test_writer.rb +0 -89
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class Command
5
+ # Internal: The implementation of the stats command.
6
+ #
3
7
  class Stats < ::FlatKit::Command
4
-
5
8
  def self.name
6
9
  "stats"
7
10
  end
@@ -12,45 +15,45 @@ module FlatKit
12
15
 
13
16
  def self.parser
14
17
  ::Optimist::Parser.new do
15
- banner "#{Sort.description}"
18
+ banner Sort.description.to_s
16
19
  banner ""
17
20
 
18
21
  banner <<~BANNER
19
- Given an input file collect basic statistics.
22
+ Given an input file collect basic statistics.
20
23
 
21
- The statistics can vary based upon the datatype of the field.
24
+ The statistics can vary based upon the datatype of the field.
22
25
 
23
- Numeric fields will report the basic count, min, max, mean, standard deviation and sum.
24
- Non-numeric fields that are comparable, like dates, will report count, min and max.
25
- Other non-numeric fields will only report the count.
26
+ Numeric fields will report the basic count, min, max, mean, standard deviation and sum.
27
+ Non-numeric fields that are comparable, like dates, will report count, min and max.
28
+ Other non-numeric fields will only report the count.
26
29
 
27
- Adding --cardinality will report the count, and frequency of distinct values in the result.
28
- This will allow for reporting the median value.
30
+ Adding --cardinality will report the count, and frequency of distinct values in the result.
31
+ This will allow for reporting the median value.
29
32
 
30
- The fields upon which stats are collected may be selected with the --fields parameter.
31
- By default statistics are collected on all fields.
33
+ The fields upon which stats are collected may be selected with the --fields parameter.
34
+ By default statistics are collected on all fields.
32
35
 
33
- The flatfile type(s) will be automatically determined by the file name.
36
+ The flatfile type(s) will be automatically determined by the file name.
34
37
 
35
- The output can be dumped as a CSV, JSON or a a formated ascii table.
38
+ The output can be dumped as a CSV, JSON or a a formated ascii table.
36
39
 
37
40
  BANNER
38
41
 
39
42
  banner <<~USAGE
40
43
 
41
- Usage:
42
- fk stats --everything file.json
43
- fk stats --select surname,given_name file.csv
44
- fk stats --select surname,given_name --output-format json file.csv > stats.json
45
- fk stats --select field1,field2 --output-format json input.csv
46
- fk stats --select field1 file.json.gz -o stats.csv
47
- gunzip -c file.json.gz | fk stats --input-format json --output-format text
44
+ Usage:
45
+ fk stats --everything file.json
46
+ fk stats --select surname,given_name file.csv
47
+ fk stats --select surname,given_name --output-format json file.csv > stats.json
48
+ fk stats --select field1,field2 --output-format json input.csv
49
+ fk stats --select field1 file.json.gz -o stats.csv
50
+ gunzip -c file.json.gz | fk stats --input-format json --output-format text
48
51
 
49
52
  USAGE
50
53
 
51
54
  banner <<~OPTIONS
52
55
 
53
- Options:
56
+ Options:
54
57
 
55
58
  OPTIONS
56
59
 
@@ -65,24 +68,23 @@ module FlatKit
65
68
 
66
69
  def parse
67
70
  parser = self.class.parser
68
- ::Optimist::with_standard_exception_handling(parser) do
69
- begin
70
- opts = parser.parse(argv)
71
- fields = ::FlatKit::Stats::AllFields
72
- fields = CSV.parse_line(opts[:select]) if opts[:select]
73
-
74
- stats = [FieldStats::CORE_STATS]
75
- stats << FieldStats::CARDINALITY_STATS if opts[:cardinality] || opts[:everything]
76
-
77
- paths = parser.leftovers
78
- raise ::Optimist::CommandlineError, "1 and only 1 input file is allowed" if paths.size > 1
79
- path = paths.first || "-" # default to stdin
80
- @stats = ::FlatKit::Stats.new(input: path, input_fallback: opts[:input_format],
81
- output: opts[:output], output_fallback: opts[:output_format],
82
- fields_to_stat: fields, stats_to_collect: stats)
83
- rescue ::FlatKit::Error => e
84
- raise ::Optimist::CommandlineError, e.message
85
- end
71
+ ::Optimist.with_standard_exception_handling(parser) do
72
+ opts = parser.parse(argv)
73
+ fields = ::FlatKit::Stats::AllFields
74
+ fields = CSV.parse_line(opts[:select]) if opts[:select]
75
+
76
+ stats = [FieldStats::CORE_STATS]
77
+ stats << FieldStats::CARDINALITY_STATS if opts[:cardinality] || opts[:everything]
78
+
79
+ paths = parser.leftovers
80
+ raise ::Optimist::CommandlineError, "1 and only 1 input file is allowed" if paths.size > 1
81
+
82
+ path = paths.first || "-" # default to stdin
83
+ @stats = ::FlatKit::Stats.new(input: path, input_fallback: opts[:input_format],
84
+ output: opts[:output], output_fallback: opts[:output_format],
85
+ fields_to_stat: fields, stats_to_collect: stats)
86
+ rescue ::FlatKit::Error => e
87
+ raise ::Optimist::CommandlineError, e.message
86
88
  end
87
89
  end
88
90
 
@@ -1,13 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
4
+ # Internal: The base class for all commands in the CLI
5
+ #
2
6
  class Command
3
7
  extend DescendantTracker
4
8
 
5
- attr_reader :argv
6
- attr_reader :env
7
- attr_reader :logger
8
- attr_reader :opts
9
- attr_reader :readers
10
- attr_reader :writer
9
+ attr_reader :argv, :env, :logger, :opts, :readers, :writer
11
10
 
12
11
  def self.name
13
12
  raise NotImplementedError, "#{self.class} must implement #{self.class}.name"
@@ -22,7 +21,7 @@ module FlatKit
22
21
  end
23
22
 
24
23
  def self.names
25
- children.map { |c| c.name }
24
+ children.map(&:name)
26
25
  end
27
26
 
28
27
  def self.for(name)
@@ -48,7 +47,7 @@ module FlatKit
48
47
  end
49
48
  end
50
49
 
51
- require 'flat_kit/command/cat'
52
- require 'flat_kit/command/merge'
53
- require 'flat_kit/command/sort'
54
- require 'flat_kit/command/stats'
50
+ require "flat_kit/command/cat"
51
+ require "flat_kit/command/merge"
52
+ require "flat_kit/command/sort"
53
+ require "flat_kit/command/stats"
@@ -1,17 +1,20 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  module FlatKit
6
+ # Internal: A module to track descendants of a class
7
+ #
4
8
  module DescendantTracker
5
9
  def inherited(klass)
6
10
  super
7
11
  return unless klass.instance_of?(Class)
8
- self.children << klass
12
+
13
+ children << klass
9
14
  end
10
15
 
11
16
  def children
12
- unless defined? @_children
13
- @_children = Set.new
14
- end
17
+ @_children = Set.new unless defined? @_children
15
18
  @_children
16
19
  end
17
20
 
@@ -24,7 +27,7 @@ module FlatKit
24
27
  end
25
28
  end
26
29
 
27
- #
30
+ #
28
31
  # Find all the children that return truthy from the given method with args
29
32
  #
30
33
  def find_children(method, *args)
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
4
+ # Internal: A Base error class for all FlatKit errors
5
+ #
2
6
  class Error < ::StandardError
3
7
  class UnknownFormat < ::FlatKit::Error; end
4
8
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
- # A simplified Observable class for use internally
4
+ # Internal: A simplified Observable class for use internally
3
5
  #
4
6
  module EventEmitter
5
7
  def add_listener(listener)
6
8
  raise ::NoMethodError, "#{listener} does not resond to #on_event" unless listener.respond_to?(:on_event)
9
+
7
10
  self._listeners ||= []
8
11
  self._listeners << listener unless _listeners.include?(listener)
9
12
  end
@@ -27,7 +30,7 @@ module FlatKit
27
30
  end
28
31
 
29
32
  def _listeners
30
- @_listeners ||= Array.new
33
+ @_listeners ||= []
31
34
  end
32
35
  end
33
36
  end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
- # Collect stats on a single field. We may not know what the field data type is
3
- # to start with, so collect a bunch of values until we have the threshold, and
4
- # then calculte states based upon the data types determined by the guess
4
+ # Internal: Collect stats on a single field.
5
+ #
6
+ # We may not know what the field data type is to start with, so collect a
7
+ # bunch of values until we have the threshold, and then calculte states based
8
+ # upon the data types determined by the guess
5
9
  #
6
10
  class FieldStats
7
11
  DEFAULT_GUESS_THRESHOLD = 1000
@@ -9,7 +13,7 @@ module FlatKit
9
13
  CORE_STATS = :core
10
14
  CARDINALITY_STATS = :cardinality
11
15
 
12
- ALL_STATS = [ CORE_STATS, CARDINALITY_STATS ]
16
+ ALL_STATS = [CORE_STATS, CARDINALITY_STATS].freeze
13
17
 
14
18
  EXPORT_FIELDS = %w[
15
19
  name
@@ -36,14 +40,13 @@ module FlatKit
36
40
  total_count
37
41
  null_percent
38
42
  unknown_percent
39
- ]
40
-
43
+ ].freeze
41
44
 
42
- attr_reader :type_counts
43
- attr_reader :field_type
44
- attr_reader :name
45
+ attr_reader :type_counts, :field_type, :name, :out_of_type_count
45
46
 
46
- def initialize(name:, stats_to_collect: CORE_STATS, type: ::FlatKit::FieldType::GuessType, guess_threshold: DEFAULT_GUESS_THRESHOLD)
47
+ def initialize(name:, stats_to_collect: CORE_STATS,
48
+ type: ::FlatKit::FieldType::GuessType,
49
+ guess_threshold: DEFAULT_GUESS_THRESHOLD)
47
50
  @name = name
48
51
  @field_type = type
49
52
  @guess_threshold = guess_threshold
@@ -56,9 +59,14 @@ module FlatKit
56
59
 
57
60
  @stats_to_collect.each do |collection_set|
58
61
  next if ALL_STATS.include?(collection_set)
59
- raise ArgumentError, "#{collection_set} is not a valid stats collection set, must be one of #{ALL_STATS.map { |s| s.to_s }.join(", ") }"
62
+
63
+ valid_sets = ALL_STATS.map(&:to_s).join(", ")
64
+
65
+ raise ArgumentError, "#{collection_set} is not a valid stats collection set, must be one of #{valid_sets}"
60
66
  end
61
- raise ArgumentError, "type: must be FieldType subclasses - not #{type}" unless type.kind_of?(Class) && (type.superclass == ::FlatKit::FieldType)
67
+ return if type.is_a?(Class) && (type.superclass == ::FlatKit::FieldType)
68
+
69
+ raise ArgumentError, "type: must be FieldType subclasses - not #{type}"
62
70
  end
63
71
 
64
72
  def field_type_determined?
@@ -68,14 +76,12 @@ module FlatKit
68
76
  def update(value)
69
77
  update_type_count(value)
70
78
 
71
- if field_type_determined? then
79
+ if field_type_determined?
72
80
  update_stats(value)
73
81
  else
74
82
  @values << value
75
83
 
76
- if @values.size >= @guess_threshold then
77
- resolve_guess
78
- end
84
+ resolve_guess if @values.size >= @guess_threshold
79
85
  end
80
86
  end
81
87
 
@@ -167,12 +173,9 @@ module FlatKit
167
173
  stats.count + @out_of_type_count
168
174
  end
169
175
 
170
- def out_of_type_count
171
- @out_of_type_count
172
- end
173
-
174
176
  def null_percent
175
177
  return 0 if total_count.zero?
178
+
176
179
  ((null_count.to_f / total_count) * 100.0).truncate(2)
177
180
  end
178
181
 
@@ -182,15 +185,16 @@ module FlatKit
182
185
 
183
186
  def unknown_percent
184
187
  return 0 if total_count.zero?
188
+
185
189
  ((unknown_count.to_f / total_count) * 100.0).truncate(2)
186
190
  end
187
191
 
188
192
  def to_hash
189
193
  resolve_guess
190
194
 
191
- Hash.new.tap do |h|
195
+ {}.tap do |h|
192
196
  EXPORT_FIELDS.each do |n|
193
- h[n] = self.send(n)
197
+ h[n] = send(n)
194
198
  end
195
199
  end
196
200
  end
@@ -209,7 +213,7 @@ module FlatKit
209
213
 
210
214
  def update_stats(value)
211
215
  coerced_value = @field_type.coerce(value)
212
- if coerced_value == FieldType::CoerceFailure then
216
+ if coerced_value == FieldType::CoerceFailure
213
217
  @out_of_type_count += 1
214
218
  return
215
219
  end
@@ -221,15 +225,16 @@ module FlatKit
221
225
  def update_type_count(value)
222
226
  guess = FieldType.best_guess(value)
223
227
  type_counts[guess] += 1
224
- return guess
228
+ guess
225
229
  end
226
230
 
227
231
  def resolve_guess
228
232
  return if field_type_determined?
229
- best_guess_type, _best_guess_count = type_counts.max_by { |k, v| v }
233
+
234
+ best_guess_type, _best_guess_count = type_counts.max_by { |_k, v| v }
230
235
  @field_type = best_guess_type
231
236
  @stats = StatType.for(@field_type).new(collecting_frequencies: collecting_frequencies?)
232
- if @field_type == ::FlatKit::FieldType::StringType then
237
+ if @field_type == ::FlatKit::FieldType::StringType
233
238
  @length_stats = ::FlatKit::StatType::NumericalStats.new(collecting_frequencies: collecting_frequencies?)
234
239
  end
235
240
  @values.each do |v|
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
5
+ # Internal: Implemenation of the boolean type and coercion to the type
6
+ #
3
7
  class BooleanType < FieldType
4
-
5
8
  TRUTHY_REGEX = /\A(true|t|1|yes|y|on)\Z/i
6
9
  FALSEY_REGEX = /\A(false|f|0|no|n|off)\Z/i
7
10
  REGEX = Regexp.union(TRUTHY_REGEX, FALSEY_REGEX)
@@ -12,16 +15,15 @@ module FlatKit
12
15
 
13
16
  def self.matches?(data)
14
17
  case data
15
- when TrueClass
16
- true
17
- when FalseClass
18
+ when TrueClass, FalseClass
18
19
  true
19
20
  when String
20
21
  REGEX.match?(data)
21
22
  when Integer
22
23
  return true if data.zero?
23
24
  return true if data == 1
24
- return false
25
+
26
+ false
25
27
  else
26
28
  false
27
29
  end
@@ -36,10 +38,12 @@ module FlatKit
36
38
  when Numeric
37
39
  return false if data.zero?
38
40
  return true if data == 1
41
+
39
42
  CoerceFailure
40
43
  when String
41
44
  return true if TRUTHY_REGEX.match?(data)
42
45
  return false if FALSEY_REGEX.match?(data)
46
+
43
47
  CoerceFailure
44
48
  end
45
49
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
3
- # Representing the type of data which only includes data up to the day
4
- # resolution
5
+ # Internal: Representing the type of data which only includes data up to
6
+ # the day resolution
7
+ #
5
8
  class DateType < FieldType
6
-
7
9
  # %Y 4 digit year
8
10
  # %y 2 didigt year (%Y mod 100) (00..99)
9
11
  # %m month of year zero padded
@@ -49,9 +51,8 @@ module FlatKit
49
51
 
50
52
  # other formats
51
53
  "%Y-%j",
52
- "%a %b %d %Y"
53
- ]
54
-
54
+ "%a %b %d %Y",
55
+ ].freeze
55
56
  end
56
57
 
57
58
  # https://en.wikipedia.org/wiki/Date_format_by_country
@@ -146,7 +147,7 @@ module FlatKit
146
147
 
147
148
  def self.matches?(data)
148
149
  coerced = coerce(data)
149
- return coerced.kind_of?(Date)
150
+ coerced.is_a?(Date)
150
151
  end
151
152
 
152
153
  def self.coerce(data)
@@ -156,20 +157,21 @@ module FlatKit
156
157
  when Date
157
158
  data
158
159
  when String
159
- coerced_data = CoerceFailure
160
- parse_formats.each do |format|
161
- begin
162
- coerced_data = Date.strptime(data, format)
163
- break
164
- rescue => _
165
- false
166
- end
167
- end
168
- coerced_data
160
+ try_parse(data)
169
161
  else
170
162
  CoerceFailure
171
163
  end
172
164
  end
165
+
166
+ def self.try_parse(data)
167
+ parse_formats.each do |format|
168
+ coerced_data = Date.strptime(data, format)
169
+ return coerced_data
170
+ rescue StandardError => _e
171
+ false
172
+ end
173
+ CoerceFailure
174
+ end
173
175
  end
174
176
  end
175
177
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
5
+ # Internal: Represeting floating point data and conversion to it
6
+ #
3
7
  class FloatType < FieldType
4
-
5
8
  def self.type_name
6
9
  "float"
7
10
  end
@@ -14,12 +17,8 @@ module FlatKit
14
17
  false
15
18
  when String
16
19
  return false if IntegerType.matches?(data)
17
- begin
18
- Float(data)
19
- true
20
- rescue ArgumentError => _
21
- false
22
- end
20
+
21
+ maybe_float?(data)
23
22
  else
24
23
  false
25
24
  end
@@ -27,11 +26,18 @@ module FlatKit
27
26
 
28
27
  def self.coerce(data)
29
28
  Float(data)
30
- rescue TypeError => _
29
+ rescue TypeError => _e
31
30
  CoerceFailure
32
- rescue ArgumentError => _
31
+ rescue ArgumentError => _e
33
32
  CoerceFailure
34
33
  end
34
+
35
+ def self.maybe_float?(data)
36
+ Float(data)
37
+ true
38
+ rescue ArgumentError => _e
39
+ false
40
+ end
35
41
  end
36
42
  end
37
43
  end
@@ -1,18 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
3
- # GuessType is a field type where we don't know what type the field is, and
4
- # it needs to be guessed. This is a sentinel type that doesn't match any
5
- # data.
5
+ # Internal: GuessType is a field type where we don't know what type the
6
+ # field is, and it needs to be guessed. This is a sentinel type that doesn't
7
+ # match any data.
8
+ #
6
9
  class GuessType < FieldType
7
10
  def self.type_name
8
- self.name
11
+ name
9
12
  end
10
13
 
11
- def self.matches?(data)
14
+ def self.matches?(*)
12
15
  false
13
16
  end
14
17
 
15
- def self.coerce(data)
18
+ def self.coerce(*)
16
19
  CoerceFailure
17
20
  end
18
21
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
5
+ # Internal: Class reprepseting the Integer type and coercian to it.
6
+ #
3
7
  class IntegerType < FieldType
4
-
5
8
  REGEX = /\A[-+]?\d+\Z/
6
9
 
7
10
  def self.type_name
@@ -23,12 +26,11 @@ module FlatKit
23
26
 
24
27
  def self.coerce(data)
25
28
  Integer(data)
26
- rescue TypeError => _
29
+ rescue TypeError => _e
27
30
  CoerceFailure
28
- rescue ArgumentError => _
31
+ rescue ArgumentError => _e
29
32
  CoerceFailure
30
33
  end
31
-
32
34
  end
33
35
  end
34
36
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
5
+ # Internal: Class reprepseting the null type and coercian to it.
6
+ #
3
7
  class NullType < FieldType
4
-
5
8
  REGEX = Regexp.union(/\A(null|nil)\Z/i, /\A\\N\Z/)
6
9
 
7
10
  def self.type_name
@@ -25,6 +28,7 @@ module FlatKit
25
28
  data
26
29
  when String
27
30
  return nil if REGEX.match?(data)
31
+
28
32
  CoerceFailure
29
33
  else
30
34
  CoerceFailure
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
3
- # StringType is essentially a fallback - hence its lower weight than other
4
- # types that might have string representations.
5
- class StringType< FieldType
6
-
5
+ # Internal: StringType is essentially a fallback - hence its lower weight
6
+ # than other types that might have string representations.
7
+ #
8
+ class StringType < FieldType
7
9
  def self.type_name
8
10
  "string"
9
11
  end
10
12
 
11
13
  def self.matches?(data)
12
- data.kind_of?(String)
14
+ data.is_a?(String)
13
15
  end
14
16
 
15
17
  def self.coerce(data)
16
18
  data.to_s
17
- rescue => _
19
+ rescue StandardError => _e
18
20
  CoerceFailure
19
21
  end
20
22
  end
@@ -1,15 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlatKit
2
4
  class FieldType
5
+ # Internal: Type for all tiemstamps types more granular than Date.
6
+ #
3
7
  class TimestampType < FieldType
4
-
5
8
  def self.parse_formats
6
- @timestamp_formats ||= [
9
+ @parse_formats ||= [
7
10
  "%Y-%m-%d %H:%M:%S.%NZ",
8
11
  "%Y-%m-%d %H:%M:%S.%N",
9
12
  "%Y-%m-%dT%H:%M:%S.%N%z", # w3cdtf
10
13
  "%Y-%m-%d %H:%M:%S",
11
14
  "%Y-%m-%dT%H:%M:%S%z",
12
- "%Y-%m-%dT%H:%M:%SZ",
15
+ "%Y-%m-%dT%H:%M:%SZ",
13
16
  "%Y%m%dT%H%M%S",
14
17
  "%a, %d %b %Y %H:%M:%S %z", # rfc2822, httpdate
15
18
  ].freeze
@@ -21,7 +24,7 @@ module FlatKit
21
24
 
22
25
  def self.matches?(data)
23
26
  coerced = coerce(data)
24
- return coerced.kind_of?(Time)
27
+ coerced.is_a?(Time)
25
28
  end
26
29
 
27
30
  def self.coerce(data)
@@ -30,12 +33,10 @@ module FlatKit
30
33
  data
31
34
  when String
32
35
  parse_formats.each do |format|
33
- begin
34
- coerced_data = Time.strptime(data, format).utc
35
- return coerced_data
36
- rescue => _
37
- # do nothing
38
- end
36
+ coerced_data = Time.strptime(data, format).utc
37
+ return coerced_data
38
+ rescue StandardError => _e
39
+ # do nothing
39
40
  end
40
41
  CoerceFailure
41
42
  else