Skip to content

Commit b223e94

Browse files
authored
Merge pull request #16 from Sage/from-hash-arg-error
Check the hash given to #from_hash is a hash
2 parents b460199 + 752417e commit b223e94

9 files changed

Lines changed: 128 additions & 79 deletions

File tree

.github/workflows/publish.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Publish Gem
2+
on:
3+
release:
4+
types:
5+
- "created"
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v2
13+
14+
- name: Publish to RubyGems
15+
run: |
16+
mkdir -p $HOME/.gem
17+
touch $HOME/.gem/credentials
18+
chmod 0600 $HOME/.gem/credentials
19+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
20+
gem build hash_kit.gemspec
21+
gem push hash_kit-*.gem
22+
env:
23+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"

.github/workflows/rspec.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: RSpec
2+
on:
3+
- push
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- uses: actions/checkout@v3
11+
12+
- name: Run tests
13+
run: |
14+
docker-compose up -d
15+
docker exec gem_test_runner bash -c "cd gem_src && bundle install && bundle exec rspec"
16+
17+
- name: Code Coverage
18+
uses: paambaati/codeclimate-action@v5.0.0
19+
env:
20+
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
21+
with:
22+
debug: true
23+
coverageLocations: |
24+
${{github.workspace}}/coverage/coverage.json:simplecov
25+
prefix: /gem_src
26+
verifyDownload: true

.travis.yml

Lines changed: 0 additions & 23 deletions
This file was deleted.

docker-compose.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
gem_test_runner:
3+
image: ruby:2.7.8-bullseye
4+
container_name: gem_test_runner
5+
command: bash -c "while true; do echo 'Container is running...'; sleep 2; done"
6+
volumes:
7+
- ./:/gem_src

hash_kit.gemspec

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
lib = File.expand_path('../lib', __FILE__)
24
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
35
require 'hash_kit/version'
@@ -13,14 +15,14 @@ Gem::Specification.new do |spec|
1315
spec.homepage = 'https://github.com/sage/hash_kit'
1416
spec.license = 'MIT'
1517

16-
spec.files = Dir.glob("{bin,lib}/**/**/**")
18+
spec.files = Dir.glob('{bin,lib}/**/**/**')
1719
spec.bindir = 'exe'
1820
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
1921
spec.require_paths = ['lib']
2022

21-
spec.add_development_dependency 'bundler', '~> 1.11'
23+
spec.add_development_dependency 'bundler'
2224
spec.add_development_dependency 'pry'
23-
spec.add_development_dependency 'rake', '~> 10.0'
25+
spec.add_development_dependency 'rake'
2426
spec.add_development_dependency 'rspec'
25-
spec.add_development_dependency 'simplecov'
27+
spec.add_development_dependency 'simplecov', '~> 0.21'
2628
end

lib/hash_kit/helper.rb

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
# frozen_string_literal: true
2+
13
module HashKit
4+
# Hash kit Helper class
25
class Helper
3-
4-
#This method is called to make a hash allow indifferent access (it will accept both strings & symbols for a valid key).
6+
# This method is called to make a hash allow indifferent access (it will
7+
# accept both strings & symbols for a valid key).
58
def indifferent!(hash)
6-
unless hash.is_a?(Hash)
7-
return
8-
end
9+
return unless hash.is_a?(Hash)
910

10-
#set the default proc to allow the key to be either string or symbol if a matching key is found.
11+
# Set the default proc to allow the key to be either string or symbol if
12+
# a matching key is found.
1113
hash.default_proc = proc do |h, k|
1214
if h.key?(k.to_s)
1315
h[k.to_s]
@@ -18,9 +20,9 @@ def indifferent!(hash)
1820
end
1921
end
2022

21-
#recursively process any child hashes
23+
# Recursively process any child hashes
2224
hash.each do |key,value|
23-
if hash[key] != nil
25+
unless hash[key].nil?
2426
if hash[key].is_a?(Hash)
2527
indifferent!(hash[key])
2628
elsif hash[key].is_a?(Array)
@@ -33,9 +35,7 @@ def indifferent!(hash)
3335
end
3436

3537
def indifferent_array!(array)
36-
unless array.is_a?(Array)
37-
return
38-
end
38+
return unless array.is_a?(Array)
3939

4040
array.each do |i|
4141
if i.is_a?(Hash)
@@ -46,14 +46,16 @@ def indifferent_array!(array)
4646
end
4747
end
4848

49-
#This method is called to convert all the keys of a hash into symbols to allow consistent usage of hashes within your Ruby application.
49+
# This method is called to convert all the keys of a hash into symbols to
50+
# allow consistent usage of hashes within your Ruby application.
5051
def symbolize(hash)
5152
{}.tap do |h|
5253
hash.each { |key, value| h[key.to_sym] = map_value_symbol(value) }
5354
end
5455
end
5556

56-
#This method is called to convert all the keys of a hash into strings to allow consistent usage of hashes within your Ruby application.
57+
# This method is called to convert all the keys of a hash into strings to
58+
# allow consistent usage of hashes within your Ruby application.
5759
def stringify(hash)
5860
{}.tap do |h|
5961
hash.each { |key, value| h[key.to_s] = map_value_string(value) }
@@ -73,18 +75,22 @@ def to_hash(obj)
7375
hash
7476
end
7577

76-
def from_hash(hash, klass, transforms = [])
78+
# Return an object of type klass from the values in the given hash
79+
#
80+
# @param [Hash] hash
81+
# @param [Class] klass
82+
# @param [Array] transforms
83+
# @return [Object]
84+
def from_hash(hash, klass, transforms = [])
7785
obj = klass.new
78-
if hash ==nil || hash == {}
79-
return obj
80-
end
86+
return obj if hash.nil? || hash.empty?
87+
raise ArgumentError, "#{hash.inspect} is not a hash" unless hash.is_a?(Hash)
88+
89+
hash.each do |k, v|
90+
next unless obj.respond_to?(k)
8191

82-
hash.each do |k,v|
83-
if !obj.respond_to?(k)
84-
next
85-
end
8692
transform = transforms.detect { |t| t.key.to_sym == k.to_sym }
87-
if transform != nil
93+
if !transform.nil?
8894
if v.is_a?(Hash)
8995
child = from_hash(v, transform.klass, transforms)
9096
obj.instance_variable_set("@#{k}", child)
@@ -98,7 +104,8 @@ def from_hash(hash, klass, transforms = [])
98104
obj.instance_variable_set("@#{k}", v)
99105
end
100106
end
101-
return obj
107+
108+
obj
102109
end
103110

104111
private

script/docker-compose.yml

Lines changed: 0 additions & 6 deletions
This file was deleted.

spec/hash_kit/helper_spec.rb

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
# frozen_string_literal: true
2+
13
require 'spec_helper'
24

35
RSpec.describe HashKit::Helper do
46
describe '#symbolize' do
5-
67
context 'when a single layer hash with string keys is specified' do
78
let(:hash) { { 'key1' => 'value1', 'key2' => 'value2' } }
89

@@ -64,7 +65,6 @@
6465
end
6566

6667
describe '#stringify' do
67-
6868
context 'when a single layer hash with symbol keys is specified' do
6969
let(:hash) { { :key1 => 'value1', :key2 => 'value2' } }
7070

@@ -122,11 +122,9 @@
122122
expect(new_hash['key3'][1].has_key?(:key4)).to be false
123123
end
124124
end
125-
126125
end
127126

128127
describe '#to_hash' do
129-
130128
let(:key1) do
131129
{ key1: 'value1' }
132130
end
@@ -195,7 +193,7 @@
195193
end
196194

197195
[
198-
"string" ,
196+
'string' ,
199197
101 ,
200198
1.01 ,
201199
Time.now ,
@@ -237,36 +235,35 @@
237235
end
238236

239237
describe '#from_hash' do
240-
241238
let(:child_hash) do
242239
{
243-
text: 'abc',
244-
numeric: 5,
245-
time: Time.now,
246-
invalid_key: 5,
247-
bool: true
240+
text: 'abc',
241+
numeric: 5,
242+
time: Time.now,
243+
invalid_key: 5,
244+
bool: true
248245
}
249246
end
250247

251248
let(:parent_hash) do
252249
{
253-
text: 'abc',
254-
numeric: 5,
255-
time: Time.now,
256-
entity: child_hash,
257-
entity_array: [child_hash, child_hash],
258-
invalid_key: 5,
259-
bool: true
250+
text: 'abc',
251+
numeric: 5,
252+
time: Time.now,
253+
entity: child_hash,
254+
entity_array: [child_hash, child_hash],
255+
invalid_key: 5,
256+
bool: true
260257
}
261258
end
262259

263260
let(:string_hash) do
264261
{
265-
'text' => 'abc',
266-
'numeric' => 5,
267-
'time' => Time.now,
268-
'invalid_key' => 5,
269-
'bool' => true
262+
'text' => 'abc',
263+
'numeric' => 5,
264+
'time' => Time.now,
265+
'invalid_key' => 5,
266+
'bool' => true
270267
}
271268
end
272269

@@ -337,6 +334,12 @@
337334
expect(obj.entity_array[1]).to be_a(TestEntity)
338335
end
339336
end
337+
338+
context 'when hash is not a hash' do
339+
it 'raises an ArgumentError' do
340+
expect { subject.from_hash('Not a hash', TestEntity, transforms) }.to raise_error(ArgumentError)
341+
end
342+
end
340343
end
341344

342345
describe '#indifferent' do
@@ -356,18 +359,22 @@
356359
}
357360
}
358361
end
362+
359363
it 'should allow access to a string key from a symbol' do
360364
subject.indifferent!(hash)
361365
expect(hash[:key1]).to eq 'value1'
362366
end
367+
363368
it 'should allow access to a symbol key from a string' do
364369
subject.indifferent!(hash)
365370
expect(hash['key2']).to eq 'value2'
366371
end
372+
367373
it 'should allow indifferent access to a key within an array' do
368374
subject.indifferent!(hash)
369375
expect(hash[:key3][0]['key4']).to eq 'value4'
370376
end
377+
371378
it 'should allow indifferent access to a key within an nested hash' do
372379
subject.indifferent!(hash)
373380
expect(hash[:key6]['key7']).to eq 'value7'

spec/spec_helper.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
# frozen_string_literal: true
2+
13
require 'simplecov'
4+
require 'simplecov_json_formatter'
5+
6+
SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
7+
28
SimpleCov.start do
39
add_filter 'spec/'
410
end

0 commit comments

Comments
 (0)