csd 0.0.15 → 0.0.16

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 (256) hide show
  1. data/.gitignore +1 -0
  2. data/COPYING +367 -0
  3. data/Rakefile +10 -10
  4. data/VERSION +1 -1
  5. data/bin/ai +19 -0
  6. data/csd.gemspec +257 -35
  7. data/lib/active_support.rb +75 -0
  8. data/lib/active_support/all.rb +3 -0
  9. data/lib/active_support/backtrace_cleaner.rb +94 -0
  10. data/lib/active_support/base64.rb +42 -0
  11. data/lib/active_support/basic_object.rb +21 -0
  12. data/lib/active_support/benchmarkable.rb +60 -0
  13. data/lib/active_support/buffered_logger.rb +132 -0
  14. data/lib/active_support/builder.rb +6 -0
  15. data/lib/active_support/cache.rb +626 -0
  16. data/lib/active_support/cache/compressed_mem_cache_store.rb +13 -0
  17. data/lib/active_support/cache/file_store.rb +188 -0
  18. data/lib/active_support/cache/mem_cache_store.rb +191 -0
  19. data/lib/active_support/cache/memory_store.rb +159 -0
  20. data/lib/active_support/cache/strategy/local_cache.rb +164 -0
  21. data/lib/active_support/cache/synchronized_memory_store.rb +11 -0
  22. data/lib/active_support/callbacks.rb +600 -0
  23. data/lib/active_support/concern.rb +29 -0
  24. data/lib/active_support/configurable.rb +36 -0
  25. data/lib/active_support/core_ext.rb +3 -0
  26. data/lib/active_support/core_ext/array.rb +7 -0
  27. data/lib/active_support/core_ext/array/access.rb +46 -0
  28. data/lib/active_support/core_ext/array/conversions.rb +164 -0
  29. data/lib/active_support/core_ext/array/extract_options.rb +29 -0
  30. data/lib/active_support/core_ext/array/grouping.rb +100 -0
  31. data/lib/active_support/core_ext/array/random_access.rb +20 -0
  32. data/lib/active_support/core_ext/array/uniq_by.rb +17 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +22 -0
  34. data/lib/active_support/core_ext/benchmark.rb +7 -0
  35. data/lib/active_support/core_ext/big_decimal.rb +1 -0
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +27 -0
  37. data/lib/active_support/core_ext/cgi.rb +1 -0
  38. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +19 -0
  39. data/lib/active_support/core_ext/class.rb +4 -0
  40. data/lib/active_support/core_ext/class/attribute.rb +67 -0
  41. data/lib/active_support/core_ext/class/attribute_accessors.rb +63 -0
  42. data/lib/active_support/core_ext/class/delegating_attributes.rb +44 -0
  43. data/lib/active_support/core_ext/class/inheritable_attributes.rb +232 -0
  44. data/lib/active_support/core_ext/class/subclasses.rb +55 -0
  45. data/lib/active_support/core_ext/date/acts_like.rb +8 -0
  46. data/lib/active_support/core_ext/date/calculations.rb +240 -0
  47. data/lib/active_support/core_ext/date/conversions.rb +99 -0
  48. data/lib/active_support/core_ext/date/freeze.rb +31 -0
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +13 -0
  50. data/lib/active_support/core_ext/date_time/calculations.rb +113 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +102 -0
  52. data/lib/active_support/core_ext/date_time/zones.rb +17 -0
  53. data/lib/active_support/core_ext/enumerable.rb +119 -0
  54. data/lib/active_support/core_ext/exception.rb +3 -0
  55. data/lib/active_support/core_ext/file.rb +2 -0
  56. data/lib/active_support/core_ext/file/atomic.rb +41 -0
  57. data/lib/active_support/core_ext/file/path.rb +5 -0
  58. data/lib/active_support/core_ext/float.rb +1 -0
  59. data/lib/active_support/core_ext/float/rounding.rb +19 -0
  60. data/lib/active_support/core_ext/hash.rb +8 -0
  61. data/lib/active_support/core_ext/hash/conversions.rb +150 -0
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +16 -0
  63. data/lib/active_support/core_ext/hash/diff.rb +13 -0
  64. data/lib/active_support/core_ext/hash/except.rb +24 -0
  65. data/lib/active_support/core_ext/hash/indifferent_access.rb +14 -0
  66. data/lib/active_support/core_ext/hash/keys.rb +45 -0
  67. data/lib/active_support/core_ext/hash/reverse_merge.rb +28 -0
  68. data/lib/active_support/core_ext/hash/slice.rb +38 -0
  69. data/lib/active_support/core_ext/integer.rb +3 -0
  70. data/lib/active_support/core_ext/integer/inflections.rb +14 -0
  71. data/lib/active_support/core_ext/integer/multiple.rb +6 -0
  72. data/lib/active_support/core_ext/integer/time.rb +39 -0
  73. data/lib/active_support/core_ext/kernel.rb +5 -0
  74. data/lib/active_support/core_ext/kernel/agnostics.rb +11 -0
  75. data/lib/active_support/core_ext/kernel/debugger.rb +16 -0
  76. data/lib/active_support/core_ext/kernel/reporting.rb +62 -0
  77. data/lib/active_support/core_ext/kernel/requires.rb +26 -0
  78. data/lib/active_support/core_ext/kernel/singleton_class.rb +13 -0
  79. data/lib/active_support/core_ext/load_error.rb +23 -0
  80. data/lib/active_support/core_ext/logger.rb +146 -0
  81. data/lib/active_support/core_ext/module.rb +12 -0
  82. data/lib/active_support/core_ext/module/aliasing.rb +70 -0
  83. data/lib/active_support/core_ext/module/anonymous.rb +24 -0
  84. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  85. data/lib/active_support/core_ext/module/attr_internal.rb +32 -0
  86. data/lib/active_support/core_ext/module/attribute_accessors.rb +66 -0
  87. data/lib/active_support/core_ext/module/delegation.rb +146 -0
  88. data/lib/active_support/core_ext/module/deprecation.rb +9 -0
  89. data/lib/active_support/core_ext/module/introspection.rb +88 -0
  90. data/lib/active_support/core_ext/module/method_names.rb +14 -0
  91. data/lib/active_support/core_ext/module/reachable.rb +10 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +6 -0
  93. data/lib/active_support/core_ext/module/synchronization.rb +42 -0
  94. data/lib/active_support/core_ext/name_error.rb +18 -0
  95. data/lib/active_support/core_ext/numeric.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/bytes.rb +44 -0
  97. data/lib/active_support/core_ext/numeric/time.rb +77 -0
  98. data/lib/active_support/core_ext/object.rb +14 -0
  99. data/lib/active_support/core_ext/object/acts_like.rb +10 -0
  100. data/lib/active_support/core_ext/object/blank.rb +76 -0
  101. data/lib/active_support/core_ext/object/conversions.rb +4 -0
  102. data/lib/active_support/core_ext/object/duplicable.rb +65 -0
  103. data/lib/active_support/core_ext/object/extending.rb +11 -0
  104. data/lib/active_support/core_ext/object/instance_variables.rb +67 -0
  105. data/lib/active_support/core_ext/object/misc.rb +2 -0
  106. data/lib/active_support/core_ext/object/returning.rb +42 -0
  107. data/lib/active_support/core_ext/object/to_param.rb +49 -0
  108. data/lib/active_support/core_ext/object/to_query.rb +27 -0
  109. data/lib/active_support/core_ext/object/try.rb +36 -0
  110. data/lib/active_support/core_ext/object/with_options.rb +26 -0
  111. data/lib/active_support/core_ext/proc.rb +14 -0
  112. data/lib/active_support/core_ext/process.rb +1 -0
  113. data/lib/active_support/core_ext/process/daemon.rb +23 -0
  114. data/lib/active_support/core_ext/range.rb +4 -0
  115. data/lib/active_support/core_ext/range/blockless_step.rb +29 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +21 -0
  117. data/lib/active_support/core_ext/range/include_range.rb +21 -0
  118. data/lib/active_support/core_ext/range/overlaps.rb +8 -0
  119. data/lib/active_support/core_ext/regexp.rb +5 -0
  120. data/lib/active_support/core_ext/rexml.rb +46 -0
  121. data/lib/active_support/core_ext/string.rb +12 -0
  122. data/lib/active_support/core_ext/string/access.rb +99 -0
  123. data/lib/active_support/core_ext/string/behavior.rb +7 -0
  124. data/lib/active_support/core_ext/string/conversions.rb +61 -0
  125. data/lib/active_support/core_ext/string/encoding.rb +11 -0
  126. data/lib/active_support/core_ext/string/exclude.rb +6 -0
  127. data/lib/active_support/core_ext/string/filters.rb +49 -0
  128. data/lib/active_support/core_ext/string/inflections.rb +149 -0
  129. data/lib/active_support/core_ext/string/interpolation.rb +2 -0
  130. data/lib/active_support/core_ext/string/multibyte.rb +72 -0
  131. data/lib/active_support/core_ext/string/output_safety.rb +109 -0
  132. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -0
  133. data/lib/active_support/core_ext/string/xchar.rb +18 -0
  134. data/lib/active_support/core_ext/time/acts_like.rb +8 -0
  135. data/lib/active_support/core_ext/time/calculations.rb +282 -0
  136. data/lib/active_support/core_ext/time/conversions.rb +85 -0
  137. data/lib/active_support/core_ext/time/marshal.rb +56 -0
  138. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +10 -0
  139. data/lib/active_support/core_ext/time/zones.rb +78 -0
  140. data/lib/active_support/core_ext/uri.rb +22 -0
  141. data/lib/active_support/dependencies.rb +628 -0
  142. data/lib/active_support/dependencies/autoload.rb +50 -0
  143. data/lib/active_support/deprecation.rb +18 -0
  144. data/lib/active_support/deprecation/behaviors.rb +38 -0
  145. data/lib/active_support/deprecation/method_wrappers.rb +29 -0
  146. data/lib/active_support/deprecation/proxy_wrappers.rb +74 -0
  147. data/lib/active_support/deprecation/reporting.rb +56 -0
  148. data/lib/active_support/duration.rb +105 -0
  149. data/lib/active_support/gzip.rb +25 -0
  150. data/lib/active_support/hash_with_indifferent_access.rb +145 -0
  151. data/lib/active_support/i18n.rb +8 -0
  152. data/lib/active_support/inflections.rb +56 -0
  153. data/lib/active_support/inflector.rb +7 -0
  154. data/lib/active_support/inflector/inflections.rb +211 -0
  155. data/lib/active_support/inflector/methods.rb +141 -0
  156. data/lib/active_support/inflector/transliterate.rb +97 -0
  157. data/lib/active_support/json.rb +2 -0
  158. data/lib/active_support/json/backends/jsongem.rb +43 -0
  159. data/lib/active_support/json/backends/yajl.rb +40 -0
  160. data/lib/active_support/json/backends/yaml.rb +90 -0
  161. data/lib/active_support/json/decoding.rb +51 -0
  162. data/lib/active_support/json/encoding.rb +254 -0
  163. data/lib/active_support/json/variable.rb +11 -0
  164. data/lib/active_support/lazy_load_hooks.rb +27 -0
  165. data/lib/active_support/locale/en.yml +36 -0
  166. data/lib/active_support/memoizable.rb +103 -0
  167. data/lib/active_support/message_encryptor.rb +71 -0
  168. data/lib/active_support/message_verifier.rb +62 -0
  169. data/lib/active_support/multibyte.rb +44 -0
  170. data/lib/active_support/multibyte/chars.rb +480 -0
  171. data/lib/active_support/multibyte/exceptions.rb +8 -0
  172. data/lib/active_support/multibyte/unicode.rb +393 -0
  173. data/lib/active_support/multibyte/utils.rb +60 -0
  174. data/lib/active_support/notifications.rb +81 -0
  175. data/lib/active_support/notifications/fanout.rb +93 -0
  176. data/lib/active_support/notifications/instrumenter.rb +56 -0
  177. data/lib/active_support/option_merger.rb +25 -0
  178. data/lib/active_support/ordered_hash.rb +158 -0
  179. data/lib/active_support/ordered_options.rb +27 -0
  180. data/lib/active_support/railtie.rb +100 -0
  181. data/lib/active_support/rescuable.rb +114 -0
  182. data/lib/active_support/ruby/shim.rb +22 -0
  183. data/lib/active_support/secure_random.rb +199 -0
  184. data/lib/active_support/string_inquirer.rb +21 -0
  185. data/lib/active_support/test_case.rb +42 -0
  186. data/lib/active_support/testing/assertions.rb +82 -0
  187. data/lib/active_support/testing/declarative.rb +40 -0
  188. data/lib/active_support/testing/default.rb +9 -0
  189. data/lib/active_support/testing/deprecation.rb +55 -0
  190. data/lib/active_support/testing/isolation.rb +154 -0
  191. data/lib/active_support/testing/pending.rb +48 -0
  192. data/lib/active_support/testing/performance.rb +455 -0
  193. data/lib/active_support/testing/setup_and_teardown.rb +111 -0
  194. data/lib/active_support/time.rb +34 -0
  195. data/lib/active_support/time/autoload.rb +5 -0
  196. data/lib/active_support/time_with_zone.rb +341 -0
  197. data/lib/active_support/values/time_zone.rb +377 -0
  198. data/lib/active_support/values/unicode_tables.dat +0 -0
  199. data/lib/active_support/version.rb +10 -0
  200. data/lib/active_support/whiny_nil.rb +60 -0
  201. data/lib/active_support/xml_mini.rb +158 -0
  202. data/lib/active_support/xml_mini/jdom.rb +168 -0
  203. data/lib/active_support/xml_mini/libxml.rb +80 -0
  204. data/lib/active_support/xml_mini/libxmlsax.rb +85 -0
  205. data/lib/active_support/xml_mini/nokogiri.rb +78 -0
  206. data/lib/active_support/xml_mini/nokogirisax.rb +83 -0
  207. data/lib/active_support/xml_mini/rexml.rb +129 -0
  208. data/lib/csd.rb +82 -2
  209. data/lib/csd/application.rb +2 -0
  210. data/lib/csd/application/default.rb +51 -0
  211. data/lib/csd/application/default/base.rb +15 -0
  212. data/lib/csd/application/minisip.rb +25 -0
  213. data/lib/csd/application/minisip/about.yml +14 -0
  214. data/lib/csd/application/minisip/base.rb +161 -0
  215. data/lib/csd/application/minisip/error.rb +11 -0
  216. data/lib/csd/application/minisip/options/common.rb +0 -0
  217. data/lib/csd/application/minisip/options/compile.rb +59 -0
  218. data/lib/csd/{applications → application}/minisip/unix/base.rb +10 -11
  219. data/lib/csd/application/opensips/about.yml +2 -0
  220. data/lib/csd/applications.rb +55 -0
  221. data/lib/csd/commands.rb +88 -65
  222. data/lib/csd/error.rb +31 -0
  223. data/lib/csd/extensions.rb +1 -0
  224. data/lib/{extensions → csd/extensions}/core/array.rb +2 -2
  225. data/lib/csd/extensions/core/dir.rb +46 -0
  226. data/lib/{extensions → csd/extensions}/core/file.rb +2 -2
  227. data/lib/{extensions → csd/extensions}/core/object.rb +2 -2
  228. data/lib/csd/extensions/core/option_parser.rb +33 -0
  229. data/lib/{extensions → csd/extensions}/core/pathname.rb +12 -3
  230. data/lib/{extensions → csd/extensions}/core/string.rb +2 -2
  231. data/lib/{extensions → csd/extensions}/gem/platform.rb +6 -2
  232. data/lib/csd/global_open_struct.rb +18 -0
  233. data/lib/csd/options.rb +124 -95
  234. data/lib/csd/path.rb +31 -0
  235. data/lib/csd/ui.rb +1 -0
  236. data/lib/csd/ui/cli.rb +7 -0
  237. data/lib/csd/ui/ui.rb +46 -0
  238. data/lib/csd/version.rb +9 -0
  239. data/lib/term/ansicolor.rb +102 -0
  240. data/lib/term/ansicolor/.keep +0 -0
  241. data/lib/term/ansicolor/version.rb +10 -0
  242. data/test/functional/test_applications.rb +86 -0
  243. data/test/functional/test_commands.rb +42 -29
  244. data/test/functional/test_options.rb +98 -0
  245. data/test/helper.rb +14 -0
  246. data/test/unit/test_dir.rb +38 -0
  247. data/test/unit/test_pathname.rb +32 -0
  248. metadata +253 -40
  249. data/LICENSE +0 -20
  250. data/bin/csd +0 -8
  251. data/lib/csd/applications/base.rb +0 -33
  252. data/lib/csd/applications/minisip/base.rb +0 -125
  253. data/lib/csd/applications/minisip/init.rb +0 -20
  254. data/lib/csd/init.rb +0 -69
  255. data/lib/csd/path_container.rb +0 -15
  256. data/publish +0 -29
@@ -0,0 +1,164 @@
1
+ require 'active_support/core_ext/object/duplicable'
2
+ require 'active_support/core_ext/string/inflections'
3
+
4
+ module ActiveSupport
5
+ module Cache
6
+ module Strategy
7
+ # Caches that implement LocalCache will be backed by an in memory cache for the
8
+ # duration of a block. Repeated calls to the cache for the same key will hit the
9
+ # in memory cache for faster access.
10
+ module LocalCache
11
+ # Simple memory backed cache. This cache is not thread safe but is intended only
12
+ # for serving as a temporary memory cache for a single thread.
13
+ class LocalStore < Store
14
+ def initialize
15
+ super
16
+ @data = {}
17
+ end
18
+
19
+ # Since it isn't thread safe, don't allow synchronizing.
20
+ def synchronize # :nodoc:
21
+ yield
22
+ end
23
+
24
+ def clear(options = nil)
25
+ @data.clear
26
+ end
27
+
28
+ def read_entry(key, options)
29
+ @data[key]
30
+ end
31
+
32
+ def write_entry(key, value, options)
33
+ @data[key] = value
34
+ true
35
+ end
36
+
37
+ def delete_entry(key, options)
38
+ !!@data.delete(key)
39
+ end
40
+ end
41
+
42
+ # Use a local cache to front for the cache for the duration of a block.
43
+ def with_local_cache
44
+ save_val = Thread.current[thread_local_key]
45
+ begin
46
+ Thread.current[thread_local_key] = LocalStore.new
47
+ yield
48
+ ensure
49
+ Thread.current[thread_local_key] = save_val
50
+ end
51
+ end
52
+
53
+ # Middleware class can be inserted as a Rack handler to use a local cache for the
54
+ # duration of a request.
55
+ def middleware
56
+ @middleware ||= begin
57
+ klass = Class.new
58
+ klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
59
+ class << self
60
+ def name
61
+ "ActiveSupport::Cache::Strategy::LocalCache"
62
+ end
63
+ alias :to_s :name
64
+ end
65
+
66
+ def initialize(app)
67
+ @app = app
68
+ end
69
+
70
+ def call(env)
71
+ Thread.current[:#{thread_local_key}] = LocalStore.new
72
+ @app.call(env)
73
+ ensure
74
+ Thread.current[:#{thread_local_key}] = nil
75
+ end
76
+ EOS
77
+ klass
78
+ end
79
+ end
80
+
81
+ def clear(options = nil) # :nodoc:
82
+ local_cache.clear(options) if local_cache
83
+ super
84
+ end
85
+
86
+ def cleanup(options = nil) # :nodoc:
87
+ local_cache.clear(options) if local_cache
88
+ super
89
+ end
90
+
91
+ def increment(name, amount = 1, options = nil) # :nodoc:
92
+ value = bypass_local_cache{super}
93
+ if local_cache
94
+ local_cache.mute do
95
+ if value
96
+ local_cache.write(name, value, options)
97
+ else
98
+ local_cache.delete(name, options)
99
+ end
100
+ end
101
+ end
102
+ value
103
+ end
104
+
105
+ def decrement(name, amount = 1, options = nil) # :nodoc:
106
+ value = bypass_local_cache{super}
107
+ if local_cache
108
+ local_cache.mute do
109
+ if value
110
+ local_cache.write(name, value, options)
111
+ else
112
+ local_cache.delete(name, options)
113
+ end
114
+ end
115
+ end
116
+ value
117
+ end
118
+
119
+ protected
120
+ def read_entry(key, options) # :nodoc:
121
+ if local_cache
122
+ entry = local_cache.read_entry(key, options)
123
+ unless entry
124
+ entry = super
125
+ local_cache.write_entry(key, entry, options)
126
+ end
127
+ entry
128
+ else
129
+ super
130
+ end
131
+ end
132
+
133
+ def write_entry(key, entry, options) # :nodoc:
134
+ local_cache.write_entry(key, entry, options) if local_cache
135
+ super
136
+ end
137
+
138
+ def delete_entry(key, options) # :nodoc:
139
+ local_cache.delete_entry(key, options) if local_cache
140
+ super
141
+ end
142
+
143
+ private
144
+ def thread_local_key
145
+ @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
146
+ end
147
+
148
+ def local_cache
149
+ Thread.current[thread_local_key]
150
+ end
151
+
152
+ def bypass_local_cache
153
+ save_cache = Thread.current[thread_local_key]
154
+ begin
155
+ Thread.current[thread_local_key] = nil
156
+ yield
157
+ ensure
158
+ Thread.current[thread_local_key] = save_cache
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ # Like MemoryStore, but thread-safe.
4
+ class SynchronizedMemoryStore < MemoryStore
5
+ def initialize(*args)
6
+ ActiveSupport::Deprecation.warn('ActiveSupport::Cache::SynchronizedMemoryStore has been deprecated in favor of ActiveSupport::Cache::MemoryStore.', caller)
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,600 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/class/inheritable_attributes'
3
+ require 'active_support/core_ext/kernel/reporting'
4
+ require 'active_support/core_ext/kernel/singleton_class'
5
+
6
+ module ActiveSupport
7
+ # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
8
+ # before or after an alteration of the object state.
9
+ #
10
+ # Mixing in this module allows you to define callbacks in your class.
11
+ #
12
+ # Example:
13
+ # class Storage
14
+ # include ActiveSupport::Callbacks
15
+ #
16
+ # define_callbacks :save
17
+ # end
18
+ #
19
+ # class ConfigStorage < Storage
20
+ # set_callback :save, :before, :saving_message
21
+ # def saving_message
22
+ # puts "saving..."
23
+ # end
24
+ #
25
+ # set_callback :save, :after do |object|
26
+ # puts "saved"
27
+ # end
28
+ #
29
+ # def save
30
+ # run_callbacks :save do
31
+ # puts "- save"
32
+ # end
33
+ # end
34
+ # end
35
+ #
36
+ # config = ConfigStorage.new
37
+ # config.save
38
+ #
39
+ # Output:
40
+ # saving...
41
+ # - save
42
+ # saved
43
+ #
44
+ # Callbacks from parent classes are inherited.
45
+ #
46
+ # Example:
47
+ # class Storage
48
+ # include ActiveSupport::Callbacks
49
+ #
50
+ # define_callbacks :save
51
+ #
52
+ # set_callback :save, :before, :prepare
53
+ # def prepare
54
+ # puts "preparing save"
55
+ # end
56
+ # end
57
+ #
58
+ # class ConfigStorage < Storage
59
+ # set_callback :save, :before, :saving_message
60
+ # def saving_message
61
+ # puts "saving..."
62
+ # end
63
+ #
64
+ # set_callback :save, :after do |object|
65
+ # puts "saved"
66
+ # end
67
+ #
68
+ # def save
69
+ # run_callbacks :save do
70
+ # puts "- save"
71
+ # end
72
+ # end
73
+ # end
74
+ #
75
+ # config = ConfigStorage.new
76
+ # config.save
77
+ #
78
+ # Output:
79
+ # preparing save
80
+ # saving...
81
+ # - save
82
+ # saved
83
+ #
84
+ module Callbacks
85
+ extend Concern
86
+
87
+ def run_callbacks(kind, *args, &block)
88
+ send("_run_#{kind}_callbacks", *args, &block)
89
+ end
90
+
91
+ class Callback
92
+ @@_callback_sequence = 0
93
+
94
+ attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
95
+
96
+ def initialize(chain, filter, kind, options, klass)
97
+ @chain, @kind, @klass = chain, kind, klass
98
+ normalize_options!(options)
99
+
100
+ @per_key = options.delete(:per_key)
101
+ @raw_filter, @options = filter, options
102
+ @filter = _compile_filter(filter)
103
+ @compiled_options = _compile_options(options)
104
+ @callback_id = next_id
105
+
106
+ _compile_per_key_options
107
+ end
108
+
109
+ def clone(chain, klass)
110
+ obj = super()
111
+ obj.chain = chain
112
+ obj.klass = klass
113
+ obj.per_key = @per_key.dup
114
+ obj.options = @options.dup
115
+ obj.per_key[:if] = @per_key[:if].dup
116
+ obj.per_key[:unless] = @per_key[:unless].dup
117
+ obj.options[:if] = @options[:if].dup
118
+ obj.options[:unless] = @options[:unless].dup
119
+ obj
120
+ end
121
+
122
+ def normalize_options!(options)
123
+ options[:if] = Array.wrap(options[:if])
124
+ options[:unless] = Array.wrap(options[:unless])
125
+
126
+ options[:per_key] ||= {}
127
+ options[:per_key][:if] = Array.wrap(options[:per_key][:if])
128
+ options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
129
+ end
130
+
131
+ def name
132
+ chain.name
133
+ end
134
+
135
+ def next_id
136
+ @@_callback_sequence += 1
137
+ end
138
+
139
+ def matches?(_kind, _filter)
140
+ @kind == _kind && @filter == _filter
141
+ end
142
+
143
+ def _update_filter(filter_options, new_options)
144
+ filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
145
+ filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
146
+ end
147
+
148
+ def recompile!(_options, _per_key)
149
+ _update_filter(self.options, _options)
150
+ _update_filter(self.per_key, _per_key)
151
+
152
+ @callback_id = next_id
153
+ @filter = _compile_filter(@raw_filter)
154
+ @compiled_options = _compile_options(@options)
155
+ _compile_per_key_options
156
+ end
157
+
158
+ def _compile_per_key_options
159
+ key_options = _compile_options(@per_key)
160
+
161
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
162
+ def _one_time_conditions_valid_#{@callback_id}?
163
+ true #{key_options[0]}
164
+ end
165
+ RUBY_EVAL
166
+ end
167
+
168
+ # This will supply contents for before and around filters, and no
169
+ # contents for after filters (for the forward pass).
170
+ def start(key=nil, object=nil)
171
+ return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
172
+
173
+ # options[0] is the compiled form of supplied conditions
174
+ # options[1] is the "end" for the conditional
175
+ #
176
+ if @kind == :before || @kind == :around
177
+ if @kind == :before
178
+ # if condition # before_save :filter_name, :if => :condition
179
+ # filter_name
180
+ # end
181
+ filter = <<-RUBY_EVAL
182
+ unless halted
183
+ result = #{@filter}
184
+ halted = (#{chain.config[:terminator]})
185
+ end
186
+ RUBY_EVAL
187
+
188
+ [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
189
+ else
190
+ # Compile around filters with conditions into proxy methods
191
+ # that contain the conditions.
192
+ #
193
+ # For `around_save :filter_name, :if => :condition':
194
+ #
195
+ # def _conditional_callback_save_17
196
+ # if condition
197
+ # filter_name do
198
+ # yield self
199
+ # end
200
+ # else
201
+ # yield self
202
+ # end
203
+ # end
204
+ #
205
+ name = "_conditional_callback_#{@kind}_#{next_id}"
206
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
207
+ def #{name}(halted)
208
+ #{@compiled_options[0] || "if true"} && !halted
209
+ #{@filter} do
210
+ yield self
211
+ end
212
+ else
213
+ yield self
214
+ end
215
+ end
216
+ RUBY_EVAL
217
+ "#{name}(halted) do"
218
+ end
219
+ end
220
+ end
221
+
222
+ # This will supply contents for around and after filters, but not
223
+ # before filters (for the backward pass).
224
+ def end(key=nil, object=nil)
225
+ return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
226
+
227
+ if @kind == :around || @kind == :after
228
+ # if condition # after_save :filter_name, :if => :condition
229
+ # filter_name
230
+ # end
231
+ if @kind == :after
232
+ [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
233
+ else
234
+ "end"
235
+ end
236
+ end
237
+ end
238
+
239
+ private
240
+
241
+ # Options support the same options as filters themselves (and support
242
+ # symbols, string, procs, and objects), so compile a conditional
243
+ # expression based on the options
244
+ def _compile_options(options)
245
+ return [] if options[:if].empty? && options[:unless].empty?
246
+
247
+ conditions = []
248
+
249
+ unless options[:if].empty?
250
+ conditions << Array.wrap(_compile_filter(options[:if]))
251
+ end
252
+
253
+ unless options[:unless].empty?
254
+ conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
255
+ end
256
+
257
+ ["if #{conditions.flatten.join(" && ")}", "end"]
258
+ end
259
+
260
+ # Filters support:
261
+ #
262
+ # Arrays:: Used in conditions. This is used to specify
263
+ # multiple conditions. Used internally to
264
+ # merge conditions from skip_* filters
265
+ # Symbols:: A method to call
266
+ # Strings:: Some content to evaluate
267
+ # Procs:: A proc to call with the object
268
+ # Objects:: An object with a before_foo method on it to call
269
+ #
270
+ # All of these objects are compiled into methods and handled
271
+ # the same after this point:
272
+ #
273
+ # Arrays:: Merged together into a single filter
274
+ # Symbols:: Already methods
275
+ # Strings:: class_eval'ed into methods
276
+ # Procs:: define_method'ed into methods
277
+ # Objects::
278
+ # a method is created that calls the before_foo method
279
+ # on the object.
280
+ #
281
+ def _compile_filter(filter)
282
+ method_name = "_callback_#{@kind}_#{next_id}"
283
+ case filter
284
+ when Array
285
+ filter.map {|f| _compile_filter(f)}
286
+ when Symbol
287
+ filter
288
+ when String
289
+ "(#{filter})"
290
+ when Proc
291
+ @klass.send(:define_method, method_name, &filter)
292
+ return method_name if filter.arity <= 0
293
+
294
+ method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
295
+ else
296
+ @klass.send(:define_method, "#{method_name}_object") { filter }
297
+
298
+ _normalize_legacy_filter(kind, filter)
299
+ scopes = Array.wrap(chain.config[:scope])
300
+ method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
301
+
302
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
303
+ def #{method_name}(&blk)
304
+ #{method_name}_object.send(:#{method_to_call}, self, &blk)
305
+ end
306
+ RUBY_EVAL
307
+
308
+ method_name
309
+ end
310
+ end
311
+
312
+ def _normalize_legacy_filter(kind, filter)
313
+ if !filter.respond_to?(kind) && filter.respond_to?(:filter)
314
+ filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
315
+ def #{kind}(context, &block) filter(context, &block) end
316
+ RUBY_EVAL
317
+ elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
318
+ def filter.around(context)
319
+ should_continue = before(context)
320
+ yield if should_continue
321
+ after(context)
322
+ end
323
+ end
324
+ end
325
+ end
326
+
327
+ # An Array with a compile method
328
+ class CallbackChain < Array
329
+ attr_reader :name, :config
330
+
331
+ def initialize(name, config)
332
+ @name = name
333
+ @config = {
334
+ :terminator => "false",
335
+ :rescuable => false,
336
+ :scope => [ :kind ]
337
+ }.merge(config)
338
+ end
339
+
340
+ def compile(key=nil, object=nil)
341
+ method = []
342
+ method << "value = nil"
343
+ method << "halted = false"
344
+
345
+ each do |callback|
346
+ method << callback.start(key, object)
347
+ end
348
+
349
+ if config[:rescuable]
350
+ method << "rescued_error = nil"
351
+ method << "begin"
352
+ end
353
+
354
+ method << "value = yield if block_given? && !halted"
355
+
356
+ if config[:rescuable]
357
+ method << "rescue Exception => e"
358
+ method << "rescued_error = e"
359
+ method << "end"
360
+ end
361
+
362
+ reverse_each do |callback|
363
+ method << callback.end(key, object)
364
+ end
365
+
366
+ method << "raise rescued_error if rescued_error" if config[:rescuable]
367
+ method << "halted ? false : (block_given? ? value : true)"
368
+ method.compact.join("\n")
369
+ end
370
+ end
371
+
372
+ module ClassMethods
373
+ # Make the run_callbacks :save method. The generated method takes
374
+ # a block that it'll yield to. It'll call the before and around filters
375
+ # in order, yield the block, and then run the after filters.
376
+ #
377
+ # run_callbacks :save do
378
+ # save
379
+ # end
380
+ #
381
+ # The run_callbacks :save method can optionally take a key, which
382
+ # will be used to compile an optimized callback method for each
383
+ # key. See #define_callbacks for more information.
384
+ #
385
+ def __define_runner(symbol) #:nodoc:
386
+ send("_update_#{symbol}_superclass_callbacks")
387
+ body = send("_#{symbol}_callbacks").compile(nil)
388
+
389
+ silence_warnings do
390
+ undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
391
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
392
+ def _run_#{symbol}_callbacks(key = nil, &blk)
393
+ @_initialized_#{symbol}_callbacks ||= begin
394
+ if self.class.send("_update_#{symbol}_superclass_callbacks")
395
+ self.class.__define_runner(#{symbol.inspect})
396
+ return _run_#{symbol}_callbacks(key, &blk)
397
+ end
398
+ true
399
+ end
400
+
401
+ if key
402
+ name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
403
+
404
+ unless respond_to?(name)
405
+ self.class.__create_keyed_callback(name, :#{symbol}, self, &blk)
406
+ end
407
+
408
+ send(name, &blk)
409
+ else
410
+ #{body}
411
+ end
412
+ end
413
+ private :_run_#{symbol}_callbacks
414
+ RUBY_EVAL
415
+ end
416
+ end
417
+
418
+ # This is called the first time a callback is called with a particular
419
+ # key. It creates a new callback method for the key, calculating
420
+ # which callbacks can be omitted because of per_key conditions.
421
+ #
422
+ def __create_keyed_callback(name, kind, object, &blk) #:nodoc:
423
+ @_keyed_callbacks ||= {}
424
+ @_keyed_callbacks[name] ||= begin
425
+ str = send("_#{kind}_callbacks").compile(name, object)
426
+ class_eval "def #{name}() #{str} end", __FILE__, __LINE__
427
+ true
428
+ end
429
+ end
430
+
431
+ # This is used internally to append, prepend and skip callbacks to the
432
+ # CallbackChain.
433
+ #
434
+ def __update_callbacks(name, filters = [], block = nil) #:nodoc:
435
+ send("_update_#{name}_superclass_callbacks")
436
+
437
+ type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
438
+ options = filters.last.is_a?(Hash) ? filters.pop : {}
439
+ filters.unshift(block) if block
440
+
441
+ chain = send("_#{name}_callbacks")
442
+ yield chain, type, filters, options if block_given?
443
+
444
+ __define_runner(name)
445
+ end
446
+
447
+ # Set callbacks for a previously defined callback.
448
+ #
449
+ # Syntax:
450
+ # set_callback :save, :before, :before_meth
451
+ # set_callback :save, :after, :after_meth, :if => :condition
452
+ # set_callback :save, :around, lambda { |r| stuff; yield; stuff }
453
+ #
454
+ # Use skip_callback to skip any defined one.
455
+ #
456
+ # When creating or skipping callbacks, you can specify conditions that
457
+ # are always the same for a given key. For instance, in ActionPack,
458
+ # we convert :only and :except conditions into per-key conditions.
459
+ #
460
+ # before_filter :authenticate, :except => "index"
461
+ # becomes
462
+ # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
463
+ #
464
+ # Per-Key conditions are evaluated only once per use of a given key.
465
+ # In the case of the above example, you would do:
466
+ #
467
+ # run_callbacks(:dispatch, action_name) { ... dispatch stuff ... }
468
+ #
469
+ # In that case, each action_name would get its own compiled callback
470
+ # method that took into consideration the per_key conditions. This
471
+ # is a speed improvement for ActionPack.
472
+ #
473
+ def set_callback(name, *filter_list, &block)
474
+ __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
475
+ filters.map! do |filter|
476
+ removed = chain.delete_if {|c| c.matches?(type, filter) }
477
+ send("_removed_#{name}_callbacks").push(*removed)
478
+ Callback.new(chain, filter, type, options.dup, self)
479
+ end
480
+
481
+ options[:prepend] ? chain.unshift(*filters) : chain.push(*filters)
482
+ end
483
+ end
484
+
485
+ # Skip a previously defined callback for a given type.
486
+ #
487
+ def skip_callback(name, *filter_list, &block)
488
+ __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
489
+ filters.each do |filter|
490
+ filter = chain.find {|c| c.matches?(type, filter) }
491
+
492
+ if filter && options.any?
493
+ new_filter = filter.clone(chain, self)
494
+ chain.insert(chain.index(filter), new_filter)
495
+ new_filter.recompile!(options, options[:per_key] || {})
496
+ end
497
+
498
+ chain.delete(filter)
499
+ send("_removed_#{name}_callbacks") << filter
500
+ end
501
+ end
502
+ end
503
+
504
+ # Reset callbacks for a given type.
505
+ #
506
+ def reset_callbacks(symbol)
507
+ callbacks = send("_#{symbol}_callbacks")
508
+ callbacks.clear
509
+ send("_removed_#{symbol}_callbacks").concat(callbacks)
510
+ __define_runner(symbol)
511
+ end
512
+
513
+ # Define callbacks types.
514
+ #
515
+ # ==== Example
516
+ #
517
+ # define_callbacks :validate
518
+ #
519
+ # ==== Options
520
+ #
521
+ # * <tt>:terminator</tt> - Indicates when a before filter is considered
522
+ # to be halted.
523
+ #
524
+ # define_callbacks :validate, :terminator => "result == false"
525
+ #
526
+ # In the example above, if any before validate callbacks returns false,
527
+ # other callbacks are not executed. Defaults to "false".
528
+ #
529
+ # * <tt>:rescuable</tt> - By default, after filters are not executed if
530
+ # the given block or an before_filter raises an error. Supply :rescuable => true
531
+ # to change this behavior.
532
+ #
533
+ # * <tt>:scope</tt> - Show which methods should be executed when a class
534
+ # is given as callback:
535
+ #
536
+ # define_callbacks :filters, :scope => [ :kind ]
537
+ #
538
+ # When a class is given:
539
+ #
540
+ # before_filter MyFilter
541
+ #
542
+ # It will call the type of the filter in the given class, which in this
543
+ # case, is "before".
544
+ #
545
+ # If, for instance, you supply the given scope:
546
+ #
547
+ # define_callbacks :validate, :scope => [ :kind, :name ]
548
+ #
549
+ # It will call "#{kind}_#{name}" in the given class. So "before_validate"
550
+ # will be called in the class below:
551
+ #
552
+ # before_validate MyValidation
553
+ #
554
+ # Defaults to :kind.
555
+ #
556
+ def define_callbacks(*callbacks)
557
+ config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
558
+ callbacks.each do |callback|
559
+ extlib_inheritable_reader("_#{callback}_callbacks") do
560
+ CallbackChain.new(callback, config)
561
+ end
562
+
563
+ extlib_inheritable_reader("_removed_#{callback}_callbacks") do
564
+ []
565
+ end
566
+
567
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
568
+ def self._#{callback}_superclass_callbacks
569
+ if superclass.respond_to?(:_#{callback}_callbacks)
570
+ superclass._#{callback}_callbacks + superclass._#{callback}_superclass_callbacks
571
+ else
572
+ []
573
+ end
574
+ end
575
+
576
+ def self._update_#{callback}_superclass_callbacks
577
+ changed, index = false, 0
578
+
579
+ callbacks = (_#{callback}_superclass_callbacks -
580
+ _#{callback}_callbacks) - _removed_#{callback}_callbacks
581
+
582
+ callbacks.each do |callback|
583
+ if new_index = _#{callback}_callbacks.index(callback)
584
+ index = new_index + 1
585
+ else
586
+ changed = true
587
+ _#{callback}_callbacks.insert(index, callback)
588
+ index = index + 1
589
+ end
590
+ end
591
+ changed
592
+ end
593
+ METHOD
594
+
595
+ __define_runner(callback)
596
+ end
597
+ end
598
+ end
599
+ end
600
+ end