ardm 0.0.1

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 (179) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +35 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +21 -0
  5. data/README.md +70 -0
  6. data/Rakefile +4 -0
  7. data/ardm.gemspec +29 -0
  8. data/db/.gitignore +1 -0
  9. data/lib/ardm/active_record/associations.rb +80 -0
  10. data/lib/ardm/active_record/base.rb +49 -0
  11. data/lib/ardm/active_record/dirty.rb +25 -0
  12. data/lib/ardm/active_record/hooks.rb +31 -0
  13. data/lib/ardm/active_record/inheritance.rb +37 -0
  14. data/lib/ardm/active_record/is/state_machine.rb +21 -0
  15. data/lib/ardm/active_record/is.rb +22 -0
  16. data/lib/ardm/active_record/not_found.rb +7 -0
  17. data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
  18. data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
  19. data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
  20. data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
  21. data/lib/ardm/active_record/predicate_builder.rb +19 -0
  22. data/lib/ardm/active_record/property.rb +357 -0
  23. data/lib/ardm/active_record/query.rb +108 -0
  24. data/lib/ardm/active_record/record.rb +70 -0
  25. data/lib/ardm/active_record/relation.rb +83 -0
  26. data/lib/ardm/active_record/repository.rb +38 -0
  27. data/lib/ardm/active_record/serialization.rb +164 -0
  28. data/lib/ardm/active_record/storage_names.rb +28 -0
  29. data/lib/ardm/active_record/validations.rb +111 -0
  30. data/lib/ardm/active_record.rb +43 -0
  31. data/lib/ardm/data_mapper/not_found.rb +5 -0
  32. data/lib/ardm/data_mapper/record.rb +41 -0
  33. data/lib/ardm/data_mapper.rb +5 -0
  34. data/lib/ardm/env.rb +5 -0
  35. data/lib/ardm/property/api_key.rb +30 -0
  36. data/lib/ardm/property/bcrypt_hash.rb +31 -0
  37. data/lib/ardm/property/binary.rb +23 -0
  38. data/lib/ardm/property/boolean.rb +29 -0
  39. data/lib/ardm/property/class.rb +19 -0
  40. data/lib/ardm/property/comma_separated_list.rb +28 -0
  41. data/lib/ardm/property/csv.rb +35 -0
  42. data/lib/ardm/property/date.rb +12 -0
  43. data/lib/ardm/property/datetime.rb +12 -0
  44. data/lib/ardm/property/decimal.rb +38 -0
  45. data/lib/ardm/property/discriminator.rb +65 -0
  46. data/lib/ardm/property/enum.rb +51 -0
  47. data/lib/ardm/property/epoch_time.rb +38 -0
  48. data/lib/ardm/property/file_path.rb +25 -0
  49. data/lib/ardm/property/flag.rb +65 -0
  50. data/lib/ardm/property/float.rb +18 -0
  51. data/lib/ardm/property/integer.rb +24 -0
  52. data/lib/ardm/property/invalid_value_error.rb +22 -0
  53. data/lib/ardm/property/ip_address.rb +35 -0
  54. data/lib/ardm/property/json.rb +49 -0
  55. data/lib/ardm/property/lookup.rb +29 -0
  56. data/lib/ardm/property/numeric.rb +40 -0
  57. data/lib/ardm/property/object.rb +36 -0
  58. data/lib/ardm/property/paranoid_boolean.rb +18 -0
  59. data/lib/ardm/property/paranoid_datetime.rb +17 -0
  60. data/lib/ardm/property/regexp.rb +22 -0
  61. data/lib/ardm/property/serial.rb +16 -0
  62. data/lib/ardm/property/slug.rb +29 -0
  63. data/lib/ardm/property/string.rb +40 -0
  64. data/lib/ardm/property/support/dirty_minder.rb +169 -0
  65. data/lib/ardm/property/support/flags.rb +41 -0
  66. data/lib/ardm/property/support/paranoid_base.rb +78 -0
  67. data/lib/ardm/property/text.rb +11 -0
  68. data/lib/ardm/property/time.rb +12 -0
  69. data/lib/ardm/property/uri.rb +27 -0
  70. data/lib/ardm/property/uuid.rb +65 -0
  71. data/lib/ardm/property/validation.rb +208 -0
  72. data/lib/ardm/property/yaml.rb +38 -0
  73. data/lib/ardm/property.rb +891 -0
  74. data/lib/ardm/property_set.rb +152 -0
  75. data/lib/ardm/query/expression.rb +85 -0
  76. data/lib/ardm/query/ext/symbol.rb +37 -0
  77. data/lib/ardm/query/operator.rb +64 -0
  78. data/lib/ardm/record.rb +1 -0
  79. data/lib/ardm/support/assertions.rb +8 -0
  80. data/lib/ardm/support/deprecate.rb +12 -0
  81. data/lib/ardm/support/descendant_set.rb +89 -0
  82. data/lib/ardm/support/equalizer.rb +48 -0
  83. data/lib/ardm/support/ext/array.rb +22 -0
  84. data/lib/ardm/support/ext/blank.rb +25 -0
  85. data/lib/ardm/support/ext/hash.rb +67 -0
  86. data/lib/ardm/support/ext/module.rb +47 -0
  87. data/lib/ardm/support/ext/object.rb +57 -0
  88. data/lib/ardm/support/ext/string.rb +24 -0
  89. data/lib/ardm/support/ext/try_dup.rb +12 -0
  90. data/lib/ardm/support/hook.rb +405 -0
  91. data/lib/ardm/support/lazy_array.rb +451 -0
  92. data/lib/ardm/support/local_object_space.rb +13 -0
  93. data/lib/ardm/support/logger.rb +201 -0
  94. data/lib/ardm/support/mash.rb +176 -0
  95. data/lib/ardm/support/naming_conventions.rb +90 -0
  96. data/lib/ardm/support/ordered_set.rb +380 -0
  97. data/lib/ardm/support/subject.rb +33 -0
  98. data/lib/ardm/support/subject_set.rb +250 -0
  99. data/lib/ardm/version.rb +3 -0
  100. data/lib/ardm.rb +56 -0
  101. data/spec/fixtures/api_user.rb +11 -0
  102. data/spec/fixtures/article.rb +22 -0
  103. data/spec/fixtures/bookmark.rb +14 -0
  104. data/spec/fixtures/invention.rb +5 -0
  105. data/spec/fixtures/network_node.rb +23 -0
  106. data/spec/fixtures/person.rb +17 -0
  107. data/spec/fixtures/software_package.rb +22 -0
  108. data/spec/fixtures/ticket.rb +12 -0
  109. data/spec/fixtures/tshirt.rb +15 -0
  110. data/spec/integration/api_key_spec.rb +25 -0
  111. data/spec/integration/bcrypt_hash_spec.rb +45 -0
  112. data/spec/integration/comma_separated_list_spec.rb +85 -0
  113. data/spec/integration/dirty_minder_spec.rb +197 -0
  114. data/spec/integration/enum_spec.rb +79 -0
  115. data/spec/integration/epoch_time_spec.rb +59 -0
  116. data/spec/integration/file_path_spec.rb +158 -0
  117. data/spec/integration/flag_spec.rb +72 -0
  118. data/spec/integration/ip_address_spec.rb +151 -0
  119. data/spec/integration/json_spec.rb +70 -0
  120. data/spec/integration/slug_spec.rb +65 -0
  121. data/spec/integration/uri_spec.rb +136 -0
  122. data/spec/integration/uuid_spec.rb +102 -0
  123. data/spec/integration/yaml_spec.rb +85 -0
  124. data/spec/public/property/binary_spec.rb +41 -0
  125. data/spec/public/property/boolean_spec.rb +30 -0
  126. data/spec/public/property/class_spec.rb +28 -0
  127. data/spec/public/property/date_spec.rb +22 -0
  128. data/spec/public/property/date_time_spec.rb +22 -0
  129. data/spec/public/property/decimal_spec.rb +23 -0
  130. data/spec/public/property/discriminator_spec.rb +133 -0
  131. data/spec/public/property/float_spec.rb +22 -0
  132. data/spec/public/property/integer_spec.rb +22 -0
  133. data/spec/public/property/object_spec.rb +103 -0
  134. data/spec/public/property/serial_spec.rb +22 -0
  135. data/spec/public/property/string_spec.rb +22 -0
  136. data/spec/public/property/text_spec.rb +23 -0
  137. data/spec/public/property/time_spec.rb +22 -0
  138. data/spec/public/property_spec.rb +316 -0
  139. data/spec/rcov.opts +6 -0
  140. data/spec/schema.rb +86 -0
  141. data/spec/semipublic/property/binary_spec.rb +14 -0
  142. data/spec/semipublic/property/boolean_spec.rb +48 -0
  143. data/spec/semipublic/property/class_spec.rb +36 -0
  144. data/spec/semipublic/property/date_spec.rb +44 -0
  145. data/spec/semipublic/property/date_time_spec.rb +47 -0
  146. data/spec/semipublic/property/decimal_spec.rb +83 -0
  147. data/spec/semipublic/property/discriminator_spec.rb +22 -0
  148. data/spec/semipublic/property/float_spec.rb +83 -0
  149. data/spec/semipublic/property/integer_spec.rb +83 -0
  150. data/spec/semipublic/property/lookup_spec.rb +27 -0
  151. data/spec/semipublic/property/serial_spec.rb +14 -0
  152. data/spec/semipublic/property/string_spec.rb +14 -0
  153. data/spec/semipublic/property/text_spec.rb +30 -0
  154. data/spec/semipublic/property/time_spec.rb +49 -0
  155. data/spec/semipublic/property_spec.rb +51 -0
  156. data/spec/shared/flags_shared_spec.rb +36 -0
  157. data/spec/shared/identity_function_group.rb +5 -0
  158. data/spec/shared/public_property_spec.rb +229 -0
  159. data/spec/shared/semipublic_property_spec.rb +159 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +58 -0
  162. data/spec/unit/bcrypt_hash_spec.rb +154 -0
  163. data/spec/unit/csv_spec.rb +139 -0
  164. data/spec/unit/dirty_minder_spec.rb +64 -0
  165. data/spec/unit/enum_spec.rb +125 -0
  166. data/spec/unit/epoch_time_spec.rb +72 -0
  167. data/spec/unit/file_path_spec.rb +75 -0
  168. data/spec/unit/flag_spec.rb +114 -0
  169. data/spec/unit/ip_address_spec.rb +109 -0
  170. data/spec/unit/json_spec.rb +127 -0
  171. data/spec/unit/paranoid_boolean_spec.rb +142 -0
  172. data/spec/unit/paranoid_datetime_spec.rb +149 -0
  173. data/spec/unit/regexp_spec.rb +62 -0
  174. data/spec/unit/uri_spec.rb +64 -0
  175. data/spec/unit/yaml_spec.rb +111 -0
  176. data/tasks/spec.rake +40 -0
  177. data/tasks/yard.rake +9 -0
  178. data/tasks/yardstick.rake +19 -0
  179. metadata +350 -0
@@ -0,0 +1,451 @@
1
+ class LazyArray # borrowed partially from StrokeDB
2
+ include Enumerable
3
+
4
+ attr_reader :head, :tail
5
+
6
+ def first(*args)
7
+ if lazy_possible?(@head, *args)
8
+ @head.first(*args)
9
+ else
10
+ lazy_load
11
+ @array.first(*args)
12
+ end
13
+ end
14
+
15
+ def last(*args)
16
+ if lazy_possible?(@tail, *args)
17
+ @tail.last(*args)
18
+ else
19
+ lazy_load
20
+ @array.last(*args)
21
+ end
22
+ end
23
+
24
+ def at(index)
25
+ if index >= 0 && lazy_possible?(@head, index + 1)
26
+ @head.at(index)
27
+ elsif index < 0 && lazy_possible?(@tail, index.abs)
28
+ @tail.at(index)
29
+ else
30
+ lazy_load
31
+ @array.at(index)
32
+ end
33
+ end
34
+
35
+ def fetch(*args, &block)
36
+ index = args.first
37
+
38
+ if index >= 0 && lazy_possible?(@head, index + 1)
39
+ @head.fetch(*args, &block)
40
+ elsif index < 0 && lazy_possible?(@tail, index.abs)
41
+ @tail.fetch(*args, &block)
42
+ else
43
+ lazy_load
44
+ @array.fetch(*args, &block)
45
+ end
46
+ end
47
+
48
+ def values_at(*args)
49
+ accumulator = []
50
+
51
+ lazy_possible = args.all? do |arg|
52
+ index, length = extract_slice_arguments(arg)
53
+
54
+ if index >= 0 && lazy_possible?(@head, index + length)
55
+ accumulator.concat(head.values_at(*arg))
56
+ elsif index < 0 && lazy_possible?(@tail, index.abs)
57
+ accumulator.concat(tail.values_at(*arg))
58
+ end
59
+ end
60
+
61
+ if lazy_possible
62
+ accumulator
63
+ else
64
+ lazy_load
65
+ @array.values_at(*args)
66
+ end
67
+ end
68
+
69
+ def index(entry)
70
+ (lazy_possible?(@head) && @head.index(entry)) || begin
71
+ lazy_load
72
+ @array.index(entry)
73
+ end
74
+ end
75
+
76
+ def include?(entry)
77
+ (lazy_possible?(@tail) && @tail.include?(entry)) ||
78
+ (lazy_possible?(@head) && @head.include?(entry)) || begin
79
+ lazy_load
80
+ @array.include?(entry)
81
+ end
82
+ end
83
+
84
+ def empty?
85
+ (@tail.nil? || @tail.empty?) &&
86
+ (@head.nil? || @head.empty?) && begin
87
+ lazy_load
88
+ @array.empty?
89
+ end
90
+ end
91
+
92
+ def any?(&block)
93
+ (lazy_possible?(@tail) && @tail.any?(&block)) ||
94
+ (lazy_possible?(@head) && @head.any?(&block)) || begin
95
+ lazy_load
96
+ @array.any?(&block)
97
+ end
98
+ end
99
+
100
+ def [](*args)
101
+ index, length = extract_slice_arguments(*args)
102
+
103
+ if length == 1 && args.size == 1 && args.first.kind_of?(Integer)
104
+ return at(index)
105
+ end
106
+
107
+ if index >= 0 && lazy_possible?(@head, index + length)
108
+ @head[*args]
109
+ elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length)
110
+ @tail[*args]
111
+ else
112
+ lazy_load
113
+ @array[*args]
114
+ end
115
+ end
116
+
117
+ alias_method :slice, :[]
118
+
119
+ def slice!(*args)
120
+ index, length = extract_slice_arguments(*args)
121
+
122
+ if index >= 0 && lazy_possible?(@head, index + length)
123
+ @head.slice!(*args)
124
+ elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length)
125
+ @tail.slice!(*args)
126
+ else
127
+ lazy_load
128
+ @array.slice!(*args)
129
+ end
130
+ end
131
+
132
+ def []=(*args)
133
+ index, length = extract_slice_arguments(*args[0..-2])
134
+
135
+ if index >= 0 && lazy_possible?(@head, index + length)
136
+ @head.[]=(*args)
137
+ elsif index < 0 && lazy_possible?(@tail, index.abs - 1 + length)
138
+ @tail.[]=(*args)
139
+ else
140
+ lazy_load
141
+ @array.[]=(*args)
142
+ end
143
+ end
144
+
145
+ alias_method :splice, :[]=
146
+
147
+ def reverse
148
+ dup.reverse!
149
+ end
150
+
151
+ def reverse!
152
+ # reverse without kicking if possible
153
+ if loaded?
154
+ @array = @array.reverse
155
+ else
156
+ @head, @tail = @tail.reverse, @head.reverse
157
+
158
+ proc = @load_with_proc
159
+
160
+ @load_with_proc = lambda do |v|
161
+ proc.call(v)
162
+ v.instance_variable_get(:@array).reverse!
163
+ end
164
+ end
165
+
166
+ self
167
+ end
168
+
169
+ def <<(entry)
170
+ if loaded?
171
+ lazy_load
172
+ @array << entry
173
+ else
174
+ @tail << entry
175
+ end
176
+ self
177
+ end
178
+
179
+ def concat(other)
180
+ if loaded?
181
+ lazy_load
182
+ @array.concat(other)
183
+ else
184
+ @tail.concat(other)
185
+ end
186
+ self
187
+ end
188
+
189
+ def push(*entries)
190
+ if loaded?
191
+ lazy_load
192
+ @array.push(*entries)
193
+ else
194
+ @tail.push(*entries)
195
+ end
196
+ self
197
+ end
198
+
199
+ def unshift(*entries)
200
+ if loaded?
201
+ lazy_load
202
+ @array.unshift(*entries)
203
+ else
204
+ @head.unshift(*entries)
205
+ end
206
+ self
207
+ end
208
+
209
+ def insert(index, *entries)
210
+ if index >= 0 && lazy_possible?(@head, index)
211
+ @head.insert(index, *entries)
212
+ elsif index < 0 && lazy_possible?(@tail, index.abs - 1)
213
+ @tail.insert(index, *entries)
214
+ else
215
+ lazy_load
216
+ @array.insert(index, *entries)
217
+ end
218
+ self
219
+ end
220
+
221
+ def pop(*args)
222
+ if lazy_possible?(@tail, *args)
223
+ @tail.pop(*args)
224
+ else
225
+ lazy_load
226
+ @array.pop(*args)
227
+ end
228
+ end
229
+
230
+ def shift(*args)
231
+ if lazy_possible?(@head, *args)
232
+ @head.shift(*args)
233
+ else
234
+ lazy_load
235
+ @array.shift(*args)
236
+ end
237
+ end
238
+
239
+ def delete_at(index)
240
+ if index >= 0 && lazy_possible?(@head, index + 1)
241
+ @head.delete_at(index)
242
+ elsif index < 0 && lazy_possible?(@tail, index.abs)
243
+ @tail.delete_at(index)
244
+ else
245
+ lazy_load
246
+ @array.delete_at(index)
247
+ end
248
+ end
249
+
250
+ def delete_if(&block)
251
+ if loaded?
252
+ lazy_load
253
+ @array.delete_if(&block)
254
+ else
255
+ @reapers << block
256
+ @head.delete_if(&block)
257
+ @tail.delete_if(&block)
258
+ end
259
+ self
260
+ end
261
+
262
+ def replace(other)
263
+ mark_loaded
264
+ @array.replace(other)
265
+ self
266
+ end
267
+
268
+ def clear
269
+ mark_loaded
270
+ @array.clear
271
+ self
272
+ end
273
+
274
+ def to_a
275
+ lazy_load
276
+ @array.to_a
277
+ end
278
+
279
+ alias_method :to_ary, :to_a
280
+
281
+ def load_with(&block)
282
+ @load_with_proc = block
283
+ self
284
+ end
285
+
286
+ def loaded?
287
+ @loaded == true
288
+ end
289
+
290
+ def kind_of?(klass)
291
+ super || @array.kind_of?(klass)
292
+ end
293
+
294
+ alias_method :is_a?, :kind_of?
295
+
296
+ def respond_to?(method, include_private = false)
297
+ super || @array.respond_to?(method)
298
+ end
299
+
300
+ def freeze
301
+ if loaded?
302
+ @array.freeze
303
+ else
304
+ @head.freeze
305
+ @tail.freeze
306
+ end
307
+ @frozen = true
308
+ self
309
+ end
310
+
311
+ def frozen?
312
+ @frozen == true
313
+ end
314
+
315
+ def ==(other)
316
+ if equal?(other)
317
+ return true
318
+ end
319
+
320
+ unless other.respond_to?(:to_ary)
321
+ return false
322
+ end
323
+
324
+ # if necessary, convert to something that can be compared
325
+ other = other.to_ary unless other.respond_to?(:[])
326
+
327
+ cmp?(other, :==)
328
+ end
329
+
330
+ def eql?(other)
331
+ if equal?(other)
332
+ return true
333
+ end
334
+
335
+ unless other.class.equal?(self.class)
336
+ return false
337
+ end
338
+
339
+ cmp?(other, :eql?)
340
+ end
341
+
342
+ def lazy_possible?(list, need_length = 1)
343
+ !loaded? && need_length <= list.size
344
+ end
345
+
346
+ private
347
+
348
+ def initialize
349
+ @frozen = false
350
+ @loaded = false
351
+ @load_with_proc = lambda { |v| v }
352
+ @head = []
353
+ @tail = []
354
+ @array = []
355
+ @reapers = []
356
+ end
357
+
358
+ def initialize_copy(original)
359
+ @head = Ardm::Ext.try_dup(@head)
360
+ @tail = Ardm::Ext.try_dup(@tail)
361
+ @array = Ardm::Ext.try_dup(@array)
362
+ end
363
+
364
+ def lazy_load
365
+ return if loaded?
366
+ mark_loaded
367
+ @load_with_proc[self]
368
+ @array.unshift(*@head)
369
+ @array.concat(@tail)
370
+ @head = @tail = nil
371
+ @reapers.each { |r| @array.delete_if(&r) } if @reapers
372
+ @array.freeze if frozen?
373
+ end
374
+
375
+ def mark_loaded
376
+ @loaded = true
377
+ end
378
+
379
+ ##
380
+ # Extract arguments for #slice an #slice! and return index and length
381
+ #
382
+ # @param [Integer, Array(Integer), Range] *args the index,
383
+ # index and length, or range indicating first and last position
384
+ #
385
+ # @return [Integer] the index
386
+ # @return [Integer,NilClass] the length, if any
387
+ #
388
+ # @api private
389
+ def extract_slice_arguments(*args)
390
+ first_arg, second_arg = args
391
+
392
+ if args.size == 2 && first_arg.kind_of?(Integer) && second_arg.kind_of?(Integer)
393
+ return first_arg, second_arg
394
+ elsif args.size == 1
395
+ if first_arg.kind_of?(Integer)
396
+ return first_arg, 1
397
+ elsif first_arg.kind_of?(Range)
398
+ index = first_arg.first
399
+ length = first_arg.last - index
400
+ length += 1 unless first_arg.exclude_end?
401
+ return index, length
402
+ end
403
+ end
404
+
405
+ raise ArgumentError, "arguments may be 1 or 2 Integers, or 1 Range object, was: #{args.inspect}", caller(1)
406
+ end
407
+
408
+ def each
409
+ lazy_load
410
+ if block_given?
411
+ @array.each { |entry| yield entry }
412
+ self
413
+ else
414
+ @array.each
415
+ end
416
+ end
417
+
418
+ # delegate any not-explicitly-handled methods to @array, if possible.
419
+ # this is handy for handling methods mixed-into Array like group_by
420
+ def method_missing(method, *args, &block)
421
+ if @array.respond_to?(method)
422
+ lazy_load
423
+ results = @array.send(method, *args, &block)
424
+ results.equal?(@array) ? self : results
425
+ else
426
+ super
427
+ end
428
+ end
429
+
430
+ def cmp?(other, operator)
431
+ unless loaded?
432
+ # compare the head against the beginning of other. start at index
433
+ # 0 and incrementally compare each entry. if other is a LazyArray
434
+ # this has a lesser likelyhood of triggering a lazy load
435
+ 0.upto(@head.size - 1) do |i|
436
+ return false unless @head[i].__send__(operator, other[i])
437
+ end
438
+
439
+ # compare the tail against the end of other. start at index
440
+ # -1 and decrementally compare each entry. if other is a LazyArray
441
+ # this has a lesser likelyhood of triggering a lazy load
442
+ -1.downto(@tail.size * -1) do |i|
443
+ return false unless @tail[i].__send__(operator, other[i])
444
+ end
445
+
446
+ lazy_load
447
+ end
448
+
449
+ @array.send(operator, other.to_ary)
450
+ end
451
+ end
@@ -0,0 +1,13 @@
1
+ module Ardm
2
+ module LocalObjectSpace
3
+ def self.extended(klass)
4
+ (class << klass; self; end).send :attr_accessor, :hook_scopes
5
+ klass.hook_scopes = []
6
+ super
7
+ end
8
+
9
+ def object_by_id(object_id)
10
+ self.hook_scopes.detect { |object| object.object_id == object_id }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,201 @@
1
+ # ==== Public Ardm Logger API
2
+ #
3
+ # To replace an existing logger with a new one:
4
+ # Ardm::Logger.set_log(log{String, IO},level{Symbol, String})
5
+ #
6
+ # Available logging levels are
7
+ # Ardm::Logger::{ Fatal, Error, Warn, Info, Debug }
8
+ #
9
+ # Logging via:
10
+ # Ardm.logger.fatal(message<String>,&block)
11
+ # Ardm.logger.error(message<String>,&block)
12
+ # Ardm.logger.warn(message<String>,&block)
13
+ # Ardm.logger.info(message<String>,&block)
14
+ # Ardm.logger.debug(message<String>,&block)
15
+ #
16
+ # Logging with autoflush:
17
+ # Ardm.logger.fatal!(message<String>,&block)
18
+ # Ardm.logger.error!(message<String>,&block)
19
+ # Ardm.logger.warn!(message<String>,&block)
20
+ # Ardm.logger.info!(message<String>,&block)
21
+ # Ardm.logger.debug!(message<String>,&block)
22
+ #
23
+ # Flush the buffer to
24
+ # Ardm.logger.flush
25
+ #
26
+ # Remove the current log object
27
+ # Ardm.logger.close
28
+ #
29
+ # ==== Private Ardm Logger API
30
+ #
31
+ # To initialize the logger you create a new object, proxies to set_log.
32
+ # Ardm::Logger.new(log{String, IO},level{Symbol, String})
33
+ module Ardm
34
+
35
+ class << self
36
+ attr_accessor :logger
37
+ end
38
+
39
+ class Logger
40
+
41
+ attr_accessor :level
42
+ attr_accessor :delimiter
43
+ attr_accessor :auto_flush
44
+ attr_reader :buffer
45
+ attr_reader :log
46
+ attr_reader :init_args
47
+
48
+ # ==== Notes
49
+ # Ruby (standard) logger levels:
50
+ # :fatal:: An unhandleable error that results in a program crash
51
+ # :error:: A handleable error condition
52
+ # :warn:: A warning
53
+ # :info:: generic (useful) information about system operation
54
+ # :debug:: low-level information for developers
55
+ Levels =
56
+ {
57
+ :fatal => 7,
58
+ :error => 6,
59
+ :warn => 4,
60
+ :info => 3,
61
+ :debug => 0
62
+ }
63
+
64
+ private
65
+
66
+ # Readies a log for writing.
67
+ #
68
+ # ==== Parameters
69
+ # log<IO, String>:: Either an IO object or a name of a logfile.
70
+ def initialize_log(log)
71
+ close if @log # be sure that we don't leave open files laying around.
72
+
73
+ if log.respond_to?(:write)
74
+ @log = log
75
+ elsif File.exist?(log)
76
+ @log = open(log, (File::WRONLY | File::APPEND))
77
+ @log.sync = true
78
+ else
79
+ FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
80
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
81
+ @log.sync = true
82
+ @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
83
+ end
84
+ end
85
+
86
+ public
87
+
88
+ # To initialize the logger you create a new object, proxies to set_log.
89
+ #
90
+ # ==== Parameters
91
+ # *args:: Arguments to create the log from. See set_logs for specifics.
92
+ def initialize(*args)
93
+ @init_args = args
94
+ set_log(*args)
95
+ self.auto_flush = true
96
+ Ardm.logger = self
97
+ end
98
+
99
+ # Replaces an existing logger with a new one.
100
+ #
101
+ # ==== Parameters
102
+ # log<IO, String>:: Either an IO object or a name of a logfile.
103
+ # log_level<~to_sym>::
104
+ # The log level from, e.g. :fatal or :info. Defaults to :error in the
105
+ # production environment and :debug otherwise.
106
+ # delimiter<String>::
107
+ # Delimiter to use between message sections. Defaults to " ~ ".
108
+ # auto_flush<Boolean>::
109
+ # Whether the log should automatically flush after new messages are
110
+ # added. Defaults to false.
111
+ def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
112
+ if log_level && Levels[log_level.to_sym]
113
+ @level = Levels[log_level.to_sym]
114
+ else
115
+ @level = Levels[:debug]
116
+ end
117
+ @buffer = []
118
+ @delimiter = delimiter
119
+ @auto_flush = auto_flush
120
+
121
+ initialize_log(log)
122
+ end
123
+
124
+ # Flush the entire buffer to the log object.
125
+ def flush
126
+ return unless @buffer.size > 0
127
+ to_flush = @buffer
128
+ @buffer = []
129
+ @log.write(to_flush.join)
130
+ end
131
+
132
+ # Close and remove the current log object.
133
+ def close
134
+ flush
135
+ @log.close if @log.respond_to?(:close) && !@log.tty?
136
+ @log = nil
137
+ end
138
+
139
+ # Appends a message to the log. The methods yield to an optional block and
140
+ # the output of this block will be appended to the message.
141
+ #
142
+ # ==== Parameters
143
+ # string<String>:: The message to be logged. Defaults to nil.
144
+ #
145
+ # ==== Returns
146
+ # String:: The resulting message added to the log file.
147
+ def <<(string = nil)
148
+ message = ""
149
+ message << delimiter
150
+ message << string if string
151
+ message << "\n" unless message[-1] == ?\n
152
+ @buffer << message
153
+ flush if @auto_flush
154
+
155
+ message
156
+ end
157
+ alias_method :push, :<<
158
+
159
+ # Generate the logging methods for Ardm.logger for each log level.
160
+ Levels.each_pair do |name, number|
161
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
162
+
163
+ # Appends a message to the log if the log level is at least as high as
164
+ # the log level of the logger.
165
+ #
166
+ # ==== Parameters
167
+ # string<String>:: The message to be logged. Defaults to nil.
168
+ #
169
+ # ==== Returns
170
+ # self:: The logger object for chaining.
171
+ def #{name}(message = nil)
172
+ self << message if #{number} >= level
173
+ self
174
+ end
175
+
176
+ # Appends a message to the log if the log level is at least as high as
177
+ # the log level of the logger. The bang! version of the method also auto
178
+ # flushes the log buffer to disk.
179
+ #
180
+ # ==== Parameters
181
+ # string<String>:: The message to be logged. Defaults to nil.
182
+ #
183
+ # ==== Returns
184
+ # self:: The logger object for chaining.
185
+ def #{name}!(message = nil)
186
+ self << message if #{number} >= level
187
+ flush if #{number} >= level
188
+ self
189
+ end
190
+
191
+ # ==== Returns
192
+ # Boolean:: True if this level will be logged by this logger.
193
+ def #{name}?
194
+ #{number} >= level
195
+ end
196
+ LEVELMETHODS
197
+ end
198
+
199
+ end
200
+
201
+ end