rordash 0.1.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 +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.gitignore +20 -0
- data/.rspec +4 -0
- data/.rubocop.yml +247 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/dev/setup.rb +26 -0
- data/lib/rordash/debug_util.rb +37 -0
- data/lib/rordash/file_util.rb +95 -0
- data/lib/rordash/hash_util.rb +181 -0
- data/lib/rordash/numeric_util.rb +28 -0
- data/lib/rordash/object_util.rb +18 -0
- data/lib/rordash/path_util.rb +11 -0
- data/lib/rordash/regex_util.rb +36 -0
- data/lib/rordash/url_util.rb +15 -0
- data/lib/rordash/version.rb +5 -0
- data/lib/rordash.rb +18 -0
- data/rordash.gemspec +42 -0
- data/sig/rordash.rbs +4 -0
- data/spec/coverage_helper.rb +14 -0
- data/spec/fixtures/files/sample.csv +3 -0
- data/spec/fixtures/files/sample.json +6 -0
- data/spec/rordash/debug_util_spec.rb +50 -0
- data/spec/rordash/file_util_spec.rb +123 -0
- data/spec/rordash/hash_util_spec.rb +182 -0
- data/spec/rordash/numeric_util_spec.rb +63 -0
- data/spec/rordash/object_util_spec.rb +14 -0
- data/spec/rordash/path_util_spec.rb +24 -0
- data/spec/rordash/regex_util_spec.rb +86 -0
- data/spec/rordash/url_util_spec.rb +25 -0
- data/spec/rordash_spec.rb +7 -0
- data/spec/spec_helper.rb +26 -0
- metadata +323 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
module FileUtil
|
5
|
+
FILE_EXTENSION_MATCH = /^.*\.\S+\w$/.freeze
|
6
|
+
CONTENT_TYPE_TO_EXT_MATCH = %r{/(.*)$}.freeze
|
7
|
+
UNSUPPORTED_CONTENT_TYPE_MAP = {
|
8
|
+
'application/vnd.ms-publisher' => 'pub'
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def filename_with_ext_from(filename:, content_type:)
|
13
|
+
filename = filename.to_s.strip
|
14
|
+
return filename if filename.blank?
|
15
|
+
return filename if filename_has_extension?(filename)
|
16
|
+
return filename if content_type.blank?
|
17
|
+
|
18
|
+
ext = content_type_to_extension(content_type)
|
19
|
+
return filename if ext.blank?
|
20
|
+
|
21
|
+
"#{filename}.#{ext}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def content_type_to_extension(content_type)
|
25
|
+
return content_type if content_type.blank?
|
26
|
+
|
27
|
+
ext = mime_type_for_content_type(content_type)&.preferred_extension
|
28
|
+
ext = UNSUPPORTED_CONTENT_TYPE_MAP[content_type] if ext.blank?
|
29
|
+
ext = content_type.match(CONTENT_TYPE_TO_EXT_MATCH).to_a.last if ext.blank?
|
30
|
+
ext
|
31
|
+
end
|
32
|
+
|
33
|
+
def fixture_file_path(filename)
|
34
|
+
rel_path = PathUtil.fixtures_path('files')
|
35
|
+
path = rel_path.join(filename)
|
36
|
+
|
37
|
+
if path.exist?
|
38
|
+
path
|
39
|
+
else
|
40
|
+
msg = "the directory '%s' does not contain a file named '%s'"
|
41
|
+
raise ArgumentError, format(msg, rel_path, filename)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def fixture_file_path_str(filename)
|
46
|
+
fixture_file_path(filename).to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_fixture_file(rel_path)
|
50
|
+
File.read(PathUtil.fixtures_path(rel_path))
|
51
|
+
end
|
52
|
+
|
53
|
+
def open_fixture_file(filename)
|
54
|
+
pathname = Utils::FileUtil.fixture_file_path(filename)
|
55
|
+
return nil unless pathname.exist?
|
56
|
+
|
57
|
+
::File.open(pathname.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_fixture_file_as_hash(rel_path)
|
61
|
+
HashUtil.from_string(read_fixture_file(rel_path))
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_file_blob(filename:, content_type: nil, metadata: nil)
|
65
|
+
content_type = content_type.present? ? content_type : content_type_from_filename(filename) || Mime[:jpg]
|
66
|
+
|
67
|
+
raise StandardError, "ActiveStorage must be installed" unless defined? ActiveStorage::Blob
|
68
|
+
|
69
|
+
ActiveStorage::Blob.create_after_upload! io: ::File.open(fixture_file_path(filename).to_s), filename: filename,
|
70
|
+
content_type: content_type, metadata: metadata
|
71
|
+
end
|
72
|
+
|
73
|
+
def file_url_for(filename)
|
74
|
+
Addressable::URI.parse(Faker::Internet.url)&.join(filename).to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def content_type_from_filename(filename)
|
78
|
+
mime_type = mime_type_from_filename(filename)
|
79
|
+
mime_type&.content_type
|
80
|
+
end
|
81
|
+
|
82
|
+
def mime_type_from_filename(filename)
|
83
|
+
MIME::Types.type_for(filename).first
|
84
|
+
end
|
85
|
+
|
86
|
+
def mime_type_for_content_type(content_type)
|
87
|
+
MIME::Types[content_type].first
|
88
|
+
end
|
89
|
+
|
90
|
+
def filename_has_extension?(filename)
|
91
|
+
filename.to_s.match?(FILE_EXTENSION_MATCH)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
# rubocop:disable Metrics/ModuleLength
|
5
|
+
module HashUtil
|
6
|
+
ROOT_PATHS = %w[. *].freeze
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def from_string(json_str)
|
10
|
+
return json_str unless json_str.is_a?(String)
|
11
|
+
|
12
|
+
Oj.load(json_str, symbol_keys: true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_str(obj)
|
16
|
+
return Oj.dump(obj.deep_stringify_keys) if hash_or_array?(obj)
|
17
|
+
|
18
|
+
obj.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def pretty(obj)
|
22
|
+
return obj unless hash_or_array?(obj)
|
23
|
+
|
24
|
+
JSON.pretty_generate(obj)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(obj, path, default: nil)
|
28
|
+
return obj if ROOT_PATHS.include?(path)
|
29
|
+
|
30
|
+
value = R_.get(obj, path.to_s)
|
31
|
+
return default if value.nil?
|
32
|
+
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_first_present(obj, dotted_paths)
|
37
|
+
dotted_paths.each do |path|
|
38
|
+
value = R_.get(obj, path)
|
39
|
+
return value if value.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def set(hash, path, value)
|
46
|
+
R_.set(hash, path.to_s, value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def group_by(obj, key_or_proc)
|
50
|
+
proc = if key_or_proc.is_a?(Proc)
|
51
|
+
key_or_proc
|
52
|
+
else
|
53
|
+
->(hash) { R_.get(hash, key_or_proc.to_s) }
|
54
|
+
end
|
55
|
+
|
56
|
+
R_.group_by(obj, proc)
|
57
|
+
end
|
58
|
+
|
59
|
+
def dot(hash, keep_arrays: true, &block)
|
60
|
+
return Dottie.flatten(hash) unless keep_arrays
|
61
|
+
|
62
|
+
results = {}
|
63
|
+
Dottie.flatten(hash, intermediate: true).each do |k, v|
|
64
|
+
next if RegexUtil.match?(:dotted_index, k)
|
65
|
+
next if v.is_a?(::Hash)
|
66
|
+
|
67
|
+
value = block ? yield(k, v) : v
|
68
|
+
results.merge!(k => value)
|
69
|
+
end
|
70
|
+
|
71
|
+
results
|
72
|
+
end
|
73
|
+
|
74
|
+
def deep_key?(obj, key)
|
75
|
+
return false unless obj.is_a?(::Hash)
|
76
|
+
return obj.key?(key) unless key.is_a?(String) && key.include?('.')
|
77
|
+
|
78
|
+
dotted_keys(obj).include?(key)
|
79
|
+
end
|
80
|
+
|
81
|
+
def dotted_keys(obj, keep_arrays: true)
|
82
|
+
dot(obj, keep_arrays: keep_arrays).keys
|
83
|
+
end
|
84
|
+
|
85
|
+
# rubocop:disable Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
|
86
|
+
def pick(hash, paths, keep_arrays: true)
|
87
|
+
all_paths = paths.is_a?(Array) ? paths.map(&:to_s) : [paths.to_s]
|
88
|
+
has_deep_paths = all_paths.any? { |path| path.include?('.') }
|
89
|
+
|
90
|
+
results = {}
|
91
|
+
dotted_hash = has_deep_paths ? Dottie.flatten(hash, intermediate: true) : hash
|
92
|
+
filtered_keys = dotted_hash.keys.select { |path| all_paths.include?(path.to_s) }
|
93
|
+
filtered_dotted_hash = dotted_hash.slice(*filtered_keys)
|
94
|
+
|
95
|
+
return filtered_dotted_hash unless has_deep_paths
|
96
|
+
|
97
|
+
if keep_arrays
|
98
|
+
filtered_dotted_hash.each_pair { |k, v| Utils::HashUtil.set(results, k, v) }
|
99
|
+
return results
|
100
|
+
else
|
101
|
+
filtered_dotted_hash.each_pair do |dotted_key, val|
|
102
|
+
stringify_dotted_key = dotted_key.to_s
|
103
|
+
next if all_paths.exclude?(stringify_dotted_key)
|
104
|
+
|
105
|
+
should_reconstruct_array = RegexUtil.match?(:dotted_index, stringify_dotted_key)
|
106
|
+
key = should_reconstruct_array ? dotted_key.split('[').first : dotted_key
|
107
|
+
value = should_reconstruct_array ? (results[key] || []).push(val) : val
|
108
|
+
|
109
|
+
results.merge!(key => value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
undot(results)
|
114
|
+
end
|
115
|
+
# rubocop:enable Metrics/MethodLength,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/AbcSize
|
116
|
+
|
117
|
+
def undot(hash, &block)
|
118
|
+
results = {}
|
119
|
+
Dottie.flatten(hash, { intermediate: false }).each do |k, v|
|
120
|
+
value = block ? yield(k, v) : v
|
121
|
+
set(results, k, value)
|
122
|
+
end
|
123
|
+
results
|
124
|
+
end
|
125
|
+
|
126
|
+
def deep_compact(attrs, each_value_proc: nil)
|
127
|
+
result = {}
|
128
|
+
|
129
|
+
dot(attrs, keep_arrays: true) do |k, v|
|
130
|
+
value = each_value_proc.respond_to?(:call) ? each_value_proc&.call(k, v) : v.compact
|
131
|
+
next if value.nil?
|
132
|
+
|
133
|
+
set(result, k, value)
|
134
|
+
end
|
135
|
+
|
136
|
+
result
|
137
|
+
end
|
138
|
+
|
139
|
+
def reject_blank_values(obj)
|
140
|
+
return obj unless hash_or_array?(obj)
|
141
|
+
|
142
|
+
obj.compact.reduce({}) do |memo, (k, v)|
|
143
|
+
v = v.is_a?(String) ? v.strip : v
|
144
|
+
next memo if !hash_or_array?(v) && v.blank?
|
145
|
+
|
146
|
+
memo.merge(k => v)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def deep_reject_blank_values(attrs)
|
151
|
+
deep_compact(attrs, each_value_proc: lambda do |_k, v|
|
152
|
+
v = v.is_a?(String) ? v.strip : v
|
153
|
+
v = v.compact if hash_or_array?(v)
|
154
|
+
if v.is_a?(Array)
|
155
|
+
v = v.reject do |val|
|
156
|
+
val = val.is_a?(String) ? val.strip : val
|
157
|
+
val.blank?
|
158
|
+
end
|
159
|
+
end
|
160
|
+
v.blank? ? nil : v
|
161
|
+
end)
|
162
|
+
end
|
163
|
+
|
164
|
+
def deep_symbolize_keys(obj)
|
165
|
+
return obj unless hash_or_array?(obj)
|
166
|
+
return obj.deep_symbolize_keys if obj.is_a?(::Hash)
|
167
|
+
|
168
|
+
obj.map { |item| item.is_a?(::Hash) ? item.deep_symbolize_keys : item }
|
169
|
+
end
|
170
|
+
|
171
|
+
def digest(obj)
|
172
|
+
Digest::SHA1.base64digest Marshal.dump(obj)
|
173
|
+
end
|
174
|
+
|
175
|
+
def hash_or_array?(obj)
|
176
|
+
obj.is_a?(::Hash) || obj.is_a?(Array)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
# rubocop:enable Metrics/ModuleLength
|
181
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
module NumericUtil
|
5
|
+
class << self
|
6
|
+
module Unit
|
7
|
+
FEET = :ft
|
8
|
+
METER = :m
|
9
|
+
end
|
10
|
+
|
11
|
+
def numeric?(value)
|
12
|
+
!Float(value.to_s).nil?
|
13
|
+
rescue StandardError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert_unit(value, from_unit:, to_unit:)
|
18
|
+
Measured::Length.new(value, from_unit).convert_to(to_unit).value.to_f
|
19
|
+
end
|
20
|
+
|
21
|
+
def convert_unit_sq(value, from_unit:, to_unit:)
|
22
|
+
value = value.is_a?(String) ? BigDecimal(value) : value
|
23
|
+
val = convert_unit(value, from_unit: from_unit, to_unit: to_unit)
|
24
|
+
(val * val) / value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
module ObjectUtil
|
5
|
+
class << self
|
6
|
+
def to_class(classname)
|
7
|
+
classname.constantize if classname.is_a?(String)
|
8
|
+
classname
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_classname(klass)
|
12
|
+
return klass.name if Object.const_defined?(klass)
|
13
|
+
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
module RegexUtil
|
5
|
+
EMAIL_MATCH = URI::MailTo::EMAIL_REGEXP
|
6
|
+
POSTAL_CODE_MATCH = /[A-Za-z]\d[A-Za-z][ -]?\d[A-Za-z]\d/.freeze
|
7
|
+
DOTTED_INDEX_MATCH = /\w+\[\d+\]/.freeze
|
8
|
+
URI_MATCH = URI::DEFAULT_PARSER.make_regexp.freeze
|
9
|
+
|
10
|
+
TYPE = {
|
11
|
+
email: EMAIL_MATCH,
|
12
|
+
uri: URI_MATCH,
|
13
|
+
postal_code: POSTAL_CODE_MATCH,
|
14
|
+
dotted_index: DOTTED_INDEX_MATCH
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def match?(type, value)
|
19
|
+
TYPE[type].match?(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def match(type, value)
|
23
|
+
TYPE[type].match(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def replace(type, value, replacement)
|
27
|
+
return value unless value.is_a?(String)
|
28
|
+
|
29
|
+
regex = TYPE[type]
|
30
|
+
return value if regex.nil?
|
31
|
+
|
32
|
+
value.sub(regex, replacement)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
module UrlUtil
|
5
|
+
class << self
|
6
|
+
def safe_escape(url)
|
7
|
+
Addressable::URI.escape(url).to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def error_from_http_status(http_status)
|
11
|
+
Rack::Utils::HTTP_STATUS_CODES[http_status.to_i] || 'Invalid HTTP Status Code'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/rordash.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
%w[
|
4
|
+
version
|
5
|
+
debug_util
|
6
|
+
regex_util
|
7
|
+
hash_util
|
8
|
+
path_util
|
9
|
+
file_util
|
10
|
+
url_util
|
11
|
+
object_util
|
12
|
+
numeric_util
|
13
|
+
].each do |filename|
|
14
|
+
require File.expand_path("../rordash/#{filename}", Pathname.new(__FILE__).realpath)
|
15
|
+
end
|
16
|
+
|
17
|
+
module Rordash
|
18
|
+
; end
|
data/rordash.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../lib/rordash/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Desmond O'Leary"]
|
7
|
+
gem.email = ["desoleary@gmail.com"]
|
8
|
+
gem.description = %q{Lodash inspired utilities}
|
9
|
+
gem.summary = %q{Lodash inspired utilities}
|
10
|
+
gem.homepage = "https://github.com/omnitech-solutions/rordash"
|
11
|
+
gem.license = "MIT"
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^exe/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(spec|features)/})
|
16
|
+
gem.name = "rordash"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Rordash::VERSION
|
19
|
+
gem.required_ruby_version = ">= 2.6.0"
|
20
|
+
|
21
|
+
gem.metadata["homepage_uri"] = gem.homepage
|
22
|
+
gem.metadata["source_code_uri"] = gem.homepage
|
23
|
+
gem.metadata["changelog_uri"] = "#{gem.homepage}/CHANGELOG.md"
|
24
|
+
|
25
|
+
gem.add_runtime_dependency 'stackprof', '>= 0.2'
|
26
|
+
gem.add_runtime_dependency 'colorize', '~> 0.8.1'
|
27
|
+
gem.add_runtime_dependency 'mime-types', '>= 3'
|
28
|
+
gem.add_runtime_dependency 'activesupport', '>= 5'
|
29
|
+
gem.add_runtime_dependency 'activestorage', '>= 5'
|
30
|
+
gem.add_runtime_dependency 'rack', '>= 2'
|
31
|
+
gem.add_runtime_dependency 'faker', '>= 2'
|
32
|
+
gem.add_runtime_dependency 'oj', '>= 3'
|
33
|
+
gem.add_runtime_dependency 'rudash', '>= 4'
|
34
|
+
gem.add_runtime_dependency 'dottie', '>= 0.0.2'
|
35
|
+
gem.add_runtime_dependency 'addressable', '>= 2.6.0'
|
36
|
+
gem.add_runtime_dependency 'measured', '>= 2.5'
|
37
|
+
|
38
|
+
gem.add_development_dependency("rake", "~> 13.0.6")
|
39
|
+
gem.add_development_dependency("rspec", "~> 3.12.0")
|
40
|
+
gem.add_development_dependency("simplecov", "~> 0.21.2")
|
41
|
+
gem.add_development_dependency("codecov", "~> 0.6.0")
|
42
|
+
end
|
data/sig/rordash.rbs
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if ENV['RUN_COVERAGE_REPORT']
|
4
|
+
require 'simplecov'
|
5
|
+
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter 'vendor/'
|
8
|
+
add_filter %r{^/spec/}
|
9
|
+
end
|
10
|
+
SimpleCov.minimum_coverage_by_file 90
|
11
|
+
|
12
|
+
require 'codecov'
|
13
|
+
SimpleCov.formatter = SimpleCov::Formatter::Codecov
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
# rubocop:disable RSpec/MessageSpies
|
5
|
+
RSpec.describe DebugUtil do
|
6
|
+
describe '.calculate_duration' do
|
7
|
+
let(:tag) { nil }
|
8
|
+
|
9
|
+
subject(:calculated_duration) { described_class.calculate_duration(tag: tag) { 'do-something' } }
|
10
|
+
|
11
|
+
context 'without block' do
|
12
|
+
it 'raises missing block error' do
|
13
|
+
expect { described_class.calculate_duration }.to raise_error ArgumentError, 'Missing block'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with tag' do
|
18
|
+
let(:tag) { 'some-tag' }
|
19
|
+
|
20
|
+
it 'includes tag' do
|
21
|
+
expect { calculated_duration }.to output(Regexp.new(/tag: `#{tag}` - total duration - /)).to_stdout
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'prints duration' do
|
26
|
+
expect { calculated_duration }.to output(/tag: `default` - total duration - /).to_stdout
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.wrap_stack_prof' do
|
31
|
+
let(:tag) { nil }
|
32
|
+
let(:out) { nil }
|
33
|
+
|
34
|
+
subject(:profile_wrapper) { described_class.wrap_stack_prof(tag: tag, out: out) { 'do something' } }
|
35
|
+
|
36
|
+
it 'wraps block inside stackprof runner' do
|
37
|
+
expect(StackProf).to receive(:run).with(mode: :wall, out: 'tmp/stackprof.dump', raw: true, interval: 1000)
|
38
|
+
|
39
|
+
profile_wrapper
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'prints output file location' do
|
43
|
+
expect(StackProf).to receive(:run).with(mode: :wall, out: 'tmp/stackprof.dump', raw: true, interval: 1000)
|
44
|
+
|
45
|
+
expect { profile_wrapper }.to output(Regexp.new(%r{StackProf output file: tmp/stackprof.dump})).to_stdout
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable RSpec/MessageSpies
|
50
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe FileUtil do
|
5
|
+
let(:filename) { 'sample.csv' }
|
6
|
+
let(:rel_path) { "files/#{filename}" }
|
7
|
+
|
8
|
+
describe '.fixture_file_path' do
|
9
|
+
it 'returns file contents' do
|
10
|
+
actual = described_class.fixture_file_path(filename)
|
11
|
+
|
12
|
+
expect(actual.exist?).to be_truthy
|
13
|
+
expect(actual.to_s).to include("spec/fixtures/files/#{filename}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#fixture_file_path_str' do
|
18
|
+
it 'returns file contents' do
|
19
|
+
expect(described_class.fixture_file_path_str(filename)).to include("spec/fixtures/files/#{filename}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#read_fixture_file' do
|
24
|
+
it 'returns file contents' do
|
25
|
+
expect(described_class.read_fixture_file(rel_path)).to include("Gemma Jones,Sept 1 2018,silver")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#read_fixture_file_as_hash' do
|
30
|
+
let(:rel_path) { 'files/sample.json' }
|
31
|
+
|
32
|
+
it 'returns file contents' do
|
33
|
+
expect(described_class.read_fixture_file_as_hash(rel_path)).to eql([
|
34
|
+
{
|
35
|
+
color: "red",
|
36
|
+
value: "#f00"
|
37
|
+
}
|
38
|
+
])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.content_type_from_filename' do
|
43
|
+
let(:filename) { 'sample.pdf' }
|
44
|
+
|
45
|
+
subject(:content_type) { described_class.content_type_from_filename(filename) }
|
46
|
+
|
47
|
+
it 'returns content type' do
|
48
|
+
expect(content_type).to eql('application/pdf')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.mime_type_from_filename' do
|
53
|
+
let(:filename) { 'sample.pdf' }
|
54
|
+
|
55
|
+
subject(:mime_type) { described_class.mime_type_from_filename(filename) }
|
56
|
+
|
57
|
+
it 'returns content type' do
|
58
|
+
expect(mime_type).to eql(MIME::Types['application/pdf'].first)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.file_url_for' do
|
63
|
+
let(:filename) { 'sample.pdf' }
|
64
|
+
|
65
|
+
subject(:file_url) { described_class.file_url_for(filename) }
|
66
|
+
|
67
|
+
it 'returns url with filename' do
|
68
|
+
expect(file_url).to match(RegexUtil::TYPE[:uri])
|
69
|
+
expect(file_url).to include(filename)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with content types' do
|
74
|
+
let(:content_types) do
|
75
|
+
[
|
76
|
+
{ content_type: 'image/png', extension: 'png' },
|
77
|
+
{ content_type: 'text/csv', extension: 'csv' },
|
78
|
+
{ content_type: 'application/msword', extension: 'doc' },
|
79
|
+
{ content_type: 'text/html', extension: 'html' },
|
80
|
+
{ content_type: 'application/zip', extension: 'zip' },
|
81
|
+
{ content_type: 'image/bmp', extension: 'bmp' },
|
82
|
+
{ content_type: 'application/vnd.ms-publisher', extension: 'pub' },
|
83
|
+
{ content_type: 'image/jp2', extension: 'jp2' },
|
84
|
+
{ content_type: 'video/mp4', extension: 'mp4' },
|
85
|
+
{ content_type: 'application/pdf', extension: 'pdf' },
|
86
|
+
{ content_type: 'image/svg+xml', extension: 'svg' },
|
87
|
+
{ content_type: 'application/octet-stream', extension: 'bin' },
|
88
|
+
{ content_type: 'image/webp', extension: 'webp' },
|
89
|
+
{ content_type: 'image/gif', extension: 'gif' },
|
90
|
+
{ content_type: 'text/plain', extension: 'txt' },
|
91
|
+
{ content_type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
92
|
+
extension: 'pptx' },
|
93
|
+
{ content_type: 'image/heic', extension: 'heic' },
|
94
|
+
{ content_type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
95
|
+
extension: 'docx' },
|
96
|
+
{ content_type: 'image/jpeg', extension: 'jpeg' },
|
97
|
+
{ content_type: 'image/tiff', extension: 'tiff' }
|
98
|
+
]
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '.filename_with_ext_from' do
|
102
|
+
let(:filename_without_extension) { 'filename' }
|
103
|
+
|
104
|
+
it 'returns the correct extension' do
|
105
|
+
content_types.each do |content_type:, extension:|
|
106
|
+
actual = described_class.filename_with_ext_from(filename: filename_without_extension,
|
107
|
+
content_type: content_type)
|
108
|
+
expect(actual).to eql("#{filename_without_extension}.#{extension}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '.content_type_to_extension' do
|
114
|
+
it 'returns the correct extension' do
|
115
|
+
content_types.each do |content_type:, extension:|
|
116
|
+
actual = described_class.content_type_to_extension(content_type)
|
117
|
+
expect(actual).to eql(extension)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|