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.
Files changed (69) hide show
  1. data/.gitignore +3 -0
  2. data/.gitmodules +3 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +2 -16
  5. data/Rakefile +2 -74
  6. data/away/aliasing_spec.rb +180 -0
  7. data/away/confidence.rb +17 -0
  8. data/away/stub_module.rb +33 -0
  9. data/gorillib.gemspec +31 -246
  10. data/lib/gorillib/collection/model_collection.rb +1 -0
  11. data/lib/gorillib/data_munging.rb +0 -1
  12. data/lib/gorillib/hashlike/slice.rb +2 -0
  13. data/lib/gorillib/model/field.rb +2 -2
  14. data/lib/gorillib/model/serialization.rb +9 -4
  15. data/lib/gorillib/model/serialization/csv.rb +1 -0
  16. data/lib/gorillib/model/serialization/lines.rb +2 -0
  17. data/lib/gorillib/model/serialization/tsv.rb +1 -0
  18. data/lib/gorillib/pathname.rb +1 -1
  19. data/lib/gorillib/pathname/utils.rb +6 -0
  20. data/lib/gorillib/string/inflector.rb +1 -1
  21. data/lib/gorillib/system.rb +1 -0
  22. data/lib/gorillib/system/runner.rb +36 -0
  23. data/lib/gorillib/type/ip_address.rb +2 -2
  24. data/lib/gorillib/version.rb +3 -0
  25. data/old/lib/gorillib/hash/indifferent_access.rb +207 -0
  26. data/old/lib/gorillib/hash/tree_merge.rb +4 -0
  27. data/old/lib/gorillib/hashlike/tree_merge.rb +49 -0
  28. data/old/lib/gorillib/metaprogramming/cattr_accessor.rb +79 -0
  29. data/old/lib/gorillib/metaprogramming/mattr_accessor.rb +61 -0
  30. data/old/lib/gorillib/receiver.rb +402 -0
  31. data/old/lib/gorillib/receiver/active_model_shim.rb +32 -0
  32. data/old/lib/gorillib/receiver/acts_as_hash.rb +195 -0
  33. data/old/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
  34. data/old/lib/gorillib/receiver/locale/en.yml +27 -0
  35. data/old/lib/gorillib/receiver/tree_diff.rb +74 -0
  36. data/old/lib/gorillib/receiver/validations.rb +30 -0
  37. data/old/lib/gorillib/receiver_model.rb +21 -0
  38. data/old/lib/gorillib/struct/acts_as_hash.rb +108 -0
  39. data/old/lib/gorillib/struct/hashlike_iteration.rb +0 -0
  40. data/old/spec/gorillib/hash/indifferent_access_spec.rb +391 -0
  41. data/old/spec/gorillib/metaprogramming/cattr_accessor_spec.rb +43 -0
  42. data/old/spec/gorillib/metaprogramming/mattr_accessor_spec.rb +45 -0
  43. data/old/spec/gorillib/receiver/receiver/acts_as_hash_spec.rb +295 -0
  44. data/old/spec/gorillib/receiver_spec.rb +551 -0
  45. data/old/spec/gorillib/struct/acts_as_hash_fuzz_spec.rb +71 -0
  46. data/old/spec/gorillib/struct/acts_as_hash_spec.rb +422 -0
  47. data/spec/gorillib/array/compact_blank_spec.rb +2 -2
  48. data/spec/gorillib/collection_spec.rb +6 -6
  49. data/spec/gorillib/factories_spec.rb +2 -2
  50. data/spec/gorillib/hashlike_spec.rb +2 -1
  51. data/spec/gorillib/model/defaults_spec.rb +3 -3
  52. data/spec/gorillib/model/serialization/csv_spec.rb +35 -0
  53. data/spec/gorillib/model/serialization/tsv_spec.rb +20 -4
  54. data/spec/gorillib/model/serialization_spec.rb +3 -3
  55. data/spec/spec_helper.rb +6 -1
  56. data/spec/support/factory_test_helpers.rb +2 -2
  57. data/spec/support/gorillib_test_helpers.rb +4 -4
  58. data/spec/support/hashlike_fuzzing_helper.rb +1 -15
  59. data/spec/support/hashlike_helper.rb +5 -1
  60. data/spec/support/model_test_helpers.rb +12 -1
  61. metadata +192 -168
  62. data/notes/HOWTO.md +0 -22
  63. data/notes/bucket.md +0 -155
  64. data/notes/builder.md +0 -170
  65. data/notes/collection.md +0 -81
  66. data/notes/factories.md +0 -86
  67. data/notes/model-overlay.md +0 -209
  68. data/notes/model.md +0 -135
  69. data/notes/structured-data-classes.md +0 -127
@@ -40,6 +40,7 @@ module Gorillib
40
40
  item.receive!(attrs, &block)
41
41
  item
42
42
  else
43
+ attrs = attrs.attributes if attrs.is_a? Gorillib::Model
43
44
  attrs = attrs.merge(key_method => label) if key_method && label
44
45
  receive_item(label, attrs, &block)
45
46
  end
@@ -12,4 +12,3 @@ require 'gorillib/model/serialization'
12
12
  require 'gorillib/model/serialization/csv'
13
13
  require 'gorillib/model/serialization/tsv'
14
14
  require 'gorillib/model/serialization/json'
15
- require 'gorillib/model/indexable'
@@ -16,6 +16,8 @@ module Gorillib
16
16
  # valid_keys = [:mass, :velocity, :time]
17
17
  # search(options.slice(*valid_keys))
18
18
  #
19
+ # Note: Compatible with Rails 4.0 Active Support
20
+ #
19
21
  # @return key/value pairs for keys in self and allowed
20
22
  def slice(*allowed)
21
23
  allowed.map!{|key| convert_key(key) } if respond_to?(:convert_key, true)
@@ -45,8 +45,8 @@ module Gorillib
45
45
  @model = model
46
46
  @name = name.to_sym
47
47
  @type = Gorillib::Factory.factory_for(type, type_opts)
48
- default_visabilities = visibilities
49
- @visibilities = default_visabilities.merge( options.extract!(*default_visabilities.keys) )
48
+ default_visibilities = visibilities
49
+ @visibilities = default_visibilities.merge( options.extract!(*default_visibilities.keys).compact )
50
50
  @doc = options.delete(:name){ "#{name} field" }
51
51
  receive!(options)
52
52
  end
@@ -1,13 +1,16 @@
1
+ require_relative '../serialization/to_wire'
2
+
1
3
  class Array
2
4
  def to_tsv
3
- join("\t")
5
+ to_wire.join("\t")
4
6
  end
5
7
  end
6
8
 
7
9
  module Gorillib
8
10
  module Model
11
+
9
12
  def to_wire(options={})
10
- attributes.merge(:_type => self.class.typename).inject({}) do |acc, (key,attr)|
13
+ compact_attributes.merge(:_type => self.class.typename).inject({}) do |acc, (key,attr)|
11
14
  acc[key] = attr.respond_to?(:to_wire) ? attr.to_wire(options) : attr
12
15
  acc
13
16
  end
@@ -18,8 +21,10 @@ module Gorillib
18
21
  MultiJson.dump(to_wire(options), options)
19
22
  end
20
23
 
21
- def to_tsv
22
- attribute_values.map(&:to_s).join("\t")
24
+ def to_tsv(options={})
25
+ attributes.map do |key, attr|
26
+ attr.respond_to?(:to_wire) ? attr.to_wire(options) : attr
27
+ end.join("\t")
23
28
  end
24
29
 
25
30
  module ClassMethods
@@ -1,5 +1,6 @@
1
1
  require 'csv'
2
2
  require 'gorillib/pathname'
3
+ require_relative '../serialization'
3
4
 
4
5
  module Gorillib
5
6
  module Model
@@ -1,3 +1,5 @@
1
+ require 'gorillib/pathname'
2
+
1
3
  module Gorillib
2
4
  module Model
3
5
 
@@ -1,4 +1,5 @@
1
1
  require_relative './lines'
2
+ require_relative '../serialization'
2
3
 
3
4
  module Gorillib
4
5
  module Model
@@ -66,7 +66,7 @@ module Gorillib
66
66
  #
67
67
  def relpath_to(*pathsegs)
68
68
  ArgumentError.arity_at_least!(pathsegs, 1)
69
- pathsegs = pathsegs.map{|ps| expand_pathseg(ps) }.flatten
69
+ pathsegs = pathsegs.flatten.map{|ps| expand_pathseg(ps) }.flatten
70
70
  self.new(File.join(*pathsegs)).cleanpath(true)
71
71
  end
72
72
  alias_method :relative_path_to, :relpath_to
@@ -14,6 +14,12 @@ class Pathname
14
14
  return self
15
15
  end
16
16
 
17
+ # Like find, but returns an enumerable
18
+ #
19
+ def find_all
20
+ Enumerator.new{|yielder| find{|path| yielder << path } }
21
+ end
22
+
17
23
  #
18
24
  # Executes the block (passing the opened file) if the file does not
19
25
  # exist. Ignores the block otherwise. The block is required.
@@ -114,7 +114,7 @@ module Gorillib::Inflector
114
114
  #
115
115
  def constantize(str)
116
116
  unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ str
117
- raise NameError, "#{self.inspect} is not a valid constant name!"
117
+ raise NameError, "#{str.inspect} is not a valid constant name!"
118
118
  end
119
119
 
120
120
  Object.module_eval("::#{$1}", __FILE__, __LINE__)
@@ -0,0 +1 @@
1
+ require_relative 'system/runner'
@@ -0,0 +1,36 @@
1
+ #You must include childprocess in your gemfile.
2
+ #Gorillib (intentionally) does not do so.
3
+ require 'childprocess'
4
+ require 'tempfile'
5
+
6
+ module Gorillib
7
+ module System
8
+ module Runner
9
+ extend self
10
+
11
+ def run(args, options={})
12
+ options = options.reverse_merge(mirror_io: false)
13
+ process = ChildProcess.build(*args)
14
+ out = Tempfile.new('gorillib-runner-out')
15
+ err = Tempfile.new('gorillib-runner-err')
16
+ process.io.stdout = out
17
+ process.io.stderr = err
18
+ process.start
19
+ process.wait
20
+ begin
21
+ out.rewind ; err.rewind
22
+ res = [out.read, err.read, process.exit_code]
23
+ if options[:mirror_io]
24
+ $stdout.write res[0]
25
+ $stderr.write res[1]
26
+ end
27
+ ensure
28
+ out.close ; err.close
29
+ out.unlink ; err.unlink
30
+ end
31
+ res
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -22,8 +22,8 @@ module IpAddresslike
22
22
  packed | (ONES >> bitness)
23
23
  end
24
24
 
25
- def hex_str
26
- packed.to_s(16)
25
+ def to_hex
26
+ "%08x" % packed
27
27
  end
28
28
 
29
29
  def to_s
@@ -0,0 +1,3 @@
1
+ module Gorillib
2
+ VERSION = '0.5.2'
3
+ end
@@ -0,0 +1,207 @@
1
+ require 'gorillib/hash/keys'
2
+
3
+ # This class has dubious semantics and we only have it so that
4
+ # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
5
+ # and they get the same value for both keys.
6
+
7
+ module Gorillib
8
+ class HashWithIndifferentAccess < Hash
9
+ def extractable_options?
10
+ true
11
+ end
12
+
13
+ def with_indifferent_access
14
+ dup
15
+ end
16
+
17
+ def initialize(constructor = {})
18
+ if constructor.is_a?(Hash)
19
+ super()
20
+ update(constructor)
21
+ else
22
+ super(constructor)
23
+ end
24
+ end
25
+
26
+ def default(key = nil)
27
+ if include?(converted = convert_key(key))
28
+ self[converted]
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def self.new_from_hash_copying_default(hash)
35
+ new(hash).tap do |new_hash|
36
+ new_hash.default = hash.default
37
+ end
38
+ end
39
+
40
+ alias_method(:regular_writer, :[]=) unless method_defined?(:regular_writer)
41
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
42
+
43
+ # Assigns a new value to the hash:
44
+ #
45
+ # hash = HashWithIndifferentAccess.new
46
+ # hash[:key] = "value"
47
+ #
48
+ def []=(key, value)
49
+ regular_writer(convert_key(key), convert_value(value))
50
+ end
51
+
52
+ alias_method :store, :[]=
53
+
54
+ # Updates the instantized hash with values from the second:
55
+ #
56
+ # hash_1 = HashWithIndifferentAccess.new
57
+ # hash_1[:key] = "value"
58
+ #
59
+ # hash_2 = HashWithIndifferentAccess.new
60
+ # hash_2[:key] = "New Value!"
61
+ #
62
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
63
+ #
64
+ def update(other_hash)
65
+ raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair)
66
+ if other_hash.is_a? HashWithIndifferentAccess
67
+ super(other_hash)
68
+ else
69
+ other_hash.each_pair{|key, value| regular_writer(convert_key(key), convert_value(value)) }
70
+ self
71
+ end
72
+ end
73
+
74
+ alias_method :merge!, :update
75
+
76
+ # Checks the hash for a key matching the argument passed in:
77
+ #
78
+ # hash = HashWithIndifferentAccess.new
79
+ # hash["key"] = "value"
80
+ # hash.key? :key # => true
81
+ # hash.key? "key" # => true
82
+ #
83
+ def key?(key)
84
+ super(convert_key(key))
85
+ end
86
+
87
+ alias_method :include?, :key?
88
+ alias_method :has_key?, :key?
89
+ alias_method :member?, :key?
90
+
91
+ # Fetches the value for the specified key, same as doing hash[key]
92
+ def fetch(key, *extras)
93
+ super(convert_key(key), *extras)
94
+ end
95
+
96
+ # Returns an array of the values at the specified indices:
97
+ #
98
+ # hash = HashWithIndifferentAccess.new
99
+ # hash[:a] = "x"
100
+ # hash[:b] = "y"
101
+ # hash.values_at("a", "b") # => ["x", "y"]
102
+ #
103
+ def values_at(*indices)
104
+ indices.collect {|key| self[convert_key(key)]}
105
+ end
106
+
107
+ # Returns an exact copy of the hash.
108
+ def dup
109
+ self.class.new(self).tap do |new_hash|
110
+ new_hash.default = default
111
+ end
112
+ end
113
+
114
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
115
+ # Does not overwrite the existing hash.
116
+ def merge(hash)
117
+ self.dup.update(hash)
118
+ end
119
+
120
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
121
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
122
+ def reverse_merge(other_hash)
123
+ super self.class.new_from_hash_copying_default(other_hash)
124
+ end
125
+
126
+ def reverse_merge!(other_hash)
127
+ replace(reverse_merge( other_hash ))
128
+ end
129
+
130
+ # Removes a specified key from the hash.
131
+ def delete(key)
132
+ super(convert_key(key))
133
+ end
134
+
135
+ def stringify_keys!; self end
136
+ def stringify_keys; dup end
137
+ undef_method :symbolize_keys! if method_defined?(:symbolize_keys!)
138
+ def symbolize_keys; to_hash.symbolize_keys end
139
+ def to_options!; self end
140
+
141
+ # Convert to a Hash with String keys.
142
+ def to_hash
143
+ Hash.new(default).merge!(self)
144
+ end
145
+
146
+ def assoc key
147
+ key = convert_key(key)
148
+ return unless has_key?(key)
149
+ [key, self[key]]
150
+ end
151
+
152
+ def rassoc val
153
+ key = key(val) or return
154
+ [key, self[key]]
155
+ end
156
+
157
+ protected
158
+ def convert_key(key)
159
+ key.kind_of?(Symbol) ? key.to_s : key
160
+ end
161
+
162
+ def convert_value(value)
163
+ if value.is_a? Hash
164
+ value.nested_under_indifferent_access
165
+ elsif value.is_a?(Array)
166
+ value.dup.replace(value.map{|e| convert_value(e) })
167
+ else
168
+ value
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+
175
+ module Gorillib
176
+ class HashWithIndifferentSymbolKeys < Gorillib::HashWithIndifferentAccess
177
+
178
+ def convert_key key
179
+ return key if key.is_a?(Fixnum)
180
+ key.respond_to?(:to_sym) ? key.to_sym : key
181
+ end
182
+ end
183
+ end
184
+
185
+ class Hash
186
+
187
+ # Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
188
+ #
189
+ # {:a => 1}.with_indifferent_access["a"] # => 1
190
+ #
191
+ def with_indifferent_access
192
+ Gorillib::HashWithIndifferentAccess.new_from_hash_copying_default(self)
193
+ end
194
+
195
+ # Called when object is nested under an object that receives
196
+ # #with_indifferent_access. This method with be called on the current object
197
+ # by the enclosing object and is aliased to #with_indifferent_access by
198
+ # default. Subclasses of Hash may overwrite this method to return +self+ if
199
+ # converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
200
+ # desirable.
201
+ #
202
+ # b = {:b => 1}
203
+ # {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
204
+ #
205
+ alias nested_under_indifferent_access with_indifferent_access
206
+ end
207
+
@@ -0,0 +1,4 @@
1
+ require 'gorillib/hashlike/tree_merge'
2
+ class Hash
3
+ include Gorillib::Hashlike::TreeMerge
4
+ end
@@ -0,0 +1,49 @@
1
+ module Gorillib
2
+ module Hashlike
3
+ module TreeMerge
4
+
5
+ # Recursively merges hashlike objects
6
+ #
7
+ # For each key in keys,
8
+ # * if block_given? and yield(key,self_val,other_val) returns non-nil, set that
9
+ # * if self is missing value for key, receive the attribute.
10
+ # * if self's attribute is an Array, append to it.
11
+ # * if self's value responds to tree_merge!, deep merge it.
12
+ # * if self's value responds_to merge!, merge! it.
13
+ # * otherwise, receive the value from other_hash
14
+ #
15
+ def tree_merge!(other_hash)
16
+ return self if other_hash.blank?
17
+ [self.keys, other_hash.keys].flatten.uniq.each do |key|
18
+ # get other's val if any
19
+ if other_hash.has_key?(key.to_sym) then other_val = other_hash[key.to_sym]
20
+ elsif other_hash.has_key?(key.to_s) then other_val = other_hash[key.to_s]
21
+ else next ; end
22
+ # get self val if any
23
+ self_val = self[key]
24
+ # get block resolved result if any
25
+ if block_given? && yield(key, self_val, other_val)
26
+ next
27
+ end
28
+ # p ['hash tree_merge', key, self_val.respond_to?(:tree_merge!), self_val, '***************', other_val]
29
+ #
30
+ case
31
+ when other_val.nil? then next
32
+ when (not has_key?(key)) then self[key] = other_val
33
+ when self_val.is_a?(Array) then self[key] += other_val
34
+ when self_val.respond_to?(:tree_merge!) then self[key] = self_val.tree_merge!(other_val)
35
+ when self_val.respond_to?(:merge!) then self[key] = self_val.merge!(other_val)
36
+ else self[key] = other_val
37
+ end
38
+ end
39
+ self
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
46
+
47
+ class Hash
48
+ include Gorillib::Hashlike::TreeMerge
49
+ end