warp-thinking-sphinx 1.2.12 → 1.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/README.textile +21 -4
  2. data/VERSION +1 -0
  3. data/features/abstract_inheritance.feature +10 -0
  4. data/features/alternate_primary_key.feature +1 -1
  5. data/features/attribute_updates.feature +22 -5
  6. data/features/deleting_instances.feature +3 -0
  7. data/features/facets.feature +6 -0
  8. data/features/facets_across_model.feature +2 -2
  9. data/features/searching_across_models.feature +1 -1
  10. data/features/searching_by_index.feature +40 -0
  11. data/features/sphinx_scopes.feature +7 -0
  12. data/features/step_definitions/alpha_steps.rb +14 -1
  13. data/features/step_definitions/beta_steps.rb +1 -1
  14. data/features/step_definitions/common_steps.rb +12 -2
  15. data/features/step_definitions/facet_steps.rb +5 -1
  16. data/features/step_definitions/scope_steps.rb +4 -0
  17. data/features/step_definitions/sphinx_steps.rb +8 -4
  18. data/features/sti_searching.feature +5 -0
  19. data/features/support/{db/database.example.yml → database.example.yml} +0 -0
  20. data/features/support/db/fixtures/foxes.rb +3 -0
  21. data/features/support/db/fixtures/music.rb +4 -0
  22. data/features/support/db/fixtures/robots.rb +1 -1
  23. data/features/support/db/fixtures/tags.rb +1 -1
  24. data/features/support/db/migrations/create_alphas.rb +1 -0
  25. data/features/support/db/migrations/create_genres.rb +3 -0
  26. data/features/support/db/migrations/create_music.rb +6 -0
  27. data/features/support/db/migrations/create_robots.rb +1 -2
  28. data/features/support/env.rb +16 -1
  29. data/features/support/models/alpha.rb +12 -0
  30. data/features/support/models/comment.rb +3 -3
  31. data/features/support/models/fox.rb +5 -0
  32. data/features/support/models/genre.rb +3 -0
  33. data/features/support/models/medium.rb +5 -0
  34. data/features/support/models/music.rb +8 -0
  35. data/features/support/models/post.rb +2 -1
  36. data/features/support/models/robot.rb +4 -0
  37. data/lib/cucumber/thinking_sphinx/external_world.rb +8 -0
  38. data/lib/cucumber/thinking_sphinx/internal_world.rb +126 -0
  39. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  40. data/lib/thinking_sphinx.rb +56 -37
  41. data/lib/thinking_sphinx/active_record.rb +257 -192
  42. data/lib/thinking_sphinx/active_record/attribute_updates.rb +10 -12
  43. data/lib/thinking_sphinx/active_record/delta.rb +0 -26
  44. data/lib/thinking_sphinx/active_record/scopes.rb +37 -1
  45. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +1 -1
  46. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +18 -11
  47. data/lib/thinking_sphinx/attribute.rb +19 -4
  48. data/lib/thinking_sphinx/auto_version.rb +22 -0
  49. data/lib/thinking_sphinx/configuration.rb +57 -59
  50. data/lib/thinking_sphinx/context.rb +74 -0
  51. data/lib/thinking_sphinx/deltas.rb +0 -2
  52. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  53. data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
  54. data/lib/thinking_sphinx/facet_search.rb +3 -1
  55. data/lib/thinking_sphinx/index.rb +77 -19
  56. data/lib/thinking_sphinx/index/builder.rb +2 -2
  57. data/lib/thinking_sphinx/search.rb +47 -9
  58. data/lib/thinking_sphinx/search_methods.rb +22 -4
  59. data/lib/thinking_sphinx/source.rb +9 -8
  60. data/lib/thinking_sphinx/source/sql.rb +5 -3
  61. data/lib/thinking_sphinx/tasks.rb +13 -57
  62. data/lib/thinking_sphinx/test.rb +52 -0
  63. data/rails/init.rb +4 -2
  64. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +4 -6
  65. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +0 -0
  66. data/spec/thinking_sphinx/active_record/scopes_spec.rb +177 -0
  67. data/spec/thinking_sphinx/active_record_spec.rb +622 -0
  68. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +0 -0
  69. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +39 -0
  70. data/spec/thinking_sphinx/auto_version_spec.rb +39 -0
  71. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +27 -61
  72. data/spec/thinking_sphinx/context_spec.rb +119 -0
  73. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +0 -0
  74. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +0 -0
  75. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +0 -0
  76. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +0 -0
  77. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +0 -0
  78. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +0 -0
  79. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +24 -0
  80. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +0 -0
  81. data/spec/thinking_sphinx/index_spec.rb +183 -0
  82. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +0 -0
  83. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +0 -0
  84. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +41 -0
  85. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +1 -1
  86. data/spec/thinking_sphinx_spec.rb +204 -0
  87. data/tasks/distribution.rb +6 -20
  88. data/tasks/testing.rb +8 -19
  89. metadata +117 -142
  90. data/VERSION.yml +0 -4
  91. data/features/a.rb +0 -17
  92. data/features/datetime_deltas.feature +0 -66
  93. data/features/delayed_delta_indexing.feature +0 -37
  94. data/features/step_definitions/datetime_delta_steps.rb +0 -15
  95. data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
  96. data/features/support/db/active_record.rb +0 -40
  97. data/features/support/db/fixtures/delayed_betas.rb +0 -10
  98. data/features/support/db/fixtures/thetas.rb +0 -10
  99. data/features/support/db/migrations/create_delayed_betas.rb +0 -17
  100. data/features/support/db/migrations/create_thetas.rb +0 -5
  101. data/features/support/db/mysql.rb +0 -3
  102. data/features/support/db/postgresql.rb +0 -3
  103. data/features/support/models/delayed_beta.rb +0 -7
  104. data/features/support/models/theta.rb +0 -7
  105. data/features/support/post_database.rb +0 -43
  106. data/features/support/z.rb +0 -19
  107. data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
  108. data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
  109. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
  110. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
  111. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
  112. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
  113. data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
  114. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
  115. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
  116. data/spec/lib/thinking_sphinx_spec.rb +0 -162
  117. data/vendor/after_commit/LICENSE +0 -20
  118. data/vendor/after_commit/README +0 -16
  119. data/vendor/after_commit/Rakefile +0 -22
  120. data/vendor/after_commit/init.rb +0 -8
  121. data/vendor/after_commit/lib/after_commit.rb +0 -45
  122. data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
  123. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
  124. data/vendor/after_commit/test/after_commit_test.rb +0 -53
  125. data/vendor/delayed_job/lib/delayed/job.rb +0 -251
  126. data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
  127. data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
  128. data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
  129. data/vendor/riddle/lib/riddle.rb +0 -30
  130. data/vendor/riddle/lib/riddle/client.rb +0 -635
  131. data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
  132. data/vendor/riddle/lib/riddle/client/message.rb +0 -66
  133. data/vendor/riddle/lib/riddle/client/response.rb +0 -84
  134. data/vendor/riddle/lib/riddle/configuration.rb +0 -33
  135. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
  136. data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
  137. data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
  138. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
  139. data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
  140. data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
  141. data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
  142. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
  143. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
  144. data/vendor/riddle/lib/riddle/controller.rb +0 -53
@@ -1,103 +0,0 @@
1
- module AfterCommit
2
- module ConnectionAdapters
3
- def self.included(base)
4
- base.class_eval do
5
- # The commit_db_transaction method gets called when the outermost
6
- # transaction finishes and everything inside commits. We want to
7
- # override it so that after this happens, any records that were saved
8
- # or destroyed within this transaction now get their after_commit
9
- # callback fired.
10
- def commit_db_transaction_with_callback
11
- commit_db_transaction_without_callback
12
- trigger_after_commit_callbacks
13
- trigger_after_commit_on_create_callbacks
14
- trigger_after_commit_on_update_callbacks
15
- trigger_after_commit_on_destroy_callbacks
16
- end
17
- alias_method_chain :commit_db_transaction, :callback
18
-
19
- # In the event the transaction fails and rolls back, nothing inside
20
- # should recieve the after_commit callback.
21
- def rollback_db_transaction_with_callback
22
- rollback_db_transaction_without_callback
23
-
24
- AfterCommit.committed_records = []
25
- AfterCommit.committed_records_on_create = []
26
- AfterCommit.committed_records_on_update = []
27
- AfterCommit.committed_records_on_destroy = []
28
- end
29
- alias_method_chain :rollback_db_transaction, :callback
30
-
31
- protected
32
- def trigger_after_commit_callbacks
33
- # Trigger the after_commit callback for each of the committed
34
- # records.
35
- if AfterCommit.committed_records.any?
36
- AfterCommit.committed_records.each do |record|
37
- begin
38
- record.after_commit_callback
39
- rescue
40
- end
41
- end
42
- end
43
-
44
- # Make sure we clear out our list of committed records now that we've
45
- # triggered the callbacks for each one.
46
- AfterCommit.committed_records = []
47
- end
48
-
49
- def trigger_after_commit_on_create_callbacks
50
- # Trigger the after_commit_on_create callback for each of the committed
51
- # records.
52
- if AfterCommit.committed_records_on_create.any?
53
- AfterCommit.committed_records_on_create.each do |record|
54
- begin
55
- record.after_commit_on_create_callback
56
- rescue
57
- end
58
- end
59
- end
60
-
61
- # Make sure we clear out our list of committed records now that we've
62
- # triggered the callbacks for each one.
63
- AfterCommit.committed_records_on_create = []
64
- end
65
-
66
- def trigger_after_commit_on_update_callbacks
67
- # Trigger the after_commit_on_update callback for each of the committed
68
- # records.
69
- if AfterCommit.committed_records_on_update.any?
70
- AfterCommit.committed_records_on_update.each do |record|
71
- begin
72
- record.after_commit_on_update_callback
73
- rescue
74
- end
75
- end
76
- end
77
-
78
- # Make sure we clear out our list of committed records now that we've
79
- # triggered the callbacks for each one.
80
- AfterCommit.committed_records_on_update = []
81
- end
82
-
83
- def trigger_after_commit_on_destroy_callbacks
84
- # Trigger the after_commit_on_destroy callback for each of the committed
85
- # records.
86
- if AfterCommit.committed_records_on_destroy.any?
87
- AfterCommit.committed_records_on_destroy.each do |record|
88
- begin
89
- record.after_commit_on_destroy_callback
90
- rescue
91
- end
92
- end
93
- end
94
-
95
- # Make sure we clear out our list of committed records now that we've
96
- # triggered the callbacks for each one.
97
- AfterCommit.committed_records_on_destroy = []
98
- end
99
- #end protected
100
- end
101
- end
102
- end
103
- end
@@ -1,53 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
- require 'test/unit'
3
- require 'rubygems'
4
- require 'activerecord'
5
- require 'after_commit'
6
- require 'after_commit/active_record'
7
- require 'after_commit/connection_adapters'
8
-
9
- ActiveRecord::Base.establish_connection({"adapter" => "sqlite3", "database" => 'test.sqlite3'})
10
- begin
11
- ActiveRecord::Base.connection.execute("drop table mock_records");
12
- rescue
13
- end
14
- ActiveRecord::Base.connection.execute("create table mock_records(id int)");
15
-
16
- require File.dirname(__FILE__) + '/../init.rb'
17
-
18
- class MockRecord < ActiveRecord::Base
19
- attr_accessor :after_commit_on_create_called
20
- attr_accessor :after_commit_on_update_called
21
- attr_accessor :after_commit_on_destroy_called
22
-
23
- after_commit_on_create :do_create
24
- def do_create
25
- self.after_commit_on_create_called = true
26
- end
27
-
28
- after_commit_on_update :do_update
29
- def do_update
30
- self.after_commit_on_update_called = true
31
- end
32
-
33
- after_commit_on_create :do_destroy
34
- def do_destroy
35
- self.after_commit_on_destroy_called = true
36
- end
37
- end
38
-
39
- class AfterCommitTest < Test::Unit::TestCase
40
- def test_after_commit_on_create_is_called
41
- assert_equal true, MockRecord.create!.after_commit_on_create_called
42
- end
43
-
44
- def test_after_commit_on_update_is_called
45
- record = MockRecord.create!
46
- record.save
47
- assert_equal true, record.after_commit_on_update_called
48
- end
49
-
50
- def test_after_commit_on_destroy_is_called
51
- assert_equal true, MockRecord.create!.destroy.after_commit_on_destroy_called
52
- end
53
- end
@@ -1,251 +0,0 @@
1
- module Delayed
2
-
3
- class DeserializationError < StandardError
4
- end
5
-
6
- class Job < ActiveRecord::Base
7
- MAX_ATTEMPTS = 25
8
- MAX_RUN_TIME = 4.hours
9
- set_table_name :delayed_jobs
10
-
11
- # By default failed jobs are destroyed after too many attempts.
12
- # If you want to keep them around (perhaps to inspect the reason
13
- # for the failure), set this to false.
14
- cattr_accessor :destroy_failed_jobs
15
- self.destroy_failed_jobs = true
16
-
17
- # Every worker has a unique name which by default is the pid of the process.
18
- # There are some advantages to overriding this with something which survives worker retarts:
19
- # Workers can safely resume working on tasks which are locked by themselves. The worker will assume that it crashed before.
20
- cattr_accessor :worker_name
21
- self.worker_name = "host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
22
-
23
- NextTaskSQL = '(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR (locked_by = ?)) AND failed_at IS NULL'
24
- NextTaskOrder = 'priority DESC, run_at ASC'
25
-
26
- ParseObjectFromYaml = /\!ruby\/\w+\:([^\s]+)/
27
-
28
- cattr_accessor :min_priority, :max_priority
29
- self.min_priority = nil
30
- self.max_priority = nil
31
-
32
- class LockError < StandardError
33
- end
34
-
35
- def self.clear_locks!
36
- update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])
37
- end
38
-
39
- def failed?
40
- failed_at
41
- end
42
- alias_method :failed, :failed?
43
-
44
- def payload_object
45
- @payload_object ||= deserialize(self['handler'])
46
- end
47
-
48
- def name
49
- @name ||= begin
50
- payload = payload_object
51
- if payload.respond_to?(:display_name)
52
- payload.display_name
53
- else
54
- payload.class.name
55
- end
56
- end
57
- end
58
-
59
- def payload_object=(object)
60
- self['handler'] = object.to_yaml
61
- end
62
-
63
- def reschedule(message, backtrace = [], time = nil)
64
- if self.attempts < MAX_ATTEMPTS
65
- time ||= Job.db_time_now + (attempts ** 4) + 5
66
-
67
- self.attempts += 1
68
- self.run_at = time
69
- self.last_error = message + "\n" + backtrace.join("\n")
70
- self.unlock
71
- save!
72
- else
73
- logger.info "* [JOB] PERMANENTLY removing #{self.name} because of #{attempts} consequetive failures."
74
- destroy_failed_jobs ? destroy : update_attribute(:failed_at, Time.now)
75
- end
76
- end
77
-
78
- def self.enqueue(*args, &block)
79
- object = block_given? ? EvaledJob.new(&block) : args.shift
80
-
81
- unless object.respond_to?(:perform) || block_given?
82
- raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
83
- end
84
-
85
- priority = args[0] || 0
86
- run_at = args[1]
87
-
88
- Job.create(:payload_object => object, :priority => priority.to_i, :run_at => run_at)
89
- end
90
-
91
- def self.find_available(limit = 5, max_run_time = MAX_RUN_TIME)
92
-
93
- time_now = db_time_now
94
-
95
- sql = NextTaskSQL.dup
96
-
97
- conditions = [time_now, time_now - max_run_time, worker_name]
98
-
99
- if self.min_priority
100
- sql << ' AND (priority >= ?)'
101
- conditions << min_priority
102
- end
103
-
104
- if self.max_priority
105
- sql << ' AND (priority <= ?)'
106
- conditions << max_priority
107
- end
108
-
109
- conditions.unshift(sql)
110
-
111
- records = ActiveRecord::Base.silence do
112
- find(:all, :conditions => conditions, :order => NextTaskOrder, :limit => limit)
113
- end
114
-
115
- records.sort_by { rand() }
116
- end
117
-
118
- # Get the payload of the next job we can get an exclusive lock on.
119
- # If no jobs are left we return nil
120
- def self.reserve(max_run_time = MAX_RUN_TIME, &block)
121
-
122
- # We get up to 5 jobs from the db. In face we cannot get exclusive access to a job we try the next.
123
- # this leads to a more even distribution of jobs across the worker processes
124
- find_available(5, max_run_time).each do |job|
125
- begin
126
- logger.info "* [JOB] aquiring lock on #{job.name}"
127
- job.lock_exclusively!(max_run_time, worker_name)
128
- runtime = Benchmark.realtime do
129
- invoke_job(job.payload_object, &block)
130
- job.destroy
131
- end
132
- logger.info "* [JOB] #{job.name} completed after %.4f" % runtime
133
-
134
- return job
135
- rescue LockError
136
- # We did not get the lock, some other worker process must have
137
- logger.warn "* [JOB] failed to aquire exclusive lock for #{job.name}"
138
- rescue StandardError => e
139
- job.reschedule e.message, e.backtrace
140
- log_exception(job, e)
141
- return job
142
- end
143
- end
144
-
145
- nil
146
- end
147
-
148
- # This method is used internally by reserve method to ensure exclusive access
149
- # to the given job. It will rise a LockError if it cannot get this lock.
150
- def lock_exclusively!(max_run_time, worker = worker_name)
151
- now = self.class.db_time_now
152
- affected_rows = if locked_by != worker
153
- # We don't own this job so we will update the locked_by name and the locked_at
154
- self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?)", id, (now - max_run_time.to_i)])
155
- else
156
- # We already own this job, this may happen if the job queue crashes.
157
- # Simply resume and update the locked_at
158
- self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
159
- end
160
- raise LockError.new("Attempted to aquire exclusive lock failed") unless affected_rows == 1
161
-
162
- self.locked_at = now
163
- self.locked_by = worker
164
- end
165
-
166
- def unlock
167
- self.locked_at = nil
168
- self.locked_by = nil
169
- end
170
-
171
- # This is a good hook if you need to report job processing errors in additional or different ways
172
- def self.log_exception(job, error)
173
- logger.error "* [JOB] #{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts"
174
- logger.error(error)
175
- end
176
-
177
- def self.work_off(num = 100)
178
- success, failure = 0, 0
179
-
180
- num.times do
181
- job = self.reserve do |j|
182
- begin
183
- j.perform
184
- success += 1
185
- rescue
186
- failure += 1
187
- raise
188
- end
189
- end
190
-
191
- break if job.nil?
192
- end
193
-
194
- return [success, failure]
195
- end
196
-
197
- # Moved into its own method so that new_relic can trace it.
198
- def self.invoke_job(job, &block)
199
- block.call(job)
200
- end
201
-
202
- private
203
-
204
- def deserialize(source)
205
- handler = YAML.load(source) rescue nil
206
-
207
- unless handler.respond_to?(:perform)
208
- if handler.nil? && source =~ ParseObjectFromYaml
209
- handler_class = $1
210
- end
211
- attempt_to_load(handler_class || handler.class)
212
- handler = YAML.load(source)
213
- end
214
-
215
- return handler if handler.respond_to?(:perform)
216
-
217
- raise DeserializationError,
218
- 'Job failed to load: Unknown handler. Try to manually require the appropiate file.'
219
- rescue TypeError, LoadError, NameError => e
220
- raise DeserializationError,
221
- "Job failed to load: #{e.message}. Try to manually require the required file."
222
- end
223
-
224
- # Constantize the object so that ActiveSupport can attempt
225
- # its auto loading magic. Will raise LoadError if not successful.
226
- def attempt_to_load(klass)
227
- klass.constantize
228
- end
229
-
230
- def self.db_time_now
231
- (ActiveRecord::Base.default_timezone == :utc) ? Time.now.utc : Time.now
232
- end
233
-
234
- protected
235
-
236
- def before_save
237
- self.run_at ||= self.class.db_time_now
238
- end
239
-
240
- end
241
-
242
- class EvaledJob
243
- def initialize
244
- @job = yield
245
- end
246
-
247
- def perform
248
- eval(@job)
249
- end
250
- end
251
- end
@@ -1,7 +0,0 @@
1
- module Delayed
2
- module MessageSending
3
- def send_later(method, *args)
4
- Delayed::Job.enqueue Delayed::PerformableMethod.new(self, method.to_sym, args)
5
- end
6
- end
7
- end
@@ -1,55 +0,0 @@
1
- module Delayed
2
- class PerformableMethod < Struct.new(:object, :method, :args)
3
- CLASS_STRING_FORMAT = /^CLASS\:([A-Z][\w\:]+)$/
4
- AR_STRING_FORMAT = /^AR\:([A-Z][\w\:]+)\:(\d+)$/
5
-
6
- def initialize(object, method, args)
7
- raise NoMethodError, "undefined method `#{method}' for #{self.inspect}" unless object.respond_to?(method)
8
-
9
- self.object = dump(object)
10
- self.args = args.map { |a| dump(a) }
11
- self.method = method.to_sym
12
- end
13
-
14
- def display_name
15
- case self.object
16
- when CLASS_STRING_FORMAT then "#{$1}.#{method}"
17
- when AR_STRING_FORMAT then "#{$1}##{method}"
18
- else "Unknown##{method}"
19
- end
20
- end
21
-
22
- def perform
23
- load(object).send(method, *args.map{|a| load(a)})
24
- rescue ActiveRecord::RecordNotFound
25
- # We cannot do anything about objects which were deleted in the meantime
26
- true
27
- end
28
-
29
- private
30
-
31
- def load(arg)
32
- case arg
33
- when CLASS_STRING_FORMAT then $1.constantize
34
- when AR_STRING_FORMAT then $1.constantize.find($2)
35
- else arg
36
- end
37
- end
38
-
39
- def dump(arg)
40
- case arg
41
- when Class then class_to_string(arg)
42
- when ActiveRecord::Base then ar_to_string(arg)
43
- else arg
44
- end
45
- end
46
-
47
- def ar_to_string(obj)
48
- "AR:#{obj.class}:#{obj.id}"
49
- end
50
-
51
- def class_to_string(obj)
52
- "CLASS:#{obj.name}"
53
- end
54
- end
55
- end