tonsser_hash_utils 1.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21216e909e28b7232940d8f26b5cf3f6d446ad60
4
- data.tar.gz: d1abc94b1c4648912a2cd6c7a454b7fbf121a8df
3
+ metadata.gz: a07a24d08bf8a7ce458efb446458126f97961994
4
+ data.tar.gz: 169432513c2a5ba66f8435897922032daec6d267
5
5
  SHA512:
6
- metadata.gz: 755fe94450976b16c5d2dd99f3191e6929ef6d7f0b734132fa0876afcb78e5a5f8a6de5b94da3dafb9d0dd85fa4586bb44a9eeb66266932f1e543e32b7ba09b1
7
- data.tar.gz: 68a2949f62b74399490ee90880c55212bd4c758116bc7c83b416d0dad52ed00c37d9dfc3b32b78e19fac007d6225457803e4d4e78356a08d2678fcbe7b6d449b
6
+ metadata.gz: 4ffaabc26f0bc621f20c0f0b64c544537f970f6d5563329189e2420024e168b63d1a78323866723469a38228709bb717d6eba966a057a461f7b5a24334cfc2b6
7
+ data.tar.gz: 8f75d7cf18ef1710a901419e9174ec2d13b081fdc43b2587196616942befa0aff126140924cd5c01571614135c7709c0e359d07c901e0933dd81777f37233da2
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in tonsser_hash_utils.gemspec
3
+ # Specify your gem"s dependencies in tonsser_hash_utils.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,26 +1,123 @@
1
- # TonsserHashUtils
1
+ # Tonsser Hash Utils
2
2
 
3
- TODO: Write a gem description
3
+ A collection of classes for dealing with hashes. We've found them to be useful when manipulating a lot of JSON.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'tonsser_hash_utils'
10
+ gem "tonsser_hash_utils", "~> 1.0"
11
11
  ```
12
12
 
13
13
  And then execute:
14
14
 
15
- $ bundle
15
+ ```
16
+ bundle install
17
+ ```
16
18
 
17
19
  Or install it yourself as:
18
20
 
19
- $ gem install tonsser_hash_utils
21
+ ```
22
+ gem install tonsser_hash_utils
23
+ ```
20
24
 
21
25
  ## Usage
22
26
 
23
- TODO: Write usage instructions here
27
+ The gem contains the following classes
28
+
29
+ - `HashWithQuickAccess`
30
+ - `DeepHash`
31
+ - `HashMerger`
32
+ - `HashBuilder`
33
+
34
+ ### HashWithQuickAccess
35
+
36
+ `HashWithQuickAccess` makes it easy to access lots of properties within deeply nested hashes. Useful when dealing with hashes that are serialized from JSON.
37
+
38
+ Example:
39
+
40
+ ```ruby
41
+ # This
42
+ json[:foo][:bar][:baz][:qux]
43
+
44
+ # Becomes this
45
+ hash = HashWithQuickAccess.new(json)
46
+ hash.foo.bar.baz.qux
47
+ ```
48
+
49
+ It also supports arrays within the hashes, so you can do stuff like
50
+
51
+ ```ruby
52
+ hash = HashWithQuickAccess.new(json)
53
+ hash.foo.bar[4].baz.qux
54
+ ```
55
+
56
+ ### DeepHash
57
+
58
+ `DeepHash` is useful for checking if some deeply nested value is present.
59
+
60
+ Example:
61
+
62
+ ```ruby
63
+ # This
64
+ params[:foo] && params[:foo][:bar] && params[:foo][:bar][:baz]
65
+
66
+ # Becomes this
67
+ DeepHash.new(params).dig(:foo, :bar, :baz)
68
+ ```
69
+
70
+ ### HashMerger
71
+
72
+ `HashMerger` is useful for recursively deep merging hashes that can contains arrays, which should also be merged.
73
+
74
+ Example:
75
+
76
+ ```ruby
77
+ one = {
78
+ foo: {
79
+ bar: [1,2,3]
80
+ }
81
+ }
82
+
83
+ two = {
84
+ foo: {
85
+ bar: [4,5]
86
+ qux: "Yo!"
87
+ }
88
+ }
89
+
90
+ HashMerger.new(one).merge_with(two) # => {
91
+ # foo: {
92
+ # bar: [1,2,3,4,5],
93
+ # qux: "Yo!"
94
+ # }
95
+ # }
96
+ ```
97
+
98
+ **NOTE**: The merging is done using recursion, which might overflow the stack in Ruby due to the lack of tail call optimization. This shouldn't be a problem unless you have giant hashes, but be aware!
99
+
100
+ ### HashBuilder
101
+
102
+ `HashBuilder` is useful for building a hash that might contain deep nesting.
103
+
104
+ ```ruby
105
+ # This
106
+ json = {
107
+ one: {
108
+ two: {
109
+ three: {
110
+ four: :foo
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ # Becomes this
117
+ builder = HashBuilder.new
118
+ builder.one.two.three.four = :foo
119
+ json = builder.as_json
120
+ ```
24
121
 
25
122
  ## Contributing
26
123
 
data/Rakefile CHANGED
@@ -1,2 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ begin
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
7
+ rescue LoadError
8
+ end
data/lib/deep_hash.rb CHANGED
@@ -14,11 +14,7 @@ class DeepHash
14
14
 
15
15
  def dig(*keys)
16
16
  keys.inject(@hash) do |location, key|
17
- if location.nil?
18
- nil
19
- else
20
- location[key]
21
- end
17
+ location[key] unless location.nil?
22
18
  end
23
19
  end
24
20
  end
data/lib/hash_builder.rb CHANGED
@@ -30,7 +30,9 @@ class HashBuilder
30
30
  end
31
31
 
32
32
  def method_missing(name, *args)
33
- if match = name.to_s.match(/(?<key>.*?)=$/)
33
+ match = name.to_s.match(/(?<key>.*?)=$/)
34
+
35
+ if match.present?
34
36
  @hash[match[:key].to_sym] = args.first
35
37
  else
36
38
  @hash[name] = HashBuilder.new if @hash[name].blank?
data/lib/hash_merger.rb CHANGED
@@ -20,7 +20,7 @@ class HashMerger
20
20
  private
21
21
 
22
22
  def do_merge(hash, acc)
23
- hash.inject(acc) do |acc, (key, value)|
23
+ hash.each_with_object(acc) do |(key, value), _acc|
24
24
  acc[key] = if value.is_a?(Hash)
25
25
  do_merge(value, acc[key].dup || {})
26
26
  elsif value.is_a?(Array)
@@ -28,8 +28,6 @@ class HashMerger
28
28
  else
29
29
  value
30
30
  end
31
-
32
- acc
33
31
  end
34
32
  end
35
33
  end
@@ -4,15 +4,15 @@ class HashWithQuickAccess
4
4
  end
5
5
 
6
6
  def method_missing(key)
7
- if has_key(key)
7
+ if key?(key)
8
8
  fetch_possibly_decorated_value(key)
9
9
  else
10
- raise KeyError.new("key :#{key} was not found")
10
+ fail KeyError, "key :#{key} was not found"
11
11
  end
12
12
  end
13
13
 
14
14
  def respond_to?(method_name, include_private = false)
15
- has_key(method_name) || super
15
+ key?(method_name) || super
16
16
  end
17
17
 
18
18
  def keys
@@ -47,7 +47,7 @@ class HashWithQuickAccess
47
47
  end
48
48
  end
49
49
 
50
- def has_key(key)
50
+ def key?(key)
51
51
  all_keys.include?(key)
52
52
  end
53
53
 
@@ -1,3 +1,3 @@
1
1
  module TonsserHashUtils
2
- VERSION = "1.0"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -2,17 +2,17 @@ require "deep_hash"
2
2
 
3
3
  describe DeepHash, "#dig" do
4
4
  it "returns the value if its there" do
5
- hash = DeepHash.new({
5
+ hash = DeepHash.new(
6
6
  foo: {
7
7
  bar: {
8
8
  baz: {
9
- qux: "value"
10
- }
11
- }
12
- }
13
- })
9
+ qux: "value",
10
+ },
11
+ },
12
+ },
13
+ )
14
14
 
15
- expect(hash.dig(:foo, :bar, :baz)).to eq({ qux: "value" })
15
+ expect(hash.dig(:foo, :bar, :baz)).to eq(qux: "value")
16
16
  expect(hash.dig(:foo, :bar, :baz, :qux)).to eq "value"
17
17
  end
18
18
 
@@ -16,7 +16,7 @@ describe HashBuilder do
16
16
  hash.foo = :foo
17
17
  hash.bar = :bar
18
18
 
19
- expect(hash.as_json).to eq({"foo"=>"foo", "bar"=>"bar"})
19
+ expect(hash.as_json).to eq("foo" => "foo", "bar" => "bar")
20
20
  end
21
21
 
22
22
  it "allows easy creation of nested hashes" do
@@ -24,6 +24,8 @@ describe HashBuilder do
24
24
  hash.one.two.three.four = :foo
25
25
 
26
26
  expect(hash.one.two.three.four).to eq :foo
27
- expect(hash.as_json).to eq({"one"=>{"two"=>{"three"=>{"four"=>"foo"}}}})
27
+ expect(hash.as_json).to eq(
28
+ "one" => { "two" => { "three" => { "four" => "foo" } } },
29
+ )
28
30
  end
29
31
  end
@@ -5,142 +5,132 @@ describe HashMerger do
5
5
  a = {
6
6
  foo: {
7
7
  bar: {
8
- baz: "one"
8
+ baz: "one",
9
9
  },
10
- yo: "yo"
10
+ yo: "yo",
11
11
  },
12
- yo: "yo"
12
+ yo: "yo",
13
13
  }
14
14
 
15
15
  b = {
16
16
  foo: {
17
17
  bar: {
18
- qux: "two"
19
- }
18
+ qux: "two",
19
+ },
20
20
  },
21
- dog: "dog"
21
+ dog: "dog",
22
22
  }
23
23
 
24
24
  merged = HashMerger.new(a).merge_with(b)
25
25
 
26
- expect(merged).to eq({
26
+ expect(merged).to eq(
27
27
  foo: {
28
28
  bar: {
29
29
  baz: "one",
30
30
  qux: "two",
31
31
  },
32
- yo: "yo"
32
+ yo: "yo",
33
33
  },
34
34
  yo: "yo",
35
35
  dog: "dog",
36
- })
36
+ )
37
37
 
38
- expect(a).to eq({
38
+ expect(a).to eq(
39
39
  foo: {
40
40
  bar: {
41
- baz: "one"
41
+ baz: "one",
42
42
  },
43
- yo: "yo"
43
+ yo: "yo",
44
44
  },
45
- yo: "yo"
46
- })
45
+ yo: "yo",
46
+ )
47
47
 
48
- expect(b).to eq({
48
+ expect(b).to eq(
49
49
  foo: {
50
50
  bar: {
51
- qux: "two"
52
- }
51
+ qux: "two",
52
+ },
53
53
  },
54
- dog: "dog"
55
- })
54
+ dog: "dog",
55
+ )
56
56
  end
57
57
 
58
58
  it "deep merges arrays" do
59
59
  a = {
60
60
  foo: {
61
- bar: ["one"]
62
- }
61
+ bar: ["one"],
62
+ },
63
63
  }
64
64
 
65
65
  b = {
66
66
  foo: {
67
- bar: ["two"]
68
- }
67
+ bar: ["two"],
68
+ },
69
69
  }
70
70
 
71
71
  merged = HashMerger.new(a).merge_with(b)
72
72
 
73
- expect(merged).to eq({
74
- foo: {
75
- bar: ["one", "two"]
76
- }
77
- })
78
-
79
- expect(a).to eq({
80
- foo: { bar: ["one"] }
81
- })
82
-
83
- expect(b).to eq({
84
- foo: { bar: ["two"] }
85
- })
73
+ expect(merged).to eq(foo: { bar: ["one", "two"] })
74
+ expect(a).to eq(foo: { bar: ["one"] })
75
+ expect(b).to eq(foo: { bar: ["two"] })
86
76
  end
87
77
 
88
78
  it "merges hashes with mixed types" do
89
79
  a = {
90
80
  foo: {
91
81
  bar: {
92
- baz: "one"
82
+ baz: "one",
93
83
  },
94
- array: [1,2],
95
- yo: "yo"
84
+ array: [1, 2],
85
+ yo: "yo",
96
86
  },
97
- yo: "yo"
87
+ yo: "yo",
98
88
  }
99
89
 
100
90
  b = {
101
91
  foo: {
102
92
  bar: {
103
- qux: "two"
93
+ qux: "two",
104
94
  },
105
- array: [3,4]
95
+ array: [3, 4],
106
96
  },
107
- dog: "dog"
97
+ dog: "dog",
108
98
  }
109
99
 
110
100
  merged = HashMerger.new(a).merge_with(b)
111
101
 
112
- expect(merged).to eq({
102
+ expect(merged).to eq(
113
103
  foo: {
114
104
  bar: {
115
105
  baz: "one",
116
106
  qux: "two",
117
107
  },
118
- array: [1,2,3,4],
119
- yo: "yo"
108
+ array: [1, 2, 3, 4],
109
+ yo: "yo",
120
110
  },
121
111
  yo: "yo",
122
112
  dog: "dog",
123
- })
113
+ )
124
114
 
125
- expect(a).to eq({
115
+ expect(a).to eq(
126
116
  foo: {
127
117
  bar: {
128
- baz: "one"
118
+ baz: "one",
129
119
  },
130
- array: [1,2],
131
- yo: "yo"
120
+ array: [1, 2],
121
+ yo: "yo",
132
122
  },
133
- yo: "yo"
134
- })
123
+ yo: "yo",
124
+ )
135
125
 
136
- expect(b).to eq({
126
+ expect(b).to eq(
137
127
  foo: {
138
128
  bar: {
139
- qux: "two"
129
+ qux: "two",
140
130
  },
141
- array: [3,4]
131
+ array: [3, 4],
142
132
  },
143
- dog: "dog"
144
- })
133
+ dog: "dog",
134
+ )
145
135
  end
146
136
  end
@@ -2,7 +2,7 @@ require "hash_with_quick_access"
2
2
 
3
3
  describe HashWithQuickAccess do
4
4
  it "gives you quick access to hashes or arrays nested in other hashes" do
5
- hash = HashWithQuickAccess.new({
5
+ hash = HashWithQuickAccess.new(
6
6
  name: "Mikkel Hansen",
7
7
  books: [
8
8
  { name: "Fight Club" },
@@ -10,7 +10,7 @@ describe HashWithQuickAccess do
10
10
  job: {
11
11
  title: "Programmer",
12
12
  },
13
- })
13
+ )
14
14
 
15
15
  expect(hash.name).to eq "Mikkel Hansen"
16
16
  expect(hash.books.first.name).to eq "Fight Club"
@@ -18,11 +18,11 @@ describe HashWithQuickAccess do
18
18
  end
19
19
 
20
20
  it "only wraps values if they are hashes" do
21
- hash = HashWithQuickAccess.new({
21
+ hash = HashWithQuickAccess.new(
22
22
  books: [
23
- "Fight Club"
23
+ "Fight Club",
24
24
  ],
25
- })
25
+ )
26
26
 
27
27
  expect(hash.books.first).to eq "Fight Club"
28
28
  end
@@ -30,32 +30,28 @@ describe HashWithQuickAccess do
30
30
  it "gives a useful error when asking for key that doesn't exist" do
31
31
  hash = HashWithQuickAccess.new({})
32
32
 
33
- expect {
33
+ expect do
34
34
  hash.foo.bar.baz
35
- }.to raise_error(KeyError, "key :foo was not found")
35
+ end.to raise_error(KeyError, "key :foo was not found")
36
36
  end
37
37
 
38
38
  it "lets you get at the keys" do
39
- hash = HashWithQuickAccess.new({
39
+ hash = HashWithQuickAccess.new(
40
40
  name: "Mikkel Hansen",
41
41
  books: [],
42
- })
42
+ )
43
43
 
44
44
  expect(hash.keys).to eq [:name, :books]
45
45
  end
46
46
 
47
47
  it "allows lookups when the keys are strings" do
48
- hash = HashWithQuickAccess.new({ "name" => 1 })
48
+ hash = HashWithQuickAccess.new("name" => 1)
49
49
 
50
50
  expect(hash.name).to eq 1
51
51
  end
52
52
 
53
53
  it "lets you use the standard hash #[] method" do
54
- hash = HashWithQuickAccess.new({
55
- job: {
56
- title: "Programmer",
57
- },
58
- })
54
+ hash = HashWithQuickAccess.new(job: { title: "Programmer" })
59
55
 
60
56
  expect(hash[:job][:title]).to eq "Programmer"
61
57
  end
data/spec/spec_helper.rb CHANGED
@@ -1,91 +1,11 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # The generated `.rspec` file contains `--require spec_helper` which will cause
4
- # this file to always be loaded, without a need to explicitly require it in any
5
- # files.
6
- #
7
- # Given that it is always loaded, you are encouraged to keep this file as
8
- # light-weight as possible. Requiring heavyweight dependencies from this file
9
- # will add to the boot time of your test suite on EVERY test run, even for an
10
- # individual file that may not need all of that loaded. Instead, consider making
11
- # a separate helper file that requires the additional dependencies and performs
12
- # the additional setup, and require it from the spec files that actually need
13
- # it.
14
- #
15
- # The `.rspec` file also contains a few flags that are not defaults but that
16
- # users commonly want.
17
- #
18
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
1
  RSpec.configure do |config|
20
- # rspec-expectations config goes here. You can use an alternate
21
- # assertion/expectation library such as wrong or the stdlib/minitest
22
- # assertions if you prefer.
23
2
  config.expect_with :rspec do |expectations|
24
- # This option will default to `true` in RSpec 4. It makes the `description`
25
- # and `failure_message` of custom matchers include text for helper methods
26
- # defined using `chain`, e.g.:
27
- # be_bigger_than(2).and_smaller_than(4).description
28
- # # => "be bigger than 2 and smaller than 4"
29
- # ...rather than:
30
- # # => "be bigger than 2"
31
3
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
32
4
  end
33
5
 
34
- # rspec-mocks config goes here. You can use an alternate test double
35
- # library (such as bogus or mocha) by changing the `mock_with` option here.
36
6
  config.mock_with :rspec do |mocks|
37
- # Prevents you from mocking or stubbing a method that does not exist on
38
- # a real object. This is generally recommended, and will default to
39
- # `true` in RSpec 4.
40
7
  mocks.verify_partial_doubles = true
41
8
  end
42
9
 
43
- # The settings below are suggested to provide a good initial experience
44
- # with RSpec, but feel free to customize to your heart's content.
45
- =begin
46
- # These two settings work together to allow you to limit a spec run
47
- # to individual examples or groups you care about by tagging them with
48
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
49
- # get run.
50
- config.filter_run :focus
51
- config.run_all_when_everything_filtered = true
52
-
53
- # Limits the available syntax to the non-monkey patched syntax that is
54
- # recommended. For more details, see:
55
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
56
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
57
- # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
58
- config.disable_monkey_patching!
59
-
60
- # This setting enables warnings. It's recommended, but in some cases may
61
- # be too noisy due to issues in dependencies.
62
- config.warnings = true
63
-
64
- # Many RSpec users commonly either run the entire suite or an individual
65
- # file, and it's useful to allow more verbose output when running an
66
- # individual spec file.
67
- if config.files_to_run.one?
68
- # Use the documentation formatter for detailed output,
69
- # unless a formatter has already been configured
70
- # (e.g. via a command-line flag).
71
- config.default_formatter = 'doc'
72
- end
73
-
74
- # Print the 10 slowest examples and example groups at the
75
- # end of the spec run, to help surface which specs are running
76
- # particularly slow.
77
- config.profile_examples = 10
78
-
79
- # Run specs in random order to surface order dependencies. If you find an
80
- # order dependency and want to debug it, you can fix the order by providing
81
- # the seed, which is printed after each run.
82
- # --seed 1234
83
10
  config.order = :random
84
-
85
- # Seed global randomization in this process using the `--seed` CLI option.
86
- # Setting this allows you to use `--seed` to deterministically reproduce
87
- # test failures related to randomization by passing the same `--seed` value
88
- # as the one that triggered the failure.
89
- Kernel.srand config.seed
90
- =end
91
11
  end
@@ -1,21 +1,21 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
2
+ lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'tonsser_hash_utils/version'
4
+ require "tonsser_hash_utils/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "tonsser_hash_utils"
8
8
  spec.version = TonsserHashUtils::VERSION
9
9
  spec.authors = ["David Pedersen"]
10
10
  spec.email = ["david@tonsser.com"]
11
- spec.summary = %q{A collection of classes for dealing with hashes}
12
- spec.description = %q{}
11
+ spec.summary = "A collection of classes for dealing with hashes"
12
+ spec.description = ""
13
13
  spec.homepage = "http://github.com/tonsser/tonsser_hash_utils"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tonsser_hash_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Pedersen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-12 00:00:00.000000000 Z
11
+ date: 2015-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - ".gitignore"
77
77
  - ".rspec"
78
+ - ".ruby-version"
78
79
  - Gemfile
79
80
  - LICENSE.txt
80
81
  - README.md