hashie 3.6.0 → 5.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +271 -207
  3. data/CONTRIBUTING.md +13 -6
  4. data/LICENSE +1 -1
  5. data/README.md +245 -54
  6. data/UPGRADING.md +121 -7
  7. data/hashie.gemspec +13 -7
  8. data/lib/hashie/dash.rb +18 -17
  9. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  10. data/lib/hashie/extensions/coercion.rb +23 -16
  11. data/lib/hashie/extensions/dash/indifferent_access.rb +29 -1
  12. data/lib/hashie/extensions/dash/predefined_values.rb +88 -0
  13. data/lib/hashie/extensions/dash/property_translation.rb +12 -4
  14. data/lib/hashie/extensions/deep_fetch.rb +4 -2
  15. data/lib/hashie/extensions/deep_find.rb +12 -3
  16. data/lib/hashie/extensions/deep_locate.rb +22 -7
  17. data/lib/hashie/extensions/deep_merge.rb +18 -1
  18. data/lib/hashie/extensions/ignore_undeclared.rb +4 -5
  19. data/lib/hashie/extensions/indifferent_access.rb +37 -6
  20. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  21. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  22. data/lib/hashie/extensions/mash/keep_original_keys.rb +2 -1
  23. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  24. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  25. data/lib/hashie/extensions/mash/symbolize_keys.rb +5 -5
  26. data/lib/hashie/extensions/method_access.rb +5 -2
  27. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +26 -4
  28. data/lib/hashie/extensions/ruby_version_check.rb +5 -1
  29. data/lib/hashie/extensions/strict_key_access.rb +8 -4
  30. data/lib/hashie/extensions/symbolize_keys.rb +12 -1
  31. data/lib/hashie/hash.rb +16 -9
  32. data/lib/hashie/mash.rb +130 -64
  33. data/lib/hashie/railtie.rb +7 -0
  34. data/lib/hashie/rash.rb +1 -1
  35. data/lib/hashie/utils.rb +28 -0
  36. data/lib/hashie/version.rb +1 -1
  37. data/lib/hashie.rb +22 -19
  38. metadata +23 -133
  39. data/spec/hashie/array_spec.rb +0 -29
  40. data/spec/hashie/clash_spec.rb +0 -70
  41. data/spec/hashie/dash_spec.rb +0 -598
  42. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  43. data/spec/hashie/extensions/coercion_spec.rb +0 -639
  44. data/spec/hashie/extensions/dash/coercion_spec.rb +0 -13
  45. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  46. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  47. data/spec/hashie/extensions/deep_find_spec.rb +0 -138
  48. data/spec/hashie/extensions/deep_locate_spec.rb +0 -137
  49. data/spec/hashie/extensions/deep_merge_spec.rb +0 -70
  50. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -47
  51. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -295
  52. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  53. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  54. data/spec/hashie/extensions/mash/keep_original_keys_spec.rb +0 -46
  55. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -50
  56. data/spec/hashie/extensions/mash/symbolize_keys_spec.rb +0 -39
  57. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  58. data/spec/hashie/extensions/method_access_spec.rb +0 -226
  59. data/spec/hashie/extensions/strict_key_access_spec.rb +0 -110
  60. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -124
  61. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -129
  62. data/spec/hashie/hash_spec.rb +0 -84
  63. data/spec/hashie/mash_spec.rb +0 -771
  64. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -46
  65. data/spec/hashie/rash_spec.rb +0 -83
  66. data/spec/hashie/trash_spec.rb +0 -328
  67. data/spec/hashie/utils_spec.rb +0 -25
  68. data/spec/hashie/version_spec.rb +0 -7
  69. data/spec/hashie_spec.rb +0 -13
  70. data/spec/integration/elasticsearch/integration_spec.rb +0 -40
  71. data/spec/integration/omniauth/app.rb +0 -11
  72. data/spec/integration/omniauth/integration_spec.rb +0 -38
  73. data/spec/integration/omniauth-oauth2/app.rb +0 -52
  74. data/spec/integration/omniauth-oauth2/integration_spec.rb +0 -26
  75. data/spec/integration/omniauth-oauth2/some_site.rb +0 -38
  76. data/spec/integration/rails/app.rb +0 -47
  77. data/spec/integration/rails/integration_spec.rb +0 -26
  78. data/spec/integration/rails-without-dependency/integration_spec.rb +0 -15
  79. data/spec/spec_helper.rb +0 -23
  80. data/spec/support/integration_specs.rb +0 -36
  81. data/spec/support/logger.rb +0 -24
  82. data/spec/support/module_context.rb +0 -11
  83. data/spec/support/ruby_version_check.rb +0 -6
data/CONTRIBUTING.md CHANGED
@@ -1,16 +1,16 @@
1
1
  Contributing to Hashie
2
2
  ======================
3
3
 
4
- Hashie is work of [many contributors](https://github.com/intridea/hashie/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/intridea/hashie/pulls), [propose features and discuss issues](https://github.com/intridea/hashie/issues).
4
+ Hashie is work of [many contributors](https://github.com/hashie/hashie/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/hashie/hashie/pulls), [propose features and discuss issues](https://github.com/hashie/hashie/issues).
5
5
 
6
6
  #### Fork the Project
7
7
 
8
- Fork the [project on Github](https://github.com/intridea/hashie) and check out your copy.
8
+ Fork the [project on Github](https://github.com/hashie/hashie) and check out your copy.
9
9
 
10
10
  ```
11
11
  git clone https://github.com/contributor/hashie.git
12
12
  cd hashie
13
- git remote add upstream https://github.com/intridea/hashie.git
13
+ git remote add upstream https://github.com/hashie/hashie.git
14
14
  ```
15
15
 
16
16
  #### Create a Topic Branch
@@ -23,12 +23,19 @@ git pull upstream master
23
23
  git checkout -b my-feature-branch
24
24
  ```
25
25
 
26
- #### Bundle Install and Test
26
+ #### Install dependencies
27
+
28
+ You can use the setup script to install dependencies for the gem and its integration tests.
29
+
30
+ ```
31
+ bin/setup
32
+ ```
33
+
34
+ #### Test
27
35
 
28
36
  Ensure that you can build the project and run tests.
29
37
 
30
38
  ```
31
- bundle install
32
39
  bundle exec rake
33
40
  ```
34
41
 
@@ -105,7 +112,7 @@ git push origin my-feature-branch -f
105
112
  Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows.
106
113
 
107
114
  ```
108
- * [#123](https://github.com/intridea/hashie/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
115
+ * [#123](https://github.com/hashie/hashie/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
109
116
  ```
110
117
 
111
118
  Amend your previous commit and force push the changes.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Intridea, Inc., and Contributors
1
+ Copyright (c) 2009-2020 Intridea, Inc., and Contributors.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,12 +1,56 @@
1
1
  # Hashie
2
2
 
3
- [![Join the chat at https://gitter.im/intridea/hashie](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/intridea/hashie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3
+ [![Join the chat at https://gitter.im/hashie/hashie](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hashie/hashie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
  [![Gem Version](http://img.shields.io/gem/v/hashie.svg)](http://badge.fury.io/rb/hashie)
5
- [![Build Status](http://img.shields.io/travis/intridea/hashie.svg)](https://travis-ci.org/intridea/hashie)
6
- [![Test Coverage](https://api.codeclimate.com/v1/badges/7a0b42c8a22c945571fd/test_coverage)](https://codeclimate.com/github/intridea/hashie/test_coverage)
7
- [![Maintainability](https://api.codeclimate.com/v1/badges/7a0b42c8a22c945571fd/maintainability)](https://codeclimate.com/github/intridea/hashie/maintainability)
8
-
9
- Hashie is a growing collection of tools that extend Hashes and make them more useful.
5
+ [![Build Status](https://github.com/hashie/hashie/actions/workflows/main.yml/badge.svg)](https://github.com/hashie/hashie/actions/workflows/main.yml)
6
+
7
+ [![eierlegende Wollmilchsau](./mascot.svg)](#mascot) Hashie is a growing collection of tools that extend Hashes and make them more useful.
8
+
9
+ # Table of Contents
10
+
11
+ - [Hashie](#hashie)
12
+ - [Table of Contents](#table-of-contents)
13
+ - [Installation](#installation)
14
+ - [Stable Release](#stable-release)
15
+ - [Hash Extensions](#hash-extensions)
16
+ - [Logging](#logging)
17
+ - [Coercion](#coercion)
18
+ - [Coercing Collections](#coercing-collections)
19
+ - [Coercing Hashes](#coercing-hashes)
20
+ - [Coercing Core Types](#coercing-core-types)
21
+ - [Coercion Proc](#coercion-proc)
22
+ - [A note on circular coercion](#a-note-on-circular-coercion)
23
+ - [KeyConversion](#keyconversion)
24
+ - [MergeInitializer](#mergeinitializer)
25
+ - [MethodAccess](#methodaccess)
26
+ - [MethodAccessWithOverride](#methodaccesswithoverride)
27
+ - [MethodOverridingInitializer](#methodoverridinginitializer)
28
+ - [IndifferentAccess](#indifferentaccess)
29
+ - [IgnoreUndeclared](#ignoreundeclared)
30
+ - [DeepMerge](#deepmerge)
31
+ - [DeepFetch](#deepfetch)
32
+ - [DeepFind](#deepfind)
33
+ - [DeepLocate](#deeplocate)
34
+ - [StrictKeyAccess](#strictkeyaccess)
35
+ - [Mash](#mash)
36
+ - [KeepOriginalKeys](#keeporiginalkeys)
37
+ - [PermissiveRespondTo](#permissiverespondto)
38
+ - [SafeAssignment](#safeassignment)
39
+ - [SymbolizeKeys](#symbolizekeys)
40
+ - [DefineAccessors](#defineaccessors)
41
+ - [Dash](#dash)
42
+ - [Potential Gotchas](#potential-gotchas)
43
+ - [PropertyTranslation](#propertytranslation)
44
+ - [Mash and Rails 4 Strong Parameters](#mash-and-rails-4-strong-parameters)
45
+ - [Coercion](#coercion-1)
46
+ - [PredefinedValues](#predefinedvalues)
47
+ - [Trash](#trash)
48
+ - [Clash](#clash)
49
+ - [Rash](#rash)
50
+ - [Auto-Optimized](#auto-optimized)
51
+ - [Mascot](#mascot)
52
+ - [Contributing](#contributing)
53
+ - [Copyright](#copyright)
10
54
 
11
55
  ## Installation
12
56
 
@@ -16,9 +60,9 @@ Hashie is available as a RubyGem:
16
60
  $ gem install hashie
17
61
  ```
18
62
 
19
- ## Upgrading
63
+ ## Stable Release
20
64
 
21
- You're reading the documentation for the stable release of Hashie, 3.6.0. Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
65
+ You're reading the documentation for the stable release of Hashie, v5.0.0.
22
66
 
23
67
  ## Hash Extensions
24
68
 
@@ -195,8 +239,8 @@ The KeyConversion extension gives you the convenience methods of `symbolize_keys
195
239
  Hashie also has a utility method for converting keys on a Hash without a mixin:
196
240
 
197
241
  ```ruby
198
- Hashie.symbolize_keys! hash # => Symbolizes keys of hash.
199
- Hashie.symbolize_keys hash # => Returns a copy of hash with keys symbolized.
242
+ Hashie.symbolize_keys! hash # => Symbolizes all string keys of hash.
243
+ Hashie.symbolize_keys hash # => Returns a copy of hash with string keys symbolized.
200
244
  Hashie.stringify_keys! hash # => Stringifies keys of hash.
201
245
  Hashie.stringify_keys hash # => Returns a copy of hash with keys stringified.
202
246
  ```
@@ -269,8 +313,6 @@ This extension can be mixed in to your Hash subclass to allow you to use Strings
269
313
 
270
314
  In addition, IndifferentAccess will also inject itself into sub-hashes so they behave the same.
271
315
 
272
- Example:
273
-
274
316
  ```ruby
275
317
  class MyHash < Hash
276
318
  include Hashie::Extensions::MergeInitializer
@@ -290,6 +332,18 @@ myhash['fishes'][:food] = 'flakes'
290
332
  myhash['fishes']['food'] # => "flakes"
291
333
  ```
292
334
 
335
+ To get back a normal, not-indifferent Hash, you can use `#to_hash` on the indifferent hash. It exports the keys as strings, not symbols:
336
+
337
+ ```ruby
338
+ myhash = MyHash.new
339
+ myhash["foo"] = "bar"
340
+ myhash[:foo] #=> "bar"
341
+
342
+ normal_hash = myhash.to_hash
343
+ myhash["foo"] #=> "bar"
344
+ myhash[:foo] #=> nil
345
+ ```
346
+
293
347
  ### IgnoreUndeclared
294
348
 
295
349
  This extension can be mixed in to silently ignore undeclared properties on initialization instead of raising an error. This is useful when using a Trash to capture a subset of a larger hash.
@@ -316,8 +370,8 @@ p.email # => NoMethodError
316
370
 
317
371
  ### DeepMerge
318
372
 
319
- This extension allow you to easily include a recursive merging
320
- system to any Hash descendant:
373
+ This extension allows you to easily include a recursive merging
374
+ system into any Hash descendant:
321
375
 
322
376
  ```ruby
323
377
  class MyHash < Hash
@@ -445,8 +499,6 @@ books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
445
499
 
446
500
  This extension can be mixed in to allow a Hash to raise an error when attempting to extract a value using a non-existent key.
447
501
 
448
- ### Example:
449
-
450
502
  ```ruby
451
503
  class StrictKeyAccessHash < Hash
452
504
  include Hashie::Extensions::StrictKeyAccess
@@ -464,8 +516,6 @@ end
464
516
 
465
517
  Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended. It is intended to give the user easier access to the objects within the Mash through a property-like syntax, while still retaining all Hash functionality.
466
518
 
467
- ### Example:
468
-
469
519
  ```ruby
470
520
  mash = Hashie::Mash.new
471
521
  mash.name? # => false
@@ -488,12 +538,10 @@ mash.inspect # => <Hashie::Mash>
488
538
 
489
539
  **Note:** The `?` method will return false if a key has been set to false or nil. In order to check if a key has been set at all, use the `mash.key?('some_key')` method instead.
490
540
 
491
- ### How does Mash handle conflicts with pre-existing methods?
541
+ _How does Mash handle conflicts with pre-existing methods?_
492
542
 
493
543
  Please note that a Mash will not override methods through the use of the property-like syntax. This can lead to confusion if you expect to be able to access a Mash value through the property-like syntax for a key that conflicts with a method name. However, it protects users of your library from the unexpected behavior of those methods being overridden behind the scenes.
494
544
 
495
- #### Example:
496
-
497
545
  ```ruby
498
546
  mash = Hashie::Mash.new
499
547
  mash.name = "My Mash"
@@ -515,11 +563,58 @@ class Response < Hashie::Mash
515
563
  end
516
564
  ```
517
565
 
518
- ### How does the wrapping of Mash sub-Hashes work?
566
+ The default is to disable logging for all methods that conflict. If you would like to only disable the logging for specific methods, you can include an array of method keys:
519
567
 
520
- Mash duplicates any sub-Hashes that you add to it and wraps them in a Mash. This allows for infinite chaining of nested Hashes within a Mash without modifying the object(s) that are passed into the Mash. When you subclass Mash, the subclass wraps any sub-Hashes in its own class. This preserves any extensions that you mixed into the Mash subclass and allows them to work within the sub-Hashes, in addition to the main containing Mash.
568
+ ```ruby
569
+ class Response < Hashie::Mash
570
+ disable_warnings :zip, :zap
571
+ end
572
+ ```
573
+
574
+ This behavior is cumulative. The examples above and below behave identically.
575
+
576
+ ```ruby
577
+ class Response < Hashie::Mash
578
+ disable_warnings :zip
579
+ disable_warnings :zap
580
+ end
581
+ ```
582
+
583
+ Disable warnings will honor the last `disable_warnings` call. Calling without parameters will override the ignored methods list, and calling with parameters will create a new ignored methods list. This includes child classes that inherit from a class that disables warnings.
584
+
585
+ ```ruby
586
+ class Message < Hashie::Mash
587
+ disable_warnings :zip, :zap
588
+ disable_warnings
589
+ end
590
+
591
+ # No errors will be logged
592
+ Message.new(merge: 'true', compact: true)
593
+ ```
594
+
595
+ ```ruby
596
+ class Message < Hashie::Mash
597
+ disable_warnings
598
+ end
599
+
600
+ class Response < Message
601
+ disable_warnings :zip, :zap
602
+ end
521
603
 
522
- #### Example:
604
+ # 2 errors will be logged
605
+ Response.new(merge: 'true', compact: true, zip: '90210', zap: 'electric')
606
+ ```
607
+
608
+ If you would like to create an anonymous subclass of a Hashie::Mash with key conflict warnings disabled:
609
+
610
+ ```ruby
611
+ Hashie::Mash.quiet.new(zip: '90210', compact: true) # no errors logged
612
+ Hashie::Mash.quiet(:zip).new(zip: '90210', compact: true) # error logged for compact
613
+ ```
614
+
615
+ _How does the wrapping of Mash sub-Hashes work?_
616
+
617
+ Mash duplicates any sub-Hashes that you add to it and wraps them in a Mash. This allows for infinite chaining of nested Hashes within a Mash without modifying the object(s) that are passed into the Mash. When you subclass Mash, the subclass wraps any sub-Hashes in its own class. This preserves any extensions that you mixed into the Mash subclass and allows them to work within the sub-Hashes, in addition to the main containing Mash.
523
618
 
524
619
  ```ruby
525
620
  mash = Hashie::Mash.new(name: "Hashie", dependencies: { rake: "< 11", rspec: "~> 3.0" })
@@ -530,11 +625,20 @@ my_gem = MyGem.new(name: "Hashie", dependencies: { rake: "< 11", rspec: "~> 3.0"
530
625
  my_gem.dependencies.class #=> MyGem
531
626
  ```
532
627
 
533
- ### What else can Mash do?
628
+ _How does Mash handle key types which cannot be symbolized?_
534
629
 
535
- Mash allows you also to transform any files into a Mash objects.
630
+ Mash preserves keys which cannot be converted *directly* to both a string and a symbol, such as numeric keys. Since Mash is conceived to provide psuedo-object functionality, handling keys which cannot represent a method call falls outside its scope of value.
536
631
 
537
- #### Example:
632
+ ```ruby
633
+ Hashie::Mash.new('1' => 'one string', :'1' => 'one sym', 1 => 'one num')
634
+ # => {"1"=>"one sym", 1=>"one num"}
635
+ ```
636
+
637
+ The symbol key `:'1'` is converted the string `'1'` to support indifferent access and consequently its value `'one sym'` will override the previously set `'one string'`. However, the subsequent key of `1` cannot directly convert to a symbol and therefore **not** converted to the string `'1'` that would otherwise override the previously set value of `'one sym'`.
638
+
639
+ _What else can Mash do?_
640
+
641
+ Mash allows you also to transform any files into a Mash objects.
538
642
 
539
643
  ```yml
540
644
  #/etc/config/settings/twitter.yml
@@ -566,7 +670,7 @@ Twitter.extend mash.to_module # NOTE: if you want another name than settings, ca
566
670
  Twitter.settings.api_key # => 'abcd'
567
671
  ```
568
672
 
569
- You can use another parser (by default: YamlErbParser):
673
+ You can use another parser (by default: [YamlErbParser](lib/hashie/extensions/parsers/yaml_erb_parser.rb)):
570
674
 
571
675
  ```
572
676
  #/etc/data/user.csv
@@ -582,9 +686,17 @@ mash = Mash.load('data/user.csv', parser: MyCustomCsvParser)
582
686
  mash[1] #=> { name: 'John', lastname: 'Doe' }
583
687
  ```
584
688
 
585
- ### Mash Extension: KeepOriginalKeys
689
+ The `Mash#load` method calls `YAML.safe_load(path, [], [], true)`.
690
+
691
+ Specify `permitted_symbols`, `permitted_classes` and `aliases` options as needed.
692
+
693
+ ```ruby
694
+ Mash.load('data/user.csv', permitted_classes: [Symbol], permitted_symbols: [], aliases: false)
695
+ ```
696
+
697
+ ### KeepOriginalKeys
586
698
 
587
- This extension can be mixed into a Mash to keep the form of any keys passed directly into the Mash. By default, Mash converts keys to strings to give indifferent access. This extension still allows indifferent access, but keeps the form of the keys to eliminate confusion when you're not expecting the keys to change.
699
+ This extension can be mixed into a Mash to keep the form of any keys passed directly into the Mash. By default, Mash converts symbol keys to strings to give indifferent access. This extension still allows indifferent access, but keeps the form of the keys to eliminate confusion when you're not expecting the keys to change.
588
700
 
589
701
  ```ruby
590
702
  class KeepingMash < ::Hashie::Mash
@@ -601,11 +713,33 @@ mash['string_key'] #=> 'string'
601
713
  mash[:string_key] #=> 'string'
602
714
  ```
603
715
 
604
- ### Mash Extension: SafeAssignment
716
+ ### PermissiveRespondTo
605
717
 
606
- This extension can be mixed into a Mash to guard the attempted overwriting of methods by property setters. When mixed in, the Mash will raise an `ArgumentError` if you attempt to write a property with the same name as an existing method.
718
+ By default, Mash only states that it responds to built-in methods, affixed methods (e.g. setters, underbangs, etc.), and keys that it currently contains. That means it won't state that it responds to a getter for an unset key, as in the following example:
719
+
720
+ ```ruby
721
+ mash = Hashie::Mash.new(a: 1)
722
+ mash.respond_to? :b #=> false
723
+ ```
724
+
725
+ This means that by default Mash is not a perfect match for use with a SimpleDelegator since the delegator will not forward messages for unset keys to the Mash even though it can handle them.
726
+
727
+ In order to have a SimpleDelegator-compatible Mash, you can use the `PermissiveRespondTo` extension to make Mash respond to anything.
728
+
729
+ ```ruby
730
+ class PermissiveMash < Hashie::Mash
731
+ include Hashie::Extensions::Mash::PermissiveRespondTo
732
+ end
733
+
734
+ mash = PermissiveMash.new(a: 1)
735
+ mash.respond_to? :b #=> true
736
+ ```
607
737
 
608
- #### Example:
738
+ This comes at the cost of approximately 20% performance for initialization and setters and 19KB of permanent memory growth for each such class that you create.
739
+
740
+ ### SafeAssignment
741
+
742
+ This extension can be mixed into a Mash to guard the attempted overwriting of methods by property setters. When mixed in, the Mash will raise an `ArgumentError` if you attempt to write a property with the same name as an existing method.
609
743
 
610
744
  ```ruby
611
745
  class SafeMash < ::Hashie::Mash
@@ -617,9 +751,9 @@ safe_mash.zip = 'Test' # => ArgumentError
617
751
  safe_mash[:zip] = 'test' # => still ArgumentError
618
752
  ```
619
753
 
620
- ### Mash Extension:: SymbolizeKeys
754
+ ### SymbolizeKeys
621
755
 
622
- This extension can be mixed into a Mash to change the default behavior of converting keys to strings. After mixing this extension into a Mash, the Mash will convert all keys to symbols.
756
+ This extension can be mixed into a Mash to change the default behavior of converting keys to strings. After mixing this extension into a Mash, the Mash will convert all string keys to symbols. It can be useful to use with keywords argument, which required symbol keys.
623
757
 
624
758
  ```ruby
625
759
  class SymbolizedMash < ::Hashie::Mash
@@ -630,6 +764,12 @@ symbol_mash = SymbolizedMash.new
630
764
  symbol_mash['test'] = 'value'
631
765
  symbol_mash.test #=> 'value'
632
766
  symbol_mash.to_h #=> {test: 'value'}
767
+
768
+ def example(test:)
769
+ puts test
770
+ end
771
+
772
+ example(symbol_mash) #=> value
633
773
  ```
634
774
 
635
775
  There is a major benefit and coupled with a major trade-off to this decision (at least on older Rubies). As a benefit, by using symbols as keys, you will be able to use the implicit conversion of a Mash via the `#to_hash` method to destructure (or splat) the contents of a Mash out to a block. This can be handy for doing iterations through the Mash's keys and values, as follows:
@@ -644,14 +784,37 @@ end
644
784
 
645
785
  However, on Rubies less than 2.0, this means that every key you send to the Mash will generate a symbol. Since symbols are not garbage-collected on older versions of Ruby, this can cause a slow memory leak when using a symbolized Mash with data generated from user input.
646
786
 
787
+ ### DefineAccessors
788
+
789
+ This extension can be mixed into a Mash so it makes it behave like `OpenStruct`. It reduces the overhead of `method_missing?` magic by lazily defining field accessors when they're requested.
790
+
791
+ ```ruby
792
+ class MyHash < ::Hashie::Mash
793
+ include Hashie::Extensions::Mash::DefineAccessors
794
+ end
795
+
796
+ mash = MyHash.new
797
+ MyHash.method_defined?(:foo=) #=> false
798
+ mash.foo = 123
799
+ MyHash.method_defined?(:foo=) #=> true
800
+
801
+ MyHash.method_defined?(:foo) #=> false
802
+ mash.foo #=> 123
803
+ MyHash.method_defined?(:foo) #=> true
804
+ ```
805
+
806
+ You can also extend the existing mash without defining a class:
807
+
808
+ ```ruby
809
+ mash = ::Hashie::Mash.new.with_accessors!
810
+ ```
811
+
647
812
  ## Dash
648
813
 
649
814
  Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset. Another option is message for required properties, which allow you to add custom messages for required property.
650
815
 
651
816
  You can also conditionally require certain properties by passing a Proc or Symbol. If a Proc is provided, it will be run in the context of the Dash instance. If a Symbol is provided, the value returned for the property or method of the same name will be evaluated. The property will be required if the result of the conditional is truthy.
652
817
 
653
- ### Example:
654
-
655
818
  ```ruby
656
819
  class Person < Hashie::Dash
657
820
  property :name, required: true
@@ -688,8 +851,6 @@ p.occupation # => 'Rubyist'
688
851
 
689
852
  Properties defined as symbols are not the same thing as properties defined as strings.
690
853
 
691
- ### Example:
692
-
693
854
  ```ruby
694
855
  class Tricky < Hashie::Dash
695
856
  property :trick
@@ -713,7 +874,28 @@ p = Tricky.new('trick' => 'two')
713
874
  p.trick # => NoMethodError
714
875
  ```
715
876
 
716
- ### Potential gotchas
877
+ If you would like to update a Dash and use any default values set in the case of a `nil` value, use `#update_attributes!`.
878
+
879
+ ```ruby
880
+ class WithDefaults < Hashie::Dash
881
+ property :description, default: 'none'
882
+ end
883
+
884
+ dash = WithDefaults.new
885
+ dash.description #=> 'none'
886
+
887
+ dash.description = 'You committed one of the classic blunders!'
888
+ dash.description #=> 'You committed one of the classic blunders!'
889
+
890
+ dash.description = nil
891
+ dash.description #=> nil
892
+
893
+ dash.description = 'Only slightly less known is ...'
894
+ dash.update_attributes!(description: nil)
895
+ dash.description #=> 'none'
896
+ ```
897
+
898
+ ### Potential Gotchas
717
899
 
718
900
  Because Dashes are subclasses of the built-in Ruby Hash class, the double-splat operator takes the Dash as-is without any conversion. This can lead to strange behavior when you use the double-splat operator on a Dash as the first part of a keyword list or built Hash. For example:
719
901
 
@@ -745,13 +927,11 @@ qux.is_a?(Hash) #=> true
745
927
  qux[:quux] #=> "corge"
746
928
  ```
747
929
 
748
- ### Dash Extension: PropertyTranslation
930
+ ### PropertyTranslation
749
931
 
750
932
  The `Hashie::Extensions::Dash::PropertyTranslation` mixin extends a Dash with
751
933
  the ability to remap keys from a source hash.
752
934
 
753
- ### Example from inconsistent APIs
754
-
755
935
  Property translation is useful when you need to read data from another
756
936
  application -- such as a Java API -- where the keys are named differently from
757
937
  Ruby conventions.
@@ -771,8 +951,6 @@ person[:first_name] #=> 'Michael'
771
951
  person[:last_name] #=> 'Bleigh
772
952
  ```
773
953
 
774
- ### Example using translation lambdas
775
-
776
954
  You can also use a lambda to translate the value. This is particularly useful
777
955
  when you want to ensure the type of data you're wrapping.
778
956
 
@@ -785,7 +963,7 @@ class DataModelHash < Hashie::Dash
785
963
  end
786
964
 
787
965
  model = DataModelHash.new(id: '123', created: '2014-04-25 22:35:28')
788
- model.id.class #=> Fixnum
966
+ model.id.class #=> Integer (Fixnum if you are using Ruby 2.3 or lower)
789
967
  model.created_at.class #=> Time
790
968
  ```
791
969
 
@@ -793,7 +971,7 @@ model.created_at.class #=> Time
793
971
 
794
972
  To enable compatibility with Rails 4 use the [hashie-forbidden_attributes](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes) gem.
795
973
 
796
- ### Dash Extension: Coercion.
974
+ ### Coercion
797
975
 
798
976
  If you want to use `Hashie::Extensions::Coercion` together with `Dash` then
799
977
  you may probably want to use `Hashie::Extensions::Dash::Coercion` instead.
@@ -823,6 +1001,20 @@ class UserHash < Hashie::Dash
823
1001
  end
824
1002
  ```
825
1003
 
1004
+ ### PredefinedValues
1005
+
1006
+ The `Hashie::Extensions::Dash::PredefinedValues` mixin extends a Dash with
1007
+ the ability to accept predefined values on a property.
1008
+
1009
+ ```ruby
1010
+ class UserHash < Hashie::Dash
1011
+ include Hashie::Extensions::Dash::PredefinedValues
1012
+
1013
+ property :gender, values: %i[male female prefer_not_to_say]
1014
+ property :age, values: (0..150)
1015
+ end
1016
+ ```
1017
+
826
1018
  ## Trash
827
1019
 
828
1020
  A Trash is a Dash that allows you to translate keys on initialization. It mixes
@@ -854,7 +1046,7 @@ this will produce the following
854
1046
 
855
1047
  ```ruby
856
1048
  result = Result.new(id: '123', creation_date: '2012-03-30 17:23:28')
857
- result.id.class # => Fixnum
1049
+ result.id.class # => Integer (Fixnum if you are using Ruby 2.3 or lower)
858
1050
  result.created_at.class # => Time
859
1051
  ```
860
1052
 
@@ -864,8 +1056,6 @@ Clash is a Chainable Lazy Hash that allows you to easily construct complex hashe
864
1056
 
865
1057
  Essentially, a Clash is a generalized way to provide much of the same kind of "chainability" that libraries like Arel or Rails 2.x's named_scopes provide.
866
1058
 
867
- ### Example:
868
-
869
1059
  ```ruby
870
1060
  c = Hashie::Clash.new
871
1061
  c.where(abc: 'def').order(:created_at)
@@ -891,8 +1081,6 @@ A good use case for the Rash is an URL router for a web framework, where URLs ne
891
1081
 
892
1082
  If the Rash's value is a `proc`, the `proc` will be automatically called with the regexp's MatchData (matched groups) as a block argument.
893
1083
 
894
- ### Example:
895
-
896
1084
  ```ruby
897
1085
 
898
1086
  # Mapping names to appropriate greetings
@@ -909,18 +1097,21 @@ mapper["I like traffic lights"] # => "Who DOESN'T like traffic lights?!"
909
1097
  mapper["Get off my lawn!"] # => "Forget your lawn, old man!"
910
1098
  ```
911
1099
 
912
- ### Auto-optimized
1100
+ ### Auto-Optimized
913
1101
 
914
1102
  **Note:** The Rash is automatically optimized every 500 accesses (which means that it sorts the list of Regexps, putting the most frequently matched ones at the beginning).
915
1103
 
916
1104
  If this value is too low or too high for your needs, you can tune it by setting: `rash.optimize_every = n`.
917
1105
 
1106
+ ## Mascot
1107
+ [![eierlegende Wollmilchsau](./mascot.svg)](https://en.wiktionary.org/wiki/eierlegende_Wollmilchsau) Meet Hashie's "offical" mascot, the [eierlegende Wollmilchsau](https://en.wiktionary.org/wiki/eierlegende_Wollmilchsau)!
1108
+
918
1109
  ## Contributing
919
1110
 
920
1111
  See [CONTRIBUTING.md](CONTRIBUTING.md)
921
1112
 
922
1113
  ## Copyright
923
1114
 
924
- Copyright (c) 2009-2014 Intridea, Inc. (http://intridea.com/) and [contributors](https://github.com/intridea/hashie/graphs/contributors).
1115
+ Copyright (c) 2009-2020 [Intridea, Inc.](http://intridea.com), and [contributors](https://github.com/hashie/hashie/graphs/contributors).
925
1116
 
926
1117
  MIT License. See [LICENSE](LICENSE) for details.