core_ext 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 (175) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/core_ext/array/access.rb +76 -0
  4. data/lib/core_ext/array/conversions.rb +211 -0
  5. data/lib/core_ext/array/extract_options.rb +29 -0
  6. data/lib/core_ext/array/grouping.rb +116 -0
  7. data/lib/core_ext/array/inquiry.rb +17 -0
  8. data/lib/core_ext/array/prepend_and_append.rb +7 -0
  9. data/lib/core_ext/array/wrap.rb +46 -0
  10. data/lib/core_ext/array.rb +7 -0
  11. data/lib/core_ext/array_inquirer.rb +44 -0
  12. data/lib/core_ext/benchmark.rb +14 -0
  13. data/lib/core_ext/benchmarkable.rb +49 -0
  14. data/lib/core_ext/big_decimal/conversions.rb +14 -0
  15. data/lib/core_ext/big_decimal.rb +1 -0
  16. data/lib/core_ext/builder.rb +6 -0
  17. data/lib/core_ext/callbacks.rb +770 -0
  18. data/lib/core_ext/class/attribute.rb +128 -0
  19. data/lib/core_ext/class/attribute_accessors.rb +4 -0
  20. data/lib/core_ext/class/subclasses.rb +42 -0
  21. data/lib/core_ext/class.rb +2 -0
  22. data/lib/core_ext/concern.rb +142 -0
  23. data/lib/core_ext/configurable.rb +148 -0
  24. data/lib/core_ext/date/acts_like.rb +8 -0
  25. data/lib/core_ext/date/blank.rb +12 -0
  26. data/lib/core_ext/date/calculations.rb +143 -0
  27. data/lib/core_ext/date/conversions.rb +93 -0
  28. data/lib/core_ext/date/zones.rb +6 -0
  29. data/lib/core_ext/date.rb +5 -0
  30. data/lib/core_ext/date_and_time/calculations.rb +328 -0
  31. data/lib/core_ext/date_and_time/zones.rb +40 -0
  32. data/lib/core_ext/date_time/acts_like.rb +14 -0
  33. data/lib/core_ext/date_time/blank.rb +12 -0
  34. data/lib/core_ext/date_time/calculations.rb +177 -0
  35. data/lib/core_ext/date_time/conversions.rb +104 -0
  36. data/lib/core_ext/date_time/zones.rb +6 -0
  37. data/lib/core_ext/date_time.rb +5 -0
  38. data/lib/core_ext/deprecation/behaviors.rb +86 -0
  39. data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
  40. data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
  41. data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
  42. data/lib/core_ext/deprecation/reporting.rb +105 -0
  43. data/lib/core_ext/deprecation.rb +43 -0
  44. data/lib/core_ext/digest/uuid.rb +51 -0
  45. data/lib/core_ext/duration.rb +157 -0
  46. data/lib/core_ext/enumerable.rb +106 -0
  47. data/lib/core_ext/file/atomic.rb +68 -0
  48. data/lib/core_ext/file.rb +1 -0
  49. data/lib/core_ext/hash/compact.rb +20 -0
  50. data/lib/core_ext/hash/conversions.rb +261 -0
  51. data/lib/core_ext/hash/deep_merge.rb +38 -0
  52. data/lib/core_ext/hash/except.rb +22 -0
  53. data/lib/core_ext/hash/indifferent_access.rb +23 -0
  54. data/lib/core_ext/hash/keys.rb +170 -0
  55. data/lib/core_ext/hash/reverse_merge.rb +22 -0
  56. data/lib/core_ext/hash/slice.rb +48 -0
  57. data/lib/core_ext/hash/transform_values.rb +29 -0
  58. data/lib/core_ext/hash.rb +9 -0
  59. data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
  60. data/lib/core_ext/inflections.rb +70 -0
  61. data/lib/core_ext/inflector/inflections.rb +244 -0
  62. data/lib/core_ext/inflector/methods.rb +381 -0
  63. data/lib/core_ext/inflector/transliterate.rb +112 -0
  64. data/lib/core_ext/inflector.rb +7 -0
  65. data/lib/core_ext/integer/inflections.rb +29 -0
  66. data/lib/core_ext/integer/multiple.rb +10 -0
  67. data/lib/core_ext/integer/time.rb +29 -0
  68. data/lib/core_ext/integer.rb +3 -0
  69. data/lib/core_ext/json/decoding.rb +67 -0
  70. data/lib/core_ext/json/encoding.rb +127 -0
  71. data/lib/core_ext/json.rb +2 -0
  72. data/lib/core_ext/kernel/agnostics.rb +11 -0
  73. data/lib/core_ext/kernel/concern.rb +10 -0
  74. data/lib/core_ext/kernel/reporting.rb +41 -0
  75. data/lib/core_ext/kernel/singleton_class.rb +6 -0
  76. data/lib/core_ext/kernel.rb +4 -0
  77. data/lib/core_ext/load_error.rb +30 -0
  78. data/lib/core_ext/logger.rb +57 -0
  79. data/lib/core_ext/logger_silence.rb +24 -0
  80. data/lib/core_ext/marshal.rb +19 -0
  81. data/lib/core_ext/module/aliasing.rb +74 -0
  82. data/lib/core_ext/module/anonymous.rb +28 -0
  83. data/lib/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/core_ext/module/concerning.rb +135 -0
  86. data/lib/core_ext/module/delegation.rb +218 -0
  87. data/lib/core_ext/module/deprecation.rb +23 -0
  88. data/lib/core_ext/module/introspection.rb +62 -0
  89. data/lib/core_ext/module/method_transplanting.rb +3 -0
  90. data/lib/core_ext/module/qualified_const.rb +52 -0
  91. data/lib/core_ext/module/reachable.rb +8 -0
  92. data/lib/core_ext/module/remove_method.rb +35 -0
  93. data/lib/core_ext/module.rb +11 -0
  94. data/lib/core_ext/multibyte/chars.rb +231 -0
  95. data/lib/core_ext/multibyte/unicode.rb +388 -0
  96. data/lib/core_ext/multibyte.rb +21 -0
  97. data/lib/core_ext/name_error.rb +31 -0
  98. data/lib/core_ext/numeric/bytes.rb +64 -0
  99. data/lib/core_ext/numeric/conversions.rb +132 -0
  100. data/lib/core_ext/numeric/inquiry.rb +26 -0
  101. data/lib/core_ext/numeric/time.rb +74 -0
  102. data/lib/core_ext/numeric.rb +4 -0
  103. data/lib/core_ext/object/acts_like.rb +10 -0
  104. data/lib/core_ext/object/blank.rb +140 -0
  105. data/lib/core_ext/object/conversions.rb +4 -0
  106. data/lib/core_ext/object/deep_dup.rb +53 -0
  107. data/lib/core_ext/object/duplicable.rb +98 -0
  108. data/lib/core_ext/object/inclusion.rb +27 -0
  109. data/lib/core_ext/object/instance_variables.rb +28 -0
  110. data/lib/core_ext/object/json.rb +199 -0
  111. data/lib/core_ext/object/to_param.rb +1 -0
  112. data/lib/core_ext/object/to_query.rb +84 -0
  113. data/lib/core_ext/object/try.rb +146 -0
  114. data/lib/core_ext/object/with_options.rb +69 -0
  115. data/lib/core_ext/object.rb +14 -0
  116. data/lib/core_ext/option_merger.rb +25 -0
  117. data/lib/core_ext/ordered_hash.rb +48 -0
  118. data/lib/core_ext/ordered_options.rb +81 -0
  119. data/lib/core_ext/range/conversions.rb +34 -0
  120. data/lib/core_ext/range/each.rb +21 -0
  121. data/lib/core_ext/range/include_range.rb +23 -0
  122. data/lib/core_ext/range/overlaps.rb +8 -0
  123. data/lib/core_ext/range.rb +4 -0
  124. data/lib/core_ext/regexp.rb +5 -0
  125. data/lib/core_ext/rescuable.rb +119 -0
  126. data/lib/core_ext/securerandom.rb +23 -0
  127. data/lib/core_ext/security_utils.rb +20 -0
  128. data/lib/core_ext/string/access.rb +104 -0
  129. data/lib/core_ext/string/behavior.rb +6 -0
  130. data/lib/core_ext/string/conversions.rb +56 -0
  131. data/lib/core_ext/string/exclude.rb +11 -0
  132. data/lib/core_ext/string/filters.rb +102 -0
  133. data/lib/core_ext/string/indent.rb +43 -0
  134. data/lib/core_ext/string/inflections.rb +235 -0
  135. data/lib/core_ext/string/inquiry.rb +13 -0
  136. data/lib/core_ext/string/multibyte.rb +53 -0
  137. data/lib/core_ext/string/output_safety.rb +261 -0
  138. data/lib/core_ext/string/starts_ends_with.rb +4 -0
  139. data/lib/core_ext/string/strip.rb +23 -0
  140. data/lib/core_ext/string/zones.rb +14 -0
  141. data/lib/core_ext/string.rb +13 -0
  142. data/lib/core_ext/string_inquirer.rb +26 -0
  143. data/lib/core_ext/tagged_logging.rb +78 -0
  144. data/lib/core_ext/test_case.rb +88 -0
  145. data/lib/core_ext/testing/assertions.rb +99 -0
  146. data/lib/core_ext/testing/autorun.rb +12 -0
  147. data/lib/core_ext/testing/composite_filter.rb +54 -0
  148. data/lib/core_ext/testing/constant_lookup.rb +50 -0
  149. data/lib/core_ext/testing/declarative.rb +26 -0
  150. data/lib/core_ext/testing/deprecation.rb +36 -0
  151. data/lib/core_ext/testing/file_fixtures.rb +34 -0
  152. data/lib/core_ext/testing/isolation.rb +115 -0
  153. data/lib/core_ext/testing/method_call_assertions.rb +41 -0
  154. data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
  155. data/lib/core_ext/testing/stream.rb +42 -0
  156. data/lib/core_ext/testing/tagged_logging.rb +25 -0
  157. data/lib/core_ext/testing/time_helpers.rb +134 -0
  158. data/lib/core_ext/time/acts_like.rb +8 -0
  159. data/lib/core_ext/time/calculations.rb +284 -0
  160. data/lib/core_ext/time/conversions.rb +66 -0
  161. data/lib/core_ext/time/zones.rb +95 -0
  162. data/lib/core_ext/time.rb +20 -0
  163. data/lib/core_ext/time_with_zone.rb +503 -0
  164. data/lib/core_ext/time_zone.rb +464 -0
  165. data/lib/core_ext/uri.rb +25 -0
  166. data/lib/core_ext/version.rb +3 -0
  167. data/lib/core_ext/xml_mini/jdom.rb +181 -0
  168. data/lib/core_ext/xml_mini/libxml.rb +79 -0
  169. data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
  170. data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
  171. data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
  172. data/lib/core_ext/xml_mini/rexml.rb +130 -0
  173. data/lib/core_ext/xml_mini.rb +194 -0
  174. data/lib/core_ext.rb +3 -0
  175. metadata +310 -0
@@ -0,0 +1,50 @@
1
+ require "core_ext/concern"
2
+ require "core_ext/inflector"
3
+
4
+ module CoreExt
5
+ module Testing
6
+ # Resolves a constant from a minitest spec name.
7
+ #
8
+ # Given the following spec-style test:
9
+ #
10
+ # describe WidgetsController, :index do
11
+ # describe "authenticated user" do
12
+ # describe "returns widgets" do
13
+ # it "has a controller that exists" do
14
+ # assert_kind_of WidgetsController, @controller
15
+ # end
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ # The test will have the following name:
21
+ #
22
+ # "WidgetsController::index::authenticated user::returns widgets"
23
+ #
24
+ # The constant WidgetsController can be resolved from the name.
25
+ # The following code will resolve the constant:
26
+ #
27
+ # controller = determine_constant_from_test_name(name) do |constant|
28
+ # Class === constant && constant < ::ActionController::Metal
29
+ # end
30
+ module ConstantLookup
31
+ extend ::CoreExt::Concern
32
+
33
+ module ClassMethods # :nodoc:
34
+ def determine_constant_from_test_name(test_name)
35
+ names = test_name.split "::"
36
+ while names.size > 0 do
37
+ names.last.sub!(/Test$/, "")
38
+ begin
39
+ constant = names.join("::").safe_constantize
40
+ break(constant) if yield(constant)
41
+ ensure
42
+ names.pop
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ module CoreExt
2
+ module Testing
3
+ module Declarative
4
+ unless defined?(Spec)
5
+ # Helper to define a test method using a String. Under the hood, it replaces
6
+ # spaces with underscores and defines the test method.
7
+ #
8
+ # test "verify something" do
9
+ # ...
10
+ # end
11
+ def test(name, &block)
12
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
13
+ defined = method_defined? test_name
14
+ raise "#{test_name} is already defined in #{self}" if defined
15
+ if block_given?
16
+ define_method(test_name, &block)
17
+ else
18
+ define_method(test_name) do
19
+ flunk "No implementation provided for #{name}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ require 'core_ext/deprecation'
2
+
3
+ module CoreExt
4
+ module Testing
5
+ module Deprecation #:nodoc:
6
+ def assert_deprecated(match = nil, deprecator = nil, &block)
7
+ result, warnings = collect_deprecations(deprecator, &block)
8
+ assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
9
+ if match
10
+ match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
11
+ assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
12
+ end
13
+ result
14
+ end
15
+
16
+ def assert_not_deprecated(deprecator = nil, &block)
17
+ result, deprecations = collect_deprecations(deprecator, &block)
18
+ assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
19
+ result
20
+ end
21
+
22
+ def collect_deprecations(deprecator = nil)
23
+ deprecator ||= CoreExt::Deprecation
24
+ old_behavior = deprecator.behavior
25
+ deprecations = []
26
+ deprecator.behavior = Proc.new do |message, callstack|
27
+ deprecations << message
28
+ end
29
+ result = yield
30
+ [result, deprecations]
31
+ ensure
32
+ deprecator.behavior = old_behavior
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ module CoreExt
2
+ module Testing
3
+ # Adds simple access to sample files called file fixtures.
4
+ # File fixtures are normal files stored in
5
+ # <tt>CoreExt::TestCase.file_fixture_path</tt>.
6
+ #
7
+ # File fixtures are represented as +Pathname+ objects.
8
+ # This makes it easy to extract specific information:
9
+ #
10
+ # file_fixture("example.txt").read # get the file's content
11
+ # file_fixture("example.mp3").size # get the file size
12
+ module FileFixtures
13
+ extend CoreExt::Concern
14
+
15
+ included do
16
+ class_attribute :file_fixture_path, instance_writer: false
17
+ end
18
+
19
+ # Returns a +Pathname+ to the fixture file named +fixture_name+.
20
+ #
21
+ # Raises +ArgumentError+ if +fixture_name+ can't be found.
22
+ def file_fixture(fixture_name)
23
+ path = Pathname.new(File.join(file_fixture_path, fixture_name))
24
+
25
+ if path.exist?
26
+ path
27
+ else
28
+ msg = "the directory '%s' does not contain a file named '%s'"
29
+ raise ArgumentError, msg % [file_fixture_path, fixture_name]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,115 @@
1
+ module CoreExt
2
+ module Testing
3
+ module Isolation
4
+ require 'thread'
5
+
6
+ def self.included(klass) #:nodoc:
7
+ klass.class_eval do
8
+ parallelize_me!
9
+ end
10
+ end
11
+
12
+ def self.forking_env?
13
+ !ENV["NO_FORK"] && Process.respond_to?(:fork)
14
+ end
15
+
16
+ @@class_setup_mutex = Mutex.new
17
+
18
+ def _run_class_setup # class setup method should only happen in parent
19
+ @@class_setup_mutex.synchronize do
20
+ unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
21
+ self.class.setup if self.class.respond_to?(:setup)
22
+ @@ran_class_setup = true
23
+ end
24
+ end
25
+ end
26
+
27
+ def run
28
+ serialized = run_in_isolation do
29
+ super
30
+ end
31
+
32
+ Marshal.load(serialized)
33
+ end
34
+
35
+ module Forking
36
+ def run_in_isolation(&blk)
37
+ read, write = IO.pipe
38
+ read.binmode
39
+ write.binmode
40
+
41
+ pid = fork do
42
+ read.close
43
+ yield
44
+ begin
45
+ if error?
46
+ failures.map! { |e|
47
+ begin
48
+ Marshal.dump e
49
+ e
50
+ rescue TypeError
51
+ ex = Exception.new e.message
52
+ ex.set_backtrace e.backtrace
53
+ Minitest::UnexpectedError.new ex
54
+ end
55
+ }
56
+ end
57
+ result = Marshal.dump(self.dup)
58
+ end
59
+
60
+ write.puts [result].pack("m")
61
+ exit!
62
+ end
63
+
64
+ write.close
65
+ result = read.read
66
+ Process.wait2(pid)
67
+ return result.unpack("m")[0]
68
+ end
69
+ end
70
+
71
+ module Subprocess
72
+ ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
73
+
74
+ # Crazy H4X to get this working in windows / jruby with
75
+ # no forking.
76
+ def run_in_isolation(&blk)
77
+ require "tempfile"
78
+
79
+ if ENV["ISOLATION_TEST"]
80
+ yield
81
+ File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
82
+ file.puts [Marshal.dump(self.dup)].pack("m")
83
+ end
84
+ exit!
85
+ else
86
+ Tempfile.open("isolation") do |tmpfile|
87
+ env = {
88
+ 'ISOLATION_TEST' => self.class.name,
89
+ 'ISOLATION_OUTPUT' => tmpfile.path
90
+ }
91
+
92
+ load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
93
+ orig_args = ORIG_ARGV.join(" ")
94
+ test_opts = "-n#{self.class.name}##{self.name}"
95
+ command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}"
96
+
97
+ # IO.popen lets us pass env in a cross-platform way
98
+ child = IO.popen(env, command)
99
+
100
+ begin
101
+ Process.wait(child.pid)
102
+ rescue Errno::ECHILD # The child process may exit before we wait
103
+ nil
104
+ end
105
+
106
+ return tmpfile.read.unpack("m")[0]
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ include forking_env? ? Forking : Subprocess
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,41 @@
1
+ require 'minitest/mock'
2
+
3
+ module CoreExt
4
+ module Testing
5
+ module MethodCallAssertions # :nodoc:
6
+ private
7
+ def assert_called(object, method_name, message = nil, times: 1, returns: nil)
8
+ times_called = 0
9
+
10
+ object.stub(method_name, proc { times_called += 1; returns }) { yield }
11
+
12
+ error = "Expected #{method_name} to be called #{times} times, " \
13
+ "but was called #{times_called} times"
14
+ error = "#{message}.\n#{error}" if message
15
+ assert_equal times, times_called, error
16
+ end
17
+
18
+ def assert_called_with(object, method_name, args = [], returns: nil)
19
+ mock = Minitest::Mock.new
20
+
21
+ if args.all? { |arg| arg.is_a?(Array) }
22
+ args.each { |arg| mock.expect(:call, returns, arg) }
23
+ else
24
+ mock.expect(:call, returns, args)
25
+ end
26
+
27
+ object.stub(method_name, mock) { yield }
28
+
29
+ mock.verify
30
+ end
31
+
32
+ def assert_not_called(object, method_name, message = nil, &block)
33
+ assert_called(object, method_name, message, times: 0, &block)
34
+ end
35
+
36
+ def stub_any_instance(klass, instance: klass.new)
37
+ klass.stub(:new, instance) { yield instance }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ require 'core_ext/concern'
2
+ require 'core_ext/callbacks'
3
+
4
+ module CoreExt
5
+ module Testing
6
+ # Adds support for +setup+ and +teardown+ callbacks.
7
+ # These callbacks serve as a replacement to overwriting the
8
+ # <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
9
+ #
10
+ # class ExampleTest < CoreExt::TestCase
11
+ # setup do
12
+ # # ...
13
+ # end
14
+ #
15
+ # teardown do
16
+ # # ...
17
+ # end
18
+ # end
19
+ module SetupAndTeardown
20
+ extend CoreExt::Concern
21
+
22
+ included do
23
+ include CoreExt::Callbacks
24
+ define_callbacks :setup, :teardown
25
+ end
26
+
27
+ module ClassMethods
28
+ # Add a callback, which runs before <tt>TestCase#setup</tt>.
29
+ def setup(*args, &block)
30
+ set_callback(:setup, :before, *args, &block)
31
+ end
32
+
33
+ # Add a callback, which runs after <tt>TestCase#teardown</tt>.
34
+ def teardown(*args, &block)
35
+ set_callback(:teardown, :after, *args, &block)
36
+ end
37
+ end
38
+
39
+ def before_setup # :nodoc:
40
+ super
41
+ run_callbacks :setup
42
+ end
43
+
44
+ def after_teardown # :nodoc:
45
+ run_callbacks :teardown
46
+ super
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ module CoreExt
2
+ module Testing
3
+ module Stream #:nodoc:
4
+ private
5
+
6
+ def silence_stream(stream)
7
+ old_stream = stream.dup
8
+ stream.reopen(IO::NULL)
9
+ stream.sync = true
10
+ yield
11
+ ensure
12
+ stream.reopen(old_stream)
13
+ old_stream.close
14
+ end
15
+
16
+ def quietly
17
+ silence_stream(STDOUT) do
18
+ silence_stream(STDERR) do
19
+ yield
20
+ end
21
+ end
22
+ end
23
+
24
+ def capture(stream)
25
+ stream = stream.to_s
26
+ captured_stream = Tempfile.new(stream)
27
+ stream_io = eval("$#{stream}")
28
+ origin_stream = stream_io.dup
29
+ stream_io.reopen(captured_stream)
30
+
31
+ yield
32
+
33
+ stream_io.rewind
34
+ return captured_stream.read
35
+ ensure
36
+ captured_stream.close
37
+ captured_stream.unlink
38
+ stream_io.reopen(origin_stream)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ module CoreExt
2
+ module Testing
3
+ # Logs a "PostsControllerTest: test name" heading before each test to
4
+ # make test.log easier to search and follow along with.
5
+ module TaggedLogging #:nodoc:
6
+ attr_writer :tagged_logger
7
+
8
+ def before_setup
9
+ if tagged_logger && tagged_logger.info?
10
+ heading = "#{self.class}: #{name}"
11
+ divider = '-' * heading.size
12
+ tagged_logger.info divider
13
+ tagged_logger.info heading
14
+ tagged_logger.info divider
15
+ end
16
+ super
17
+ end
18
+
19
+ private
20
+ def tagged_logger
21
+ @tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,134 @@
1
+ module CoreExt
2
+ module Testing
3
+ class SimpleStubs # :nodoc:
4
+ Stub = Struct.new(:object, :method_name, :original_method)
5
+
6
+ def initialize
7
+ @stubs = {}
8
+ end
9
+
10
+ def stub_object(object, method_name, return_value)
11
+ key = [object.object_id, method_name]
12
+
13
+ if stub = @stubs[key]
14
+ unstub_object(stub)
15
+ end
16
+
17
+ new_name = "__simple_stub__#{method_name}"
18
+
19
+ @stubs[key] = Stub.new(object, method_name, new_name)
20
+
21
+ object.singleton_class.send :alias_method, new_name, method_name
22
+ object.define_singleton_method(method_name) { return_value }
23
+ end
24
+
25
+ def unstub_all!
26
+ @stubs.each_value do |stub|
27
+ unstub_object(stub)
28
+ end
29
+ @stubs = {}
30
+ end
31
+
32
+ private
33
+
34
+ def unstub_object(stub)
35
+ singleton_class = stub.object.singleton_class
36
+ singleton_class.send :undef_method, stub.method_name
37
+ singleton_class.send :alias_method, stub.method_name, stub.original_method
38
+ singleton_class.send :undef_method, stub.original_method
39
+ end
40
+ end
41
+
42
+ # Contains helpers that help you test passage of time.
43
+ module TimeHelpers
44
+ # Changes current time to the time in the future or in the past by a given time difference by
45
+ # stubbing +Time.now+, +Date.today+, and +DateTime.now+.
46
+ #
47
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
48
+ # travel 1.day
49
+ # Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
50
+ # Date.current # => Sun, 10 Nov 2013
51
+ # DateTime.current # => Sun, 10 Nov 2013 15:34:49 -0500
52
+ #
53
+ # This method also accepts a block, which will return the current time back to its original
54
+ # state at the end of the block:
55
+ #
56
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
57
+ # travel 1.day do
58
+ # User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
59
+ # end
60
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
61
+ def travel(duration, &block)
62
+ travel_to Time.now + duration, &block
63
+ end
64
+
65
+ # Changes current time to the given time by stubbing +Time.now+,
66
+ # +Date.today+, and +DateTime.now+ to return the time or date passed into this method.
67
+ #
68
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
69
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44)
70
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
71
+ # Date.current # => Wed, 24 Nov 2004
72
+ # DateTime.current # => Wed, 24 Nov 2004 01:04:44 -0500
73
+ #
74
+ # Dates are taken as their timestamp at the beginning of the day in the
75
+ # application time zone. <tt>Time.current</tt> returns said timestamp,
76
+ # and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
77
+ # <tt>Date.current</tt> returns a date equal to the argument, and
78
+ # <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
79
+ # be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
80
+ # or <tt>Date.today</tt>, in order to honor the application time zone
81
+ # please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
82
+ #
83
+ # Note that the usec for the time passed will be set to 0 to prevent rounding
84
+ # errors with external services, like MySQL (which will round instead of floor,
85
+ # leading to off-by-one-second errors).
86
+ #
87
+ # This method also accepts a block, which will return the current time back to its original
88
+ # state at the end of the block:
89
+ #
90
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
91
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44) do
92
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
93
+ # end
94
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
95
+ def travel_to(date_or_time)
96
+ if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
97
+ now = date_or_time.midnight.to_time
98
+ else
99
+ now = date_or_time.to_time.change(usec: 0)
100
+ end
101
+
102
+ simple_stubs.stub_object(Time, :now, now)
103
+ simple_stubs.stub_object(Date, :today, now.to_date)
104
+ simple_stubs.stub_object(DateTime, :now, now.to_datetime)
105
+
106
+ if block_given?
107
+ begin
108
+ yield
109
+ ensure
110
+ travel_back
111
+ end
112
+ end
113
+ end
114
+
115
+ # Returns the current time back to its original state, by removing the stubs added by
116
+ # `travel` and `travel_to`.
117
+ #
118
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
119
+ # travel_to Time.new(2004, 11, 24, 01, 04, 44)
120
+ # Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
121
+ # travel_back
122
+ # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
123
+ def travel_back
124
+ simple_stubs.unstub_all!
125
+ end
126
+
127
+ private
128
+
129
+ def simple_stubs
130
+ @simple_stubs ||= SimpleStubs.new
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,8 @@
1
+ require 'core_ext/object/acts_like'
2
+
3
+ class Time
4
+ # Duck-types as a Time-like class. See Object#acts_like?.
5
+ def acts_like_time?
6
+ true
7
+ end
8
+ end