gorillib 0.5.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|