gorillib 0.5.0 → 0.5.2

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