gorillib 0.5.0 → 0.5.2
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.
- data/.gitignore +3 -0
- data/.gitmodules +3 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -16
- data/Rakefile +2 -74
- data/away/aliasing_spec.rb +180 -0
- data/away/confidence.rb +17 -0
- data/away/stub_module.rb +33 -0
- data/gorillib.gemspec +31 -246
- data/lib/gorillib/collection/model_collection.rb +1 -0
- data/lib/gorillib/data_munging.rb +0 -1
- data/lib/gorillib/hashlike/slice.rb +2 -0
- data/lib/gorillib/model/field.rb +2 -2
- data/lib/gorillib/model/serialization.rb +9 -4
- data/lib/gorillib/model/serialization/csv.rb +1 -0
- data/lib/gorillib/model/serialization/lines.rb +2 -0
- data/lib/gorillib/model/serialization/tsv.rb +1 -0
- data/lib/gorillib/pathname.rb +1 -1
- data/lib/gorillib/pathname/utils.rb +6 -0
- data/lib/gorillib/string/inflector.rb +1 -1
- data/lib/gorillib/system.rb +1 -0
- data/lib/gorillib/system/runner.rb +36 -0
- data/lib/gorillib/type/ip_address.rb +2 -2
- data/lib/gorillib/version.rb +3 -0
- data/old/lib/gorillib/hash/indifferent_access.rb +207 -0
- data/old/lib/gorillib/hash/tree_merge.rb +4 -0
- data/old/lib/gorillib/hashlike/tree_merge.rb +49 -0
- data/old/lib/gorillib/metaprogramming/cattr_accessor.rb +79 -0
- data/old/lib/gorillib/metaprogramming/mattr_accessor.rb +61 -0
- data/old/lib/gorillib/receiver.rb +402 -0
- data/old/lib/gorillib/receiver/active_model_shim.rb +32 -0
- data/old/lib/gorillib/receiver/acts_as_hash.rb +195 -0
- data/old/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
- data/old/lib/gorillib/receiver/locale/en.yml +27 -0
- data/old/lib/gorillib/receiver/tree_diff.rb +74 -0
- data/old/lib/gorillib/receiver/validations.rb +30 -0
- data/old/lib/gorillib/receiver_model.rb +21 -0
- data/old/lib/gorillib/struct/acts_as_hash.rb +108 -0
- data/old/lib/gorillib/struct/hashlike_iteration.rb +0 -0
- data/old/spec/gorillib/hash/indifferent_access_spec.rb +391 -0
- data/old/spec/gorillib/metaprogramming/cattr_accessor_spec.rb +43 -0
- data/old/spec/gorillib/metaprogramming/mattr_accessor_spec.rb +45 -0
- data/old/spec/gorillib/receiver/receiver/acts_as_hash_spec.rb +295 -0
- data/old/spec/gorillib/receiver_spec.rb +551 -0
- data/old/spec/gorillib/struct/acts_as_hash_fuzz_spec.rb +71 -0
- data/old/spec/gorillib/struct/acts_as_hash_spec.rb +422 -0
- data/spec/gorillib/array/compact_blank_spec.rb +2 -2
- data/spec/gorillib/collection_spec.rb +6 -6
- data/spec/gorillib/factories_spec.rb +2 -2
- data/spec/gorillib/hashlike_spec.rb +2 -1
- data/spec/gorillib/model/defaults_spec.rb +3 -3
- data/spec/gorillib/model/serialization/csv_spec.rb +35 -0
- data/spec/gorillib/model/serialization/tsv_spec.rb +20 -4
- data/spec/gorillib/model/serialization_spec.rb +3 -3
- data/spec/spec_helper.rb +6 -1
- data/spec/support/factory_test_helpers.rb +2 -2
- data/spec/support/gorillib_test_helpers.rb +4 -4
- data/spec/support/hashlike_fuzzing_helper.rb +1 -15
- data/spec/support/hashlike_helper.rb +5 -1
- data/spec/support/model_test_helpers.rb +12 -1
- metadata +192 -168
- data/notes/HOWTO.md +0 -22
- data/notes/bucket.md +0 -155
- data/notes/builder.md +0 -170
- data/notes/collection.md +0 -81
- data/notes/factories.md +0 -86
- data/notes/model-overlay.md +0 -209
- data/notes/model.md +0 -135
- data/notes/structured-data-classes.md +0 -127
@@ -0,0 +1,32 @@
|
|
1
|
+
# require 'active_model'
|
2
|
+
|
3
|
+
require 'active_model/deprecated_error_methods'
|
4
|
+
require 'active_model/errors'
|
5
|
+
require 'active_model/naming'
|
6
|
+
require 'active_model/validator'
|
7
|
+
require 'active_model/translation'
|
8
|
+
require 'active_model/validations'
|
9
|
+
require 'active_support/i18n'
|
10
|
+
I18n.load_path << File.join(File.expand_path(File.dirname(__FILE__)), 'locale/en.yml')
|
11
|
+
|
12
|
+
module Receiver
|
13
|
+
module ActiveModelShim
|
14
|
+
|
15
|
+
def to_model
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def new_record?() true end
|
20
|
+
def destroyed?() false end
|
21
|
+
def errors
|
22
|
+
@_errors ||= ActiveModel::Errors.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(base)
|
26
|
+
base.class_eval do
|
27
|
+
extend ActiveModel::Naming
|
28
|
+
include ActiveModel::Validations
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'gorillib/hashlike'
|
2
|
+
|
3
|
+
module Receiver
|
4
|
+
#
|
5
|
+
# Makes a Receiver thingie behave mostly like a hash.
|
6
|
+
#
|
7
|
+
# By default, the hashlike methods iterate over the receiver attributes:
|
8
|
+
# instance #keys delegates to self.class.keys which calls
|
9
|
+
# receiver_attr_names. If you want to filter our add to the keys list, you
|
10
|
+
# can just override the class-level keys method (and call super, or not):
|
11
|
+
#
|
12
|
+
# def self.keys
|
13
|
+
# super + [:firstname, :lastname] - [:fullname]
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# All methods are defined naturally on [], []= and has_key? -- if you enjoy
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# in addition to the below, by including Enumerable, this also adds
|
20
|
+
#
|
21
|
+
# :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
|
22
|
+
# :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce,
|
23
|
+
# :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop,
|
24
|
+
# :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep,
|
25
|
+
# :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by,
|
26
|
+
# :minmax, :minmax_by, :sort, :sort_by
|
27
|
+
#
|
28
|
+
# As opposed to hash, does *not* define
|
29
|
+
#
|
30
|
+
# default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity
|
31
|
+
# compare_by_identity? rehash
|
32
|
+
#
|
33
|
+
module ActsAsHash
|
34
|
+
|
35
|
+
module InstanceMethods
|
36
|
+
|
37
|
+
# Hashlike#[]
|
38
|
+
#
|
39
|
+
# Element Reference -- Retrieves the value stored for +key+.
|
40
|
+
#
|
41
|
+
# In a normal hash, a default value can be set; none is provided here.
|
42
|
+
#
|
43
|
+
# Delegates to self.send(key)
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# hsh = { :a => 100, :b => 200 }
|
47
|
+
# hsh[:a] # => 100
|
48
|
+
# hsh[:c] # => nil
|
49
|
+
#
|
50
|
+
# @param key [Object] key to retrieve
|
51
|
+
# @return [Object] the value stored for key, nil if missing
|
52
|
+
#
|
53
|
+
def [](key)
|
54
|
+
key = convert_key(key)
|
55
|
+
self.send(key)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Hashlike#[]=
|
59
|
+
# Hashlike#store
|
60
|
+
#
|
61
|
+
# Element Assignment -- Associates the value given by +val+ with the key
|
62
|
+
# given by +key+.
|
63
|
+
#
|
64
|
+
# key should not have its value changed while it is in use as a key. In a
|
65
|
+
# normal hash, a String passed as a key will be duplicated and frozen. No such
|
66
|
+
# guarantee is provided here
|
67
|
+
#
|
68
|
+
# Delegates to self.send("key=", val)
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# hsh = { :a => 100, :b => 200 }
|
72
|
+
# hsh[:a] = 9
|
73
|
+
# hsh[:c] = 4
|
74
|
+
# hsh # => { :a => 9, :b => 200, :c => 4 }
|
75
|
+
#
|
76
|
+
# hsh[key] = val -> val
|
77
|
+
# hsh.store(key, val) -> val
|
78
|
+
#
|
79
|
+
# @param key [Object] key to associate
|
80
|
+
# @param val [Object] value to associate it with
|
81
|
+
# @return [Object]
|
82
|
+
#
|
83
|
+
def []=(key, val)
|
84
|
+
key = convert_key(key)
|
85
|
+
self.send("#{key}=", val)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Hashlike#delete
|
89
|
+
#
|
90
|
+
# Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
|
91
|
+
# optional code block is given and the key is not found, pass in the key and
|
92
|
+
# return the result of +block+.
|
93
|
+
#
|
94
|
+
# In a normal hash, a default value can be set; none is provided here.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# hsh = { :a => 100, :b => 200 }
|
98
|
+
# hsh.delete(:a) # => 100
|
99
|
+
# hsh.delete(:z) # => nil
|
100
|
+
# hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
|
101
|
+
#
|
102
|
+
# @overload hsh.delete(key) -> val
|
103
|
+
# @param key [Object] key to remove
|
104
|
+
# @return [Object, Nil] the removed object, nil if missing
|
105
|
+
#
|
106
|
+
# @overload hsh.delete(key){|key| block } -> val
|
107
|
+
# @param key [Object] key to remove
|
108
|
+
# @yield [Object] called (with key) if key is missing
|
109
|
+
# @yieldparam key
|
110
|
+
# @return [Object, Nil] the removed object, or if missing, the return value
|
111
|
+
# of the block
|
112
|
+
#
|
113
|
+
def delete(key, &block)
|
114
|
+
key = convert_key(key)
|
115
|
+
if has_key?(key)
|
116
|
+
val = self[key]
|
117
|
+
self.send(:remove_instance_variable, "@#{key}")
|
118
|
+
val
|
119
|
+
elsif block_given?
|
120
|
+
block.call(key)
|
121
|
+
else
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Hashlike#keys
|
127
|
+
#
|
128
|
+
# Returns a new array populated with the keys from this hashlike.
|
129
|
+
#
|
130
|
+
# @see Hashlike#values.
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
|
134
|
+
# hsh.keys # => [:a, :b, :c, :d]
|
135
|
+
#
|
136
|
+
# @return [Array] list of keys
|
137
|
+
#
|
138
|
+
def keys
|
139
|
+
members & instance_variables.map{|s| convert_key(s[1..-1]) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def members
|
143
|
+
self.class.members
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Returns a hash with each key set to its associated value.
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# my_hshlike = MyHashlike.new
|
151
|
+
# my_hshlike[:a] = 100; my_hshlike[:b] = 200
|
152
|
+
# my_hshlike.to_hash # => { :a => 100, :b => 200 }
|
153
|
+
#
|
154
|
+
# @return [Hash] a new Hash instance, with each key set to its associated value.
|
155
|
+
#
|
156
|
+
def to_hash
|
157
|
+
{}.tap do |hsh|
|
158
|
+
each_pair do |key, val|
|
159
|
+
hsh[key] = val.respond_to?(:to_hash) ? val.to_hash : val
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
module ClassMethods
|
167
|
+
# By default, the hashlike methods iterate over the receiver attributes.
|
168
|
+
# If you want to filter our add to the keys list, override this method
|
169
|
+
#
|
170
|
+
# @example
|
171
|
+
# def self.keys
|
172
|
+
# super + [:firstname, :lastname] - [:fullname]
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
def keys
|
176
|
+
receiver_attr_names
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
protected
|
181
|
+
|
182
|
+
def convert_key(key)
|
183
|
+
raise ArgumentError, "Keys for #{self.class} must be symbols, strings or respond to #to_sym" unless key.respond_to?(:to_sym)
|
184
|
+
key.to_sym
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.included base
|
188
|
+
base.class_eval do
|
189
|
+
include Gorillib::Hashlike
|
190
|
+
extend ClassMethods
|
191
|
+
include InstanceMethods
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Receiver
|
2
|
+
#
|
3
|
+
# adds methods to load and store from json, yaml or magic
|
4
|
+
#
|
5
|
+
# This will require 'json' UNLESS you have already included something (so if
|
6
|
+
# you want to say require 'yajl' then do that first).
|
7
|
+
#
|
8
|
+
module ActsAsLoadable
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def receive_json stream
|
12
|
+
receive(JSON.load(stream))
|
13
|
+
end
|
14
|
+
|
15
|
+
def receive_yaml stream
|
16
|
+
receive(YAML.load(stream))
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# The file is loaded with
|
21
|
+
# * YAML if the filename ends in .yaml or .yml
|
22
|
+
# * JSON otherwise
|
23
|
+
#
|
24
|
+
def receive_from_file filename
|
25
|
+
stream = File.open(filename)
|
26
|
+
(filename =~ /.ya?ml$/) ? receive_yaml(stream) : receive_json(stream)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def merge_from_file! filename
|
31
|
+
other_obj = self.class.receive_from_file(filename)
|
32
|
+
tree_merge! other_obj
|
33
|
+
end
|
34
|
+
|
35
|
+
# put all the things in ClassMethods at class level
|
36
|
+
def self.included base
|
37
|
+
require 'yaml'
|
38
|
+
require 'json' unless defined?(JSON)
|
39
|
+
base.extend ClassMethods
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
en:
|
2
|
+
errors:
|
3
|
+
# The default format to use in full error messages.
|
4
|
+
format: "%{attribute} %{message}"
|
5
|
+
|
6
|
+
# The values :model, :attribute and :value are always available for interpolation
|
7
|
+
# The value :count is available when applicable. Can be used for pluralization.
|
8
|
+
messages:
|
9
|
+
inclusion: "is not included in the list"
|
10
|
+
exclusion: "is reserved"
|
11
|
+
invalid: "is invalid"
|
12
|
+
confirmation: "doesn't match confirmation"
|
13
|
+
accepted: "must be accepted"
|
14
|
+
empty: "can't be empty"
|
15
|
+
blank: "can't be blank"
|
16
|
+
too_long: "is too long (maximum is %{count} characters)"
|
17
|
+
too_short: "is too short (minimum is %{count} characters)"
|
18
|
+
wrong_length: "is the wrong length (should be %{count} characters)"
|
19
|
+
not_a_number: "is not a number"
|
20
|
+
not_an_integer: "must be an integer"
|
21
|
+
greater_than: "must be greater than %{count}"
|
22
|
+
greater_than_or_equal_to: "must be greater than or equal to %{count}"
|
23
|
+
equal_to: "must be equal to %{count}"
|
24
|
+
less_than: "must be less than %{count}"
|
25
|
+
less_than_or_equal_to: "must be less than or equal to %{count}"
|
26
|
+
odd: "must be odd"
|
27
|
+
even: "must be even"
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Receiver
|
2
|
+
def tree_diff(other)
|
3
|
+
diff_hsh = {}
|
4
|
+
other = other.symbolize_keys if other.respond_to?(:symbolize_keys)
|
5
|
+
each do |k, v|
|
6
|
+
case
|
7
|
+
when v.is_a?(Array) && other[k].is_a?(Array)
|
8
|
+
val = v.tree_diff(other[k])
|
9
|
+
diff_hsh[k] = val unless val.blank?
|
10
|
+
when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
|
11
|
+
val = v.tree_diff(other[k])
|
12
|
+
diff_hsh[k] = val unless val.blank?
|
13
|
+
else
|
14
|
+
diff_hsh[k] = v unless v == other[k]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
other_hsh = other.dup.delete_if{|k, v| has_key?(k) }
|
18
|
+
diff_hsh.merge!(other_hsh)
|
19
|
+
end
|
20
|
+
|
21
|
+
module ActsAsHash
|
22
|
+
def <=>(other)
|
23
|
+
return 1 if other.blank?
|
24
|
+
each_key do |k|
|
25
|
+
if has_key?(k) && other.has_key?(k)
|
26
|
+
cmp = self[k] <=> other[k]
|
27
|
+
return cmp unless cmp == 0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Array
|
36
|
+
def tree_diff(other)
|
37
|
+
arr = dup
|
38
|
+
if other.length > arr.length then arr = arr + ([nil] * (other.length - arr.length)) end
|
39
|
+
diff_ary = arr.zip(other).map do |arr_el, other_el|
|
40
|
+
if arr_el.respond_to?(:tree_diff) && other_el.respond_to?(:to_hash)
|
41
|
+
arr_el.tree_diff(other_el)
|
42
|
+
else
|
43
|
+
(arr_el == other_el) ? nil : [arr_el, other_el]
|
44
|
+
end
|
45
|
+
end.reject(&:blank?)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Hash
|
50
|
+
# Returns a hash that represents the difference between two hashes.
|
51
|
+
#
|
52
|
+
# Examples:
|
53
|
+
#
|
54
|
+
# {1 => 2}.tree_diff(1 => 2) # => {}
|
55
|
+
# {1 => 2}.tree_diff(1 => 3) # => {1 => 2}
|
56
|
+
# {}.tree_diff(1 => 2) # => {1 => 2}
|
57
|
+
# {1 => 2, 3 => 4}.tree_diff(1 => 2) # => {3 => 4}
|
58
|
+
def tree_diff(other)
|
59
|
+
diff_hsh = self.dup
|
60
|
+
each do |k, v|
|
61
|
+
case
|
62
|
+
when v.is_a?(Array) && other[k].is_a?(Array)
|
63
|
+
diff_hsh[k] = v.tree_diff(other[k])
|
64
|
+
diff_hsh.delete(k) if diff_hsh[k].blank?
|
65
|
+
when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
|
66
|
+
diff_hsh[k] = v.tree_diff(other[k])
|
67
|
+
diff_hsh.delete(k) if diff_hsh[k].blank?
|
68
|
+
else diff_hsh.delete(k) if v == other[k]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
other_hsh = other.dup.delete_if{|k, v| has_key?(k) || has_key?(k.to_s) }
|
72
|
+
diff_hsh.merge!(other_hsh)
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Receiver
|
2
|
+
|
3
|
+
# An array of strings describing any ways this fails validation
|
4
|
+
def validation_errors
|
5
|
+
errors = []
|
6
|
+
if (ma = missing_attrs).present?
|
7
|
+
errors << "Missing values for {#{ma.join(",")}}"
|
8
|
+
end
|
9
|
+
errors
|
10
|
+
end
|
11
|
+
|
12
|
+
# returns a list of required but missing attributes
|
13
|
+
def missing_attrs
|
14
|
+
missing = []
|
15
|
+
self.class.required_rcvrs.each do |name, info|
|
16
|
+
missing << name if (not attr_set?(name))
|
17
|
+
end
|
18
|
+
missing
|
19
|
+
end
|
20
|
+
|
21
|
+
# methods become class-level
|
22
|
+
module ClassMethods
|
23
|
+
|
24
|
+
# class method gives info for all receiver attributes with required => true
|
25
|
+
def required_rcvrs
|
26
|
+
receiver_attrs.select{|name, info| info[:required] }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'gorillib/hashlike'
|
2
|
+
require 'gorillib/hashlike/tree_merge'
|
3
|
+
require 'gorillib/receiver'
|
4
|
+
require 'gorillib/receiver/acts_as_hash'
|
5
|
+
require 'gorillib/receiver/acts_as_loadable'
|
6
|
+
require 'gorillib/receiver/active_model_shim'
|
7
|
+
|
8
|
+
module Gorillib
|
9
|
+
module ReceiverModel
|
10
|
+
def self.included base
|
11
|
+
base.class_eval do
|
12
|
+
include Receiver
|
13
|
+
include Receiver::ActsAsHash
|
14
|
+
include Receiver::ActsAsLoadable
|
15
|
+
include Gorillib::Hashlike
|
16
|
+
include Gorillib::Hashlike::TreeMerge
|
17
|
+
include Receiver::ActiveModelShim
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Gorillib
|
2
|
+
module Struct
|
3
|
+
#
|
4
|
+
# Make a Struct behave mostly like a hash.
|
5
|
+
#
|
6
|
+
# By default, the hashlike methods iterate over the receiver attributes:
|
7
|
+
# instance #keys delegates to self.class.keys which calls
|
8
|
+
# receiver_attr_names. If you want to filter our add to the keys list, you
|
9
|
+
# can just override the class-level keys method (and call super, or not):
|
10
|
+
#
|
11
|
+
# def self.keys
|
12
|
+
# super + [:firstname, :lastname] - [:fullname]
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# in addition to the below, by including Enumerable, this also adds
|
16
|
+
#
|
17
|
+
# :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object,
|
18
|
+
# :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce,
|
19
|
+
# :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop,
|
20
|
+
# :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep,
|
21
|
+
# :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by,
|
22
|
+
# :minmax, :minmax_by, :sort, :sort_by
|
23
|
+
#
|
24
|
+
# As opposed to hash, does *not* define
|
25
|
+
#
|
26
|
+
# default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity
|
27
|
+
# compare_by_identity? rehash
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# StructUsingHashlike = Struct.new(:a, :b, :c, :z) do
|
31
|
+
# include Gorillib::Struct::ActsAsHash
|
32
|
+
# include Gorillib::Hashlike
|
33
|
+
# end
|
34
|
+
# foo = StructUsingHashlike.new(1,2,3)
|
35
|
+
# foo.to_hash # => { :a => 1, :b => 2, :c => 3, :z => nil }
|
36
|
+
#
|
37
|
+
module ActsAsHash
|
38
|
+
|
39
|
+
# Hashlike#delete
|
40
|
+
#
|
41
|
+
# Deletes and returns the value from +hsh+ whose key is equal to +key+. If the
|
42
|
+
# optional code block is given and the key is not found, pass in the key and
|
43
|
+
# return the result of +block+.
|
44
|
+
#
|
45
|
+
# In a normal hash, a default value can be set; none is provided here.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# hsh = { :a => 100, :b => 200 }
|
49
|
+
# hsh.delete(:a) # => 100
|
50
|
+
# hsh.delete(:z) # => nil
|
51
|
+
# hsh.delete(:z){|el| "#{el} not found" } # => "z not found"
|
52
|
+
#
|
53
|
+
# @overload hsh.delete(key) -> val
|
54
|
+
# @param key [Object] key to remove
|
55
|
+
# @return [Object, Nil] the removed object, nil if missing
|
56
|
+
#
|
57
|
+
# @overload hsh.delete(key){|key| block } -> val
|
58
|
+
# @param key [Object] key to remove
|
59
|
+
# @yield [Object] called (with key) if key is missing
|
60
|
+
# @yieldparam key
|
61
|
+
# @return [Object, Nil] the removed object, or if missing, the return value
|
62
|
+
# of the block
|
63
|
+
#
|
64
|
+
def delete(key, &block)
|
65
|
+
if has_key?(key)
|
66
|
+
val = self[key]
|
67
|
+
self[key] = nil
|
68
|
+
self.send(:remove_instance_variable, "@#{key}") if instance_variables.include?("@#{key}")
|
69
|
+
val
|
70
|
+
elsif block_given?
|
71
|
+
block.call(key)
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Hashlike#keys
|
78
|
+
#
|
79
|
+
# Returns a new array populated with the keys from this hashlike.
|
80
|
+
#
|
81
|
+
# @see Hashlike#values.
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# hsh = { :a => 100, :b => 200, :c => 300, :d => 400 }
|
85
|
+
# hsh.keys # => [:a, :b, :c, :d]
|
86
|
+
#
|
87
|
+
# @return [Array] list of keys
|
88
|
+
#
|
89
|
+
def keys
|
90
|
+
@keys ||= members.map{|key| convert_key(key) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def empty?
|
94
|
+
keys.select{|key| not self[key].nil? }.empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def convert_key(key)
|
98
|
+
return key if key.is_a?(Fixnum)
|
99
|
+
key.respond_to?(:to_sym) ? key.to_sym : key
|
100
|
+
end
|
101
|
+
|
102
|
+
def size
|
103
|
+
length
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|