gorillib 0.1.11 → 0.4.0pre

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 (143) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -2
  3. data/.yardopts +9 -0
  4. data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
  5. data/Gemfile +21 -14
  6. data/Guardfile +19 -0
  7. data/{LICENSE.textile → LICENSE.md} +43 -29
  8. data/README.md +47 -52
  9. data/Rakefile +31 -30
  10. data/TODO.md +32 -0
  11. data/VERSION +1 -1
  12. data/examples/builder/ironfan.rb +133 -0
  13. data/examples/model/simple.rb +17 -0
  14. data/gorillib.gemspec +106 -86
  15. data/lib/alt/kernel/call_stack.rb +56 -0
  16. data/lib/gorillib/array/wrap.rb +53 -0
  17. data/lib/gorillib/base.rb +3 -3
  18. data/lib/gorillib/builder/field.rb +5 -0
  19. data/lib/gorillib/builder.rb +260 -0
  20. data/lib/gorillib/collection/has_collection.rb +31 -0
  21. data/lib/gorillib/collection.rb +129 -0
  22. data/lib/gorillib/configurable.rb +28 -0
  23. data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
  24. data/lib/gorillib/exception/confidence.rb +17 -0
  25. data/lib/gorillib/exception/raisers.rb +78 -0
  26. data/lib/gorillib/hash/mash.rb +202 -0
  27. data/lib/gorillib/hashlike/slice.rb +53 -19
  28. data/lib/gorillib/hashlike.rb +5 -3
  29. data/lib/gorillib/io/system_helpers.rb +30 -0
  30. data/lib/gorillib/logger/log.rb +18 -0
  31. data/lib/gorillib/metaprogramming/concern.rb +124 -0
  32. data/lib/gorillib/model/active_model_conversion.rb +68 -0
  33. data/lib/gorillib/model/active_model_naming.rb +87 -0
  34. data/lib/gorillib/model/active_model_shim.rb +33 -0
  35. data/lib/gorillib/model/base.rb +341 -0
  36. data/lib/gorillib/model/defaults.rb +71 -0
  37. data/lib/gorillib/model/errors.rb +14 -0
  38. data/lib/gorillib/model/factories.rb +372 -0
  39. data/lib/gorillib/model/field.rb +146 -0
  40. data/lib/gorillib/model/named_schema.rb +53 -0
  41. data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
  42. data/lib/gorillib/model/record_schema.rb +9 -0
  43. data/lib/gorillib/model/serialization.rb +23 -0
  44. data/lib/gorillib/model/validate.rb +22 -0
  45. data/lib/gorillib/model.rb +23 -0
  46. data/lib/gorillib/pathname.rb +78 -0
  47. data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
  48. data/lib/gorillib/some.rb +11 -9
  49. data/lib/gorillib/string/constantize.rb +21 -14
  50. data/lib/gorillib/string/inflections.rb +6 -76
  51. data/lib/gorillib/string/inflector.rb +192 -0
  52. data/lib/gorillib/string/simple_inflector.rb +267 -0
  53. data/lib/gorillib/type/extended.rb +52 -0
  54. data/lib/gorillib/utils/capture_output.rb +28 -0
  55. data/lib/gorillib/utils/console.rb +131 -0
  56. data/lib/gorillib/utils/nuke_constants.rb +9 -0
  57. data/lib/gorillib/utils/stub_module.rb +33 -0
  58. data/spec/examples/builder/ironfan_spec.rb +37 -0
  59. data/spec/extlib/hash_spec.rb +64 -0
  60. data/spec/extlib/mash_spec.rb +312 -0
  61. data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
  62. data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
  63. data/spec/gorillib/builder_spec.rb +187 -0
  64. data/spec/gorillib/collection_spec.rb +20 -0
  65. data/spec/gorillib/configurable_spec.rb +62 -0
  66. data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
  67. data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
  68. data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
  69. data/spec/gorillib/exception/raisers_spec.rb +60 -0
  70. data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
  71. data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
  72. data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
  73. data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
  74. data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
  75. data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
  76. data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
  77. data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
  78. data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
  79. data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
  80. data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
  81. data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
  82. data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
  83. data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
  84. data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
  85. data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
  86. data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
  87. data/spec/gorillib/model/record/defaults_spec.rb +108 -0
  88. data/spec/gorillib/model/record/factories_spec.rb +321 -0
  89. data/spec/gorillib/model/record/overlay_spec.rb +46 -0
  90. data/spec/gorillib/model/serialization_spec.rb +48 -0
  91. data/spec/gorillib/model_spec.rb +281 -0
  92. data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
  93. data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
  94. data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
  95. data/spec/{object → gorillib/object}/try_spec.rb +3 -2
  96. data/spec/gorillib/pathname_spec.rb +114 -0
  97. data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
  98. data/spec/{string → gorillib/string}/human_spec.rb +2 -2
  99. data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
  100. data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
  101. data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
  102. data/spec/gorillib/type/extended_spec.rb +120 -0
  103. data/spec/gorillib/utils/capture_output_spec.rb +71 -0
  104. data/spec/spec_helper.rb +8 -11
  105. data/spec/support/gorillib_test_helpers.rb +66 -0
  106. data/spec/support/hashlike_fuzzing_helper.rb +31 -33
  107. data/spec/support/hashlike_helper.rb +3 -3
  108. data/spec/support/model_test_helpers.rb +81 -0
  109. data/spec/support/shared_examples/included_module.rb +20 -0
  110. metadata +177 -158
  111. data/lib/gorillib/array/average.rb +0 -13
  112. data/lib/gorillib/array/sorted_median.rb +0 -11
  113. data/lib/gorillib/array/sorted_percentile.rb +0 -11
  114. data/lib/gorillib/array/sorted_sample.rb +0 -12
  115. data/lib/gorillib/dsl_object.rb +0 -64
  116. data/lib/gorillib/hash/indifferent_access.rb +0 -207
  117. data/lib/gorillib/hash/tree_merge.rb +0 -4
  118. data/lib/gorillib/hashlike/tree_merge.rb +0 -49
  119. data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
  120. data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
  121. data/lib/gorillib/receiver/active_model_shim.rb +0 -32
  122. data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
  123. data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
  124. data/lib/gorillib/receiver/locale/en.yml +0 -27
  125. data/lib/gorillib/receiver/tree_diff.rb +0 -74
  126. data/lib/gorillib/receiver/validations.rb +0 -30
  127. data/lib/gorillib/receiver.rb +0 -402
  128. data/lib/gorillib/receiver_model.rb +0 -21
  129. data/lib/gorillib/struct/acts_as_hash.rb +0 -108
  130. data/notes/fancy_hashes_and_receivers.textile +0 -120
  131. data/notes/hash_rdocs.textile +0 -97
  132. data/spec/array/average_spec.rb +0 -24
  133. data/spec/array/sorted_median_spec.rb +0 -18
  134. data/spec/array/sorted_percentile_spec.rb +0 -24
  135. data/spec/array/sorted_sample_spec.rb +0 -28
  136. data/spec/dsl_object_spec.rb +0 -99
  137. data/spec/hash/indifferent_access_spec.rb +0 -391
  138. data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
  139. data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
  140. data/spec/receiver/acts_as_hash_spec.rb +0 -295
  141. data/spec/receiver_spec.rb +0 -551
  142. data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
  143. data/spec/struct/acts_as_hash_spec.rb +0 -422
@@ -0,0 +1,202 @@
1
+ class Hash
2
+
3
+ # Convert to Mash. This class has semantics of ActiveSupport's
4
+ # HashWithIndifferentAccess and we only have it so that people can write
5
+ # params[:key] instead of params['key'].
6
+ #
7
+ # @return [Mash] This hash as a Mash for string or symbol key access.
8
+ def to_mash
9
+ hash = Mash.new(self)
10
+ hash.default = default
11
+ hash
12
+ end
13
+
14
+ ##
15
+ # Create a hash with *only* key/value pairs in receiver and +allowed+
16
+ #
17
+ # { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 }
18
+ #
19
+ # @param allowed [Array[String, Symbol]] The hash keys to include.
20
+ #
21
+ # @return [Hash] A new hash with only the selected keys.
22
+ #
23
+ # @api public
24
+ def only(*allowed)
25
+ hash = {}
26
+ allowed.each {|k| hash[k] = self[k] if self.has_key?(k) }
27
+ hash
28
+ end
29
+
30
+ ##
31
+ # Create a hash with all key/value pairs in receiver *except* +rejected+
32
+ #
33
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
34
+ # #=> { :two => 2, :three => 3 }
35
+ #
36
+ # @param rejected [Array[String, Symbol]] The hash keys to exclude.
37
+ #
38
+ # @return [Hash] A new hash without the selected keys.
39
+ #
40
+ # @api public
41
+ def except(*rejected)
42
+ hash = self.dup
43
+ rejected.each {|k| hash.delete(k) }
44
+ hash
45
+ end
46
+ end
47
+
48
+ # This class has dubious semantics and we only have it so that people can write
49
+ # params[:key] instead of params['key'].
50
+ class Mash < Hash
51
+
52
+ # @param constructor [Object]
53
+ # The default value for the mash. Defaults to an empty hash.
54
+ #
55
+ # @overload [Alternatives]
56
+ # If constructor is a Hash, a new mash will be created based on the keys of
57
+ # the hash and no default value will be set.
58
+ def initialize(constructor = {})
59
+ if constructor.is_a?(Hash)
60
+ super()
61
+ update(constructor)
62
+ else
63
+ super(constructor)
64
+ end
65
+ end
66
+
67
+ # @param key<Object> The default value for the mash. Defaults to nil.
68
+ #
69
+ # @overload [Alternatives]
70
+ # If key is a Symbol and it is a key in the mash, then the default value will
71
+ # be set to the value matching the key.
72
+ def default(key = nil)
73
+ if key.is_a?(Symbol) && include?(key = key.to_s)
74
+ self[key]
75
+ else
76
+ super
77
+ end
78
+ end
79
+
80
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
81
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
82
+
83
+ # @param key<Object> The key to set.
84
+ # @param value<Object>
85
+ # The value to set the key to.
86
+ #
87
+ # @see Mash#convert_key
88
+ # @see Mash#convert_value
89
+ def []=(key, value)
90
+ regular_writer(convert_key(key), convert_value(value))
91
+ end
92
+
93
+ # @param other_hash<Hash>
94
+ # A hash to update values in the mash with. The keys and the values will be
95
+ # converted to Mash format.
96
+ #
97
+ # @return [Mash] The updated mash.
98
+ def update(other_hash)
99
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
100
+ self
101
+ end
102
+
103
+ alias_method :merge!, :update
104
+
105
+ # @param key<Object> The key to check for. This will be run through convert_key.
106
+ #
107
+ # @return [Boolean] True if the key exists in the mash.
108
+ def key?(key)
109
+ super(convert_key(key))
110
+ end
111
+
112
+ # def include? def has_key? def member?
113
+ alias_method :include?, :key?
114
+ alias_method :has_key?, :key?
115
+ alias_method :member?, :key?
116
+
117
+ # @param <Object> key The key to fetch. This will be run through convert_key.
118
+ # @param <Array> extras Default value.
119
+ #
120
+ # @return [Object] The value at key or the default value.
121
+ def fetch(key, *extras)
122
+ super(convert_key(key), *extras)
123
+ end
124
+
125
+ # @param indices [Array]
126
+ # The keys to retrieve values for. These will be run through +convert_key+.
127
+ #
128
+ # @return [Array] The values at each of the provided keys
129
+ def values_at(*indices)
130
+ indices.collect {|key| self[convert_key(key)]}
131
+ end
132
+
133
+ # @param hash<Hash> The hash to merge with the mash.
134
+ #
135
+ # @return [Mash] A new mash with the hash values merged in.
136
+ def merge(hash)
137
+ self.dup.update(hash)
138
+ end
139
+
140
+ # @param key<Object>
141
+ # The key to delete from the mash.\
142
+ def delete(key)
143
+ super(convert_key(key))
144
+ end
145
+
146
+ # @param [Array[(String, Symbol)]] rejected The mash keys to exclude.
147
+ #
148
+ # @return [Mash] A new mash without the selected keys.
149
+ #
150
+ # @example
151
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
152
+ # #=> { "two" => 2, "three" => 3 }
153
+ def except(*rejected)
154
+ super(*rejected.map {|k| convert_key(k)})
155
+ end
156
+
157
+ # Used to provide the same interface as Hash.
158
+ #
159
+ # @return [Mash] This mash unchanged.
160
+ def stringify_keys!; self end
161
+
162
+ # @return [Hash] The mash as a Hash with symbolized keys.
163
+ def symbolize_keys
164
+ h = Hash.new(default)
165
+ each { |key, val| h[key.to_sym] = val }
166
+ h
167
+ end
168
+
169
+ # @return [Hash] The mash as a Hash with string keys.
170
+ def to_hash
171
+ Hash.new(default).merge(self)
172
+ end
173
+
174
+ protected
175
+ # @param key [Object] The key to convert.
176
+ #
177
+ # @return [Object]
178
+ # The converted key. If the key was a symbol, it will be converted to a
179
+ # string.
180
+ #
181
+ # @api private
182
+ def convert_key(key)
183
+ key.kind_of?(Symbol) ? key.to_s : key
184
+ end
185
+
186
+ # @param value [Object] The value to convert.
187
+ #
188
+ # @return [Object]
189
+ # The converted value. A Hash or an Array of hashes, will be converted to
190
+ # their Mash equivalents.
191
+ #
192
+ # @api private
193
+ def convert_value(value)
194
+ if value.class == Hash
195
+ value.to_mash
196
+ elsif value.is_a?(Array)
197
+ value.collect { |e| convert_value(e) }
198
+ else
199
+ value
200
+ end
201
+ end
202
+ end
@@ -1,13 +1,14 @@
1
1
  module Gorillib
2
2
  module Hashlike
3
3
  module Slice
4
- # Slice a hash to include only the given allowed_keys. This is useful for
5
- # limiting an options hash to valid keys before passing to a method:
4
+ # Slice a hash to include only the given allowed_keys.
6
5
  #
6
+ # @return the sliced hash
7
+ #
8
+ # @example limit an options hash to valid keys before passing to a method:
7
9
  # def search(criteria = {})
8
10
  # assert_valid_keys(:mass, :velocity, :time)
9
11
  # end
10
- #
11
12
  # search(options.slice(:mass, :velocity, :time))
12
13
  #
13
14
  # If you have an array of keys you want to limit to, you should splat them:
@@ -21,8 +22,10 @@ module Gorillib
21
22
  hash
22
23
  end unless method_defined?(:slice)
23
24
 
24
- # Replaces the hash with only the given allowed_keys.
25
- # Returns a hash containing the removed key/value pairs
25
+ # Replace the hash with only the given allowed_keys.
26
+ #
27
+ # @return a hash containing the removed key/value pairs
28
+ #
26
29
  # @example
27
30
  # hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
28
31
  # hsh.slice!(:a, :b)
@@ -37,21 +40,9 @@ module Gorillib
37
40
  omit
38
41
  end unless method_defined?(:slice!)
39
42
 
40
- # This also works, and doesn't require #replace method, but is uglier and
41
- # wasn't written by Railsians. I'm not sure that slice! is interesting if
42
- # you're a duck-typed Hash but not is_a?(Hash), so we'll just leave it at the
43
- # active_record implementation.
44
- #
45
- # def slice!(*allowed_keys)
46
- # allowed_keys = allowed_keys.map!{|key| convert_key(key) } if respond_to?(:convert_key)
47
- # omit_keys = self.keys - allowed_keys
48
- # omit = slice(*omit_keys)
49
- # omit_keys.each{|k| delete(k) }
50
- # omit
51
- # end
52
-
53
43
  # Removes the given allowed_keys from the hash
54
- # Returns a hash containing the removed key/value pairs
44
+ #
45
+ # @return a hash containing the removed key/value pairs
55
46
  #
56
47
  # @example
57
48
  # hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
@@ -62,6 +53,49 @@ module Gorillib
62
53
  def extract!(*allowed_keys)
63
54
  slice!(*self.keys - allowed_keys)
64
55
  end unless method_defined?(:extract!)
56
+
57
+ # Return a hash that includes everything but the given keys.
58
+ #
59
+ # @example Exclude a blacklist of parameters
60
+ # @person.update_attributes(params[:person].except(:admin))
61
+ #
62
+ # If the receiver responds to +convert_key+, the method is called on each of the
63
+ # arguments. This allows +except+ to play nice with hashes with indifferent access
64
+ # for instance:
65
+ #
66
+ # @example mash, hash, it does the right thing:
67
+ # {:a => 1}.to_mash.except(:a) # => {}
68
+ # {:a => 1}.to_mash.except('a') # => {}
69
+ #
70
+ def except(*keys)
71
+ dup.except!(*keys)
72
+ end
73
+
74
+ # Replaces the hash without the given keys.
75
+ def except!(*keys)
76
+ keys.each{|key| delete(key) }
77
+ self
78
+ end
79
+
80
+ def only(*allowed)
81
+ dup.only!(*allowed)
82
+ end
83
+
84
+ # Create a hash with *only* key/value pairs in receiver and +allowed+
85
+ #
86
+ # @example Limit a set of parameters to everything but a few known toggles:
87
+ # { :one => 1, :two => 2, :three => 3 }.only(:one) #=> { :one => 1 }
88
+ #
89
+ # @param [Array[String, Symbol]] allowed The hash keys to include.
90
+ #
91
+ # @return [Hash] A new hash with only the selected keys.
92
+ #
93
+ # @api public
94
+ def only!(*allowed)
95
+ allowed = allowed.to_set
96
+ select!{|key, val| allowed.include?(key) }
97
+ end
98
+
65
99
  end
66
100
  end
67
101
  end
@@ -199,7 +199,7 @@ module Gorillib
199
199
  # hsh.values_at(:c, :a, :c, :z, :a)
200
200
  # # => [300, 100, 300, nil, 100]
201
201
  #
202
- # @param *allowed_keys [Object] the keys to retrieve.
202
+ # @param allowed_keys [Object] the keys to retrieve.
203
203
  # @return [Array] the values, in order according to allowed_keys.
204
204
  #
205
205
  def values_at(*allowed_keys)
@@ -304,7 +304,7 @@ module Gorillib
304
304
  # hsh.values_of(:c, :a, :c, :z, :a)
305
305
  # # => [300, 100, 300, nil, 100]
306
306
  #
307
- # @param *allowed_keys [Object] the keys to retrieve.
307
+ # @param allowed_keys [Object] the keys to retrieve.
308
308
  # @return [Array] the values, in order according to allowed_keys.
309
309
  #
310
310
  def values_of(*allowed_keys)
@@ -794,7 +794,9 @@ module Gorillib
794
794
  # hsh.flatten(nil)
795
795
  # # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
796
796
  #
797
- # @param level [Integer] the level of recursion to flatten, 0 by default.
797
+ # @overload flatten
798
+ # @overload flatten(level)
799
+ # @param level [Integer] the level of recursion to flatten, 0 by default.
798
800
  # @return [Array] the flattened key-value array.
799
801
  #
800
802
  def flatten(*args)
@@ -0,0 +1,30 @@
1
+ module Gorillib::CheckedPopen
2
+ module_function
3
+
4
+ def checked_popen(command, mode, fail_action, io_class=IO)
5
+ check_child_exit_status do
6
+ io_class.popen(command, mode) do |process|
7
+ yield(process)
8
+ end
9
+ end
10
+ rescue Errno::EPIPE
11
+ fail_action.call
12
+ end
13
+
14
+ # @private
15
+ NO_EXIT_STATUS = OpenStruct.new(:exitstatus => 0)
16
+
17
+ def check_child_exit_status
18
+ result = yield
19
+ status = $? || NO_EXIT_STATUS
20
+ unless [0, 172].include?(status.exitstatus)
21
+ raise ArgumentError, "Command exited with status '#{status.exitstatus}'"
22
+ end
23
+ result
24
+ end
25
+
26
+ end
27
+
28
+ ::IO.class_eval do
29
+ include Gorillib::CheckedPopen
30
+ end
@@ -7,6 +7,24 @@ require 'logger'
7
7
  #
8
8
  ::Log = Logger.new($stderr) unless defined?(::Log)
9
9
 
10
+ # unless defined?(Log)
11
+ # require 'log4r'
12
+ # Log = Log4r::Logger.new('wukong')
13
+ # Log.outputters = Log4r::Outputter.stderr
14
+ # # require 'logger'
15
+ # # Log = Logger.new(STDERR)
16
+ # end
17
+
18
+ # require 'log_buddy'; LogBuddy.init :log_to_stdout => false, :logger => Log
19
+ # LogBuddy::Utils.module_eval do
20
+ # def arg_and_blk_debug(arg, blk)
21
+ # result = eval(arg, blk.binding)
22
+ # result_str = obj_to_string(result, :quote_strings => true)
23
+ # LogBuddy.debug(%[#{arg} = #{result_str}])
24
+ # end
25
+ # end
26
+
27
+
10
28
  def Log.dump *args
11
29
  self.debug([
12
30
  args.map(&:inspect),
@@ -0,0 +1,124 @@
1
+ module Gorillib
2
+ # A typical module looks like this:
3
+ #
4
+ # module M
5
+ # def self.included(base)
6
+ # base.extend ClassMethods
7
+ # scope :disabled, where(:disabled => true)
8
+ # end
9
+ #
10
+ # module ClassMethods
11
+ # ...
12
+ # end
13
+ # end
14
+ #
15
+ # By using <tt>Gorillib::Concern</tt> the above module could instead be written as:
16
+ #
17
+ # require 'active_support/concern'
18
+ #
19
+ # module M
20
+ # extend Gorillib::Concern
21
+ #
22
+ # included do
23
+ # scope :disabled, where(:disabled => true)
24
+ # end
25
+ #
26
+ # module ClassMethods
27
+ # ...
28
+ # end
29
+ # end
30
+ #
31
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module and a +Bar+
32
+ # module which depends on the former, we would typically write the following:
33
+ #
34
+ # module Foo
35
+ # def self.included(base)
36
+ # base.class_eval do
37
+ # def self.method_injected_by_foo
38
+ # ...
39
+ # end
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # module Bar
45
+ # def self.included(base)
46
+ # base.method_injected_by_foo
47
+ # end
48
+ # end
49
+ #
50
+ # class Host
51
+ # include Foo # We need to include this dependency for Bar
52
+ # include Bar # Bar is the module that Host really needs
53
+ # end
54
+ #
55
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We could try to hide
56
+ # these from +Host+ directly including +Foo+ in +Bar+:
57
+ #
58
+ # module Bar
59
+ # include Foo
60
+ # def self.included(base)
61
+ # base.method_injected_by_foo
62
+ # end
63
+ # end
64
+ #
65
+ # class Host
66
+ # include Bar
67
+ # end
68
+ #
69
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt> is the +Bar+ module,
70
+ # not the +Host+ class. With <tt>Gorillib::Concern</tt>, module dependencies are properly resolved:
71
+ #
72
+ # require 'active_support/concern'
73
+ #
74
+ # module Foo
75
+ # extend Gorillib::Concern
76
+ # included do
77
+ # class_eval do
78
+ # def self.method_injected_by_foo
79
+ # ...
80
+ # end
81
+ # end
82
+ # end
83
+ # end
84
+ #
85
+ # module Bar
86
+ # extend Gorillib::Concern
87
+ # include Foo
88
+ #
89
+ # included do
90
+ # self.method_injected_by_foo
91
+ # end
92
+ # end
93
+ #
94
+ # class Host
95
+ # include Bar # works, Bar takes care now of its dependencies
96
+ # end
97
+ #
98
+ module Concern
99
+ def self.extended(base)
100
+ base.instance_variable_set("@_dependencies", [])
101
+ end
102
+
103
+ def append_features(base)
104
+ if base.instance_variable_defined?("@_dependencies")
105
+ base.instance_variable_get("@_dependencies") << self
106
+ return false
107
+ else
108
+ return false if base < self
109
+ @_dependencies.each { |dep| base.send(:include, dep) }
110
+ super
111
+ base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
112
+ base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
113
+ end
114
+ end
115
+
116
+ def included(base = nil, &block)
117
+ if base.nil?
118
+ @_included_block = block
119
+ else
120
+ super
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,68 @@
1
+ module Gorillib::Model
2
+ # == Active Model Conversions
3
+ #
4
+ # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
5
+ #
6
+ # Let's take for example this non-persisted object.
7
+ #
8
+ # class ContactMessage
9
+ # include ActiveModel::Conversion
10
+ #
11
+ # # ContactMessage are never persisted in the DB
12
+ # def persisted?
13
+ # false
14
+ # end
15
+ # end
16
+ #
17
+ # cm = ContactMessage.new
18
+ # cm.to_model == self # => true
19
+ # cm.to_key # => nil
20
+ # cm.to_param # => nil
21
+ # cm.to_path # => "contact_messages/contact_message"
22
+ #
23
+ module Conversion
24
+ extend Gorillib::Concern
25
+
26
+ # If your object is already designed to implement all of the Active Model
27
+ # you can use the default <tt>:to_model</tt> implementation, which simply
28
+ # returns self.
29
+ #
30
+ # If your model does not act like an Active Model object, then you should
31
+ # define <tt>:to_model</tt> yourself returning a proxy object that wraps
32
+ # your object with Active Model compliant methods.
33
+ def to_model
34
+ self
35
+ end
36
+
37
+ # Returns an Enumerable of all key attributes if any is set, regardless
38
+ # if the object is persisted or not.
39
+ #
40
+ # Note the default implementation uses persisted? just because all objects
41
+ # in Ruby 1.8.x responds to <tt>:id</tt>.
42
+ def to_key
43
+ persisted? ? [id] : nil
44
+ end
45
+
46
+ # Returns a string representing the object's key suitable for use in URLs,
47
+ # or nil if <tt>persisted?</tt> is false.
48
+ def to_param
49
+ persisted? ? to_key.join('-') : nil
50
+ end
51
+
52
+ # Returns a string identifying the path associated with the object.
53
+ # ActionPack uses this to find a suitable partial to represent the object.
54
+ def to_partial_path
55
+ self.class._to_partial_path
56
+ end
57
+
58
+ module ClassMethods #:nodoc:
59
+ # Provide a class level cache for the to_path. This is an
60
+ # internal method and should not be accessed directly.
61
+ def _to_partial_path #:nodoc:
62
+ @_to_partial_path ||= begin
63
+ "#{model_name.collection}/#{model_name.element}".freeze
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,87 @@
1
+ module Gorillib::Model
2
+ class Name < String
3
+ attr_accessor :namespace
4
+ attr_reader :singular, :element, :collection, :partial_path, :param_key, :i18n_key
5
+
6
+ alias_method :cache_key, :collection
7
+
8
+ class_attribute :inflector
9
+ self.inflector ||= Gorillib::String::Inflector
10
+
11
+ def initialize(klass, namespace = nil, name = nil)
12
+ name ||= klass.name
13
+ raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank?
14
+
15
+ super(name)
16
+
17
+ @klass = klass
18
+ @singular = _singularize(self).freeze
19
+ @element = inflector.underscore(inflector.demodulize(self)).freeze
20
+ @human = inflector.humanize(@element).freeze
21
+ @i18n_key = inflector.underscore(self).to_sym
22
+ #
23
+ self.namespace = namespace
24
+ self.plural = inflector.pluralize(@singular)
25
+ end
26
+
27
+ def plural=(str)
28
+ @plural = str.dup.freeze
29
+ @collection = inflector.underscore(@plural).freeze
30
+ @partial_path = "#{@collection}/#{@element}".freeze
31
+ str
32
+ end
33
+
34
+ def namespace=(ns)
35
+ if ns.present?
36
+ @unnamespaced = self.sub(/^#{ns.name}::/, '')
37
+ @param_key = _singularize(@unnamespaced).freeze
38
+ else
39
+ @unnamespaced = nil
40
+ @param_key = @singular.freeze
41
+ end
42
+ end
43
+
44
+ def singular_route_key
45
+ inflector.singularize(route_key).freeze
46
+ end
47
+
48
+ def route_key
49
+ rk = (namespace ? inflector.pluralize(param_key) : plural.dup)
50
+ rk << "_index" if plural == singular
51
+ rk.freeze
52
+ end
53
+
54
+ private
55
+
56
+ def _singularize(string, replacement='_')
57
+ inflector.underscore(string).tr('/', replacement)
58
+ end
59
+ end
60
+
61
+ # == Active Model Naming
62
+ #
63
+ # Creates a +model_name+ method on your object.
64
+ #
65
+ # To implement, just extend ActiveModel::Naming in your object:
66
+ #
67
+ # class BookCover
68
+ # extend ActiveModel::Naming
69
+ # end
70
+ #
71
+ # BookCover.model_name # => "BookCover"
72
+ # BookCover.model_name.human # => "Book cover"
73
+ #
74
+ # BookCover.model_name.i18n_key # => :book_cover
75
+ # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
76
+ #
77
+ # Providing the functionality that ActiveModel::Naming provides in your object
78
+ # is required to pass the Active Model Lint test. So either extending the provided
79
+ # method below, or rolling your own is required.
80
+ module Naming
81
+ # Returns a Name object for module, which can be used to retrieve all kinds
82
+ # of naming-related information.
83
+ def model_name
84
+ @_model_name ||= Gorillib::Model::Name.new(self, namespace)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,33 @@
1
+ require "active_model"
2
+
3
+ module Gorillib
4
+ module Model
5
+
6
+ # Provides the minimum functionality to pass the ActiveModel lint tests
7
+ #
8
+ # @example Usage
9
+ # class Person
10
+ # include Gorillib::Model::ActiveModelShim
11
+ # end
12
+ #
13
+ module ActiveModelShim
14
+ extend Gorillib::Concern
15
+ extend ActiveModel::Naming
16
+ include Gorillib::Model::Conversion
17
+ include ActiveModel::Validations
18
+
19
+ # @return [false]
20
+ def persisted?
21
+ false
22
+ end
23
+
24
+ def attribute_method?(attr_name)
25
+ self.class.has_field?(attr_name)
26
+ end
27
+
28
+ module ClassMethods
29
+ end # ActiveModelShim::ClassMethods
30
+ end # ActiveModelShim
31
+
32
+ end
33
+ end