marty 0.5.15 → 0.5.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +27 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +23 -0
  5. data/Gemfile +23 -0
  6. data/INDEPENDENCE_ISSUES.md +23 -0
  7. data/app/assets/images/marty/.gitkeep +0 -0
  8. data/app/components/marty/report_form.rb +9 -4
  9. data/gemini_deprecations.md +6 -0
  10. data/lib/marty/data_change.rb +99 -0
  11. data/lib/marty/data_conversion.rb +11 -3
  12. data/lib/marty/data_exporter.rb +9 -0
  13. data/lib/marty/version.rb +1 -1
  14. data/marty.gemspec +35 -0
  15. data/script/rails +8 -0
  16. data/spec/controllers/application_controller_spec.rb +52 -0
  17. data/spec/controllers/job_controller_spec.rb +226 -0
  18. data/spec/controllers/rpc_controller_spec.rb +379 -0
  19. data/spec/controllers/rpc_import_spec.rb +45 -0
  20. data/spec/dummy/README.rdoc +261 -0
  21. data/spec/dummy/Rakefile +7 -0
  22. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  23. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  25. data/spec/dummy/app/controllers/components_controller.rb +7 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  27. data/spec/dummy/app/mailers/.gitkeep +0 -0
  28. data/spec/dummy/app/models/.gitkeep +0 -0
  29. data/spec/dummy/app/models/gemini/amortization_type.rb +5 -0
  30. data/spec/dummy/app/models/gemini/bud_category.rb +7 -0
  31. data/spec/dummy/app/models/gemini/entity.rb +2 -0
  32. data/spec/dummy/app/models/gemini/extras/data_import.rb +5 -0
  33. data/spec/dummy/app/models/gemini/extras/settlement_import.rb +28 -0
  34. data/spec/dummy/app/models/gemini/fannie_bup.rb +29 -0
  35. data/spec/dummy/app/models/gemini/grouping.rb +8 -0
  36. data/spec/dummy/app/models/gemini/grouping_head_version.rb +14 -0
  37. data/spec/dummy/app/models/gemini/head.rb +7 -0
  38. data/spec/dummy/app/models/gemini/head_version.rb +14 -0
  39. data/spec/dummy/app/models/gemini/helper.rb +44 -0
  40. data/spec/dummy/app/models/gemini/loan_program.rb +11 -0
  41. data/spec/dummy/app/models/gemini/mortgage_type.rb +5 -0
  42. data/spec/dummy/app/models/gemini/simple.rb +6 -0
  43. data/spec/dummy/app/models/gemini/streamline_type.rb +16 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/config/application.rb +82 -0
  47. data/spec/dummy/config/boot.rb +10 -0
  48. data/spec/dummy/config/database.yml.example +10 -0
  49. data/spec/dummy/config/environment.rb +5 -0
  50. data/spec/dummy/config/environments/development.rb +35 -0
  51. data/spec/dummy/config/environments/production.rb +69 -0
  52. data/spec/dummy/config/environments/test.rb +39 -0
  53. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/spec/dummy/config/initializers/delayed_job.rb +5 -0
  55. data/spec/dummy/config/initializers/inflections.rb +15 -0
  56. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  57. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  58. data/spec/dummy/config/initializers/session_store.rb +8 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +5 -0
  61. data/spec/dummy/config/routes.rb +12 -0
  62. data/spec/dummy/db/migrate/20140801000000_create_groupings.rb +11 -0
  63. data/spec/dummy/db/migrate/20150406171536_create_categories.rb +27 -0
  64. data/spec/dummy/db/migrate/20150408200916_create_loan_programs.rb +26 -0
  65. data/spec/dummy/db/migrate/20150408201429_create_types.rb +21 -0
  66. data/spec/dummy/db/migrate/20150420000001_create_heads.rb +14 -0
  67. data/spec/dummy/db/migrate/20150420000002_create_head_versions.rb +15 -0
  68. data/spec/dummy/db/migrate/20150420000003_create_grouping_head_versions.rb +12 -0
  69. data/spec/dummy/db/migrate/20151023000001_create_simple.rb +12 -0
  70. data/spec/dummy/db/seeds.rb +8 -0
  71. data/spec/dummy/delorean/blame_report.dl +171 -0
  72. data/spec/dummy/delorean/data_report.dl +105 -0
  73. data/spec/dummy/delorean/fields.dl +52 -0
  74. data/spec/dummy/delorean/styles.dl +134 -0
  75. data/spec/dummy/lib/assets/.gitkeep +0 -0
  76. data/spec/dummy/lib/class_list.rb +3 -0
  77. data/spec/dummy/log/.gitkeep +0 -0
  78. data/spec/dummy/public/404.html +26 -0
  79. data/spec/dummy/public/422.html +26 -0
  80. data/spec/dummy/public/500.html +25 -0
  81. data/spec/dummy/public/favicon.ico +0 -0
  82. data/spec/dummy/public/icons/READ.txt +3 -0
  83. data/spec/dummy/public/icons/application_cascade.png +0 -0
  84. data/spec/dummy/public/icons/application_delete.png +0 -0
  85. data/spec/dummy/public/icons/application_put.png +0 -0
  86. data/spec/dummy/public/icons/application_view_detail.png +0 -0
  87. data/spec/dummy/public/icons/arrow_in.png +0 -0
  88. data/spec/dummy/public/icons/arrow_refresh.png +0 -0
  89. data/spec/dummy/public/icons/database_save.png +0 -0
  90. data/spec/dummy/public/icons/door_in.png +0 -0
  91. data/spec/dummy/public/icons/door_out.png +0 -0
  92. data/spec/dummy/public/icons/group.png +0 -0
  93. data/spec/dummy/public/icons/page_lightning.png +0 -0
  94. data/spec/dummy/public/icons/printer.png +0 -0
  95. data/spec/dummy/public/icons/report_disk.png +0 -0
  96. data/spec/dummy/public/icons/report_go.png +0 -0
  97. data/spec/dummy/public/icons/report_magnify.png +0 -0
  98. data/spec/dummy/public/icons/script.png +0 -0
  99. data/spec/dummy/public/icons/script_add.png +0 -0
  100. data/spec/dummy/public/icons/script_go.png +0 -0
  101. data/spec/dummy/public/icons/script_key.png +0 -0
  102. data/spec/dummy/public/icons/table_go.png +0 -0
  103. data/spec/dummy/public/icons/time.png +0 -0
  104. data/spec/dummy/public/icons/time_add.png +0 -0
  105. data/spec/dummy/public/icons/time_go.png +0 -0
  106. data/spec/dummy/public/icons/timeline_marker.png +0 -0
  107. data/spec/dummy/public/icons/user_add.png +0 -0
  108. data/spec/dummy/public/icons/user_delete.png +0 -0
  109. data/spec/dummy/public/icons/user_edit.png +0 -0
  110. data/spec/dummy/public/icons/wrench.png +0 -0
  111. data/spec/dummy/script/delayed_job +6 -0
  112. data/spec/dummy/script/rails +6 -0
  113. data/spec/features/javascripts/job_dashboard_live_search.js.coffee +8 -0
  114. data/spec/features/javascripts/login.js.coffee +8 -0
  115. data/spec/features/jobs_dashboard_netzke_spec.rb +24 -0
  116. data/spec/features/jobs_dashboard_spec.rb +49 -0
  117. data/spec/fixtures/scripts/load_tests/script1.dl +2 -0
  118. data/spec/fixtures/scripts/load_tests/script2.dl +2 -0
  119. data/spec/job_helper.rb +102 -0
  120. data/spec/lib/data_exporter_spec.rb +71 -0
  121. data/spec/lib/data_importer_spec.rb +461 -0
  122. data/spec/lib/xl_spec.rb +198 -0
  123. data/spec/lib/xl_styles_spec.rb +115 -0
  124. data/spec/models/api_auth_spec.rb +187 -0
  125. data/spec/models/posting_spec.rb +107 -0
  126. data/spec/models/promise_spec.rb +65 -0
  127. data/spec/models/script_spec.rb +187 -0
  128. data/spec/models/user_spec.rb +68 -0
  129. data/spec/requests/routes_spec.rb +12 -0
  130. data/spec/spec_helper.rb +61 -0
  131. data/spec/support/clean_db_helpers.rb +18 -0
  132. data/spec/support/delayed_job_helpers.rb +12 -0
  133. data/spec/support/user_helpers.rb +12 -0
  134. metadata +139 -89
  135. data/app/components/marty/auth_app.rb~ +0 -51
  136. data/app/components/marty/auth_app/javascripts/auth_app.js~ +0 -91
  137. data/app/components/marty/cm_form_panel.rb~ +0 -5
  138. data/app/components/marty/cm_grid_panel.rb~ +0 -35
  139. data/app/components/marty/data_import_view.rb~ +0 -142
  140. data/app/components/marty/extras/layout.rb~ +0 -46
  141. data/app/components/marty/live_search_grid_panel.rb~ +0 -49
  142. data/app/components/marty/main_auth_app.rb~ +0 -238
  143. data/app/components/marty/mcfly_grid_panel.rb~ +0 -80
  144. data/app/components/marty/new_posting_form.rb~ +0 -46
  145. data/app/components/marty/new_posting_window.rb~ +0 -21
  146. data/app/components/marty/pivot_grid.rb +0 -52
  147. data/app/components/marty/pivot_grid/endpoints.rb +0 -45
  148. data/app/components/marty/pivot_grid/javascripts/extensions.js +0 -150
  149. data/app/components/marty/pivot_grid/javascripts/pivot_grid.js +0 -86
  150. data/app/components/marty/pivot_grid/services.rb +0 -44
  151. data/app/components/marty/posting_grid.rb~ +0 -140
  152. data/app/components/marty/promise_view.rb~ +0 -157
  153. data/app/components/marty/promise_view/stylesheets/promise_view.css~ +0 -15
  154. data/app/components/marty/report_form.rb~ +0 -217
  155. data/app/components/marty/report_select.rb~ +0 -133
  156. data/app/components/marty/reporting.rb~ +0 -39
  157. data/app/components/marty/script_detail.rb~ +0 -430
  158. data/app/components/marty/script_form.rb~ +0 -233
  159. data/app/components/marty/script_form/javascripts/Ext.ux.form.field.CodeMirror.js~ +0 -909
  160. data/app/components/marty/script_grid.rb~ +0 -99
  161. data/app/components/marty/script_tester.rb~ +0 -213
  162. data/app/components/marty/scripting.rb~ +0 -124
  163. data/app/components/marty/select_report.rb~ +0 -143
  164. data/app/components/marty/simple_app.rb~ +0 -101
  165. data/app/components/marty/tag_grid.rb~ +0 -89
  166. data/app/components/marty/tree_panel.rb~ +0 -256
  167. data/app/components/marty/tree_panel/javascripts/tree_panel.js~ +0 -317
  168. data/app/components/marty/user_pivot.rb +0 -128
  169. data/app/components/marty/user_view.rb~ +0 -188
  170. data/app/controllers/marty/application_controller.rb~ +0 -133
  171. data/app/controllers/marty/components_controller.rb~ +0 -37
  172. data/app/controllers/marty/job_controller.rb~ +0 -28
  173. data/app/controllers/marty/rpc_controller.rb~ +0 -61
  174. data/app/helpers/marty/script_set.rb~ +0 -59
  175. data/app/models/marty/api_auth.rb~ +0 -48
  176. data/app/models/marty/data_change.rb~ +0 -141
  177. data/app/models/marty/enum.rb~ +0 -16
  178. data/app/models/marty/import_type.rb~ +0 -48
  179. data/app/models/marty/poop.rb~ +0 -169
  180. data/app/models/marty/posting.rb~ +0 -86
  181. data/app/models/marty/posting_type.rb~ +0 -21
  182. data/app/models/marty/promise.rb~ +0 -196
  183. data/app/models/marty/role.rb~ +0 -10
  184. data/app/models/marty/script.rb~ +0 -62
  185. data/app/models/marty/tag.rb~ +0 -91
  186. data/app/models/marty/user.rb~ +0 -148
  187. data/app/models/marty/user_role.rb~ +0 -13
  188. data/app/views/layouts/marty/application.html.erb~ +0 -11
  189. data/config/routes.rb~ +0 -10
  190. data/db/migrate/019_create_marty_postings.rb~ +0 -19
  191. data/db/migrate/095_create_marty_tags.rb~ +0 -19
  192. data/lib/marty.rb~ +0 -13
  193. data/lib/marty/content_handler.rb~ +0 -93
  194. data/lib/marty/data_exporter.rb~ +0 -137
  195. data/lib/marty/data_importer.rb~ +0 -114
  196. data/lib/marty/data_row_processor.rb~ +0 -206
  197. data/lib/marty/drop_folder_hook.rb~ +0 -17
  198. data/lib/marty/folder_hook.rb~ +0 -9
  199. data/lib/marty/lazy_column_loader.rb~ +0 -47
  200. data/lib/marty/mcfly_query.rb~ +0 -188
  201. data/lib/marty/migrations.rb~ +0 -65
  202. data/lib/marty/monkey.rb~ +0 -160
  203. data/lib/marty/permissions.rb~ +0 -69
  204. data/lib/marty/promise.rb~ +0 -41
  205. data/lib/marty/promise_job.rb~ +0 -121
  206. data/lib/marty/promise_proxy.rb~ +0 -69
  207. data/lib/marty/util.rb~ +0 -80
  208. data/lib/marty/version.rb~ +0 -3
  209. data/lib/marty/xl.rb~ +0 -526
  210. data/lib/pyxll/README.txt~ +0 -16
  211. data/lib/pyxll/gemini.py~ +0 -110
  212. data/lib/pyxll/pyxll.cfg~ +0 -12
@@ -1,169 +0,0 @@
1
- class Marty::Promise < Marty::Base
2
-
3
- # default timeout (seconds) to wait for promise values
4
- DEFAULT_PROMISE_TIMEOUT = 30
5
-
6
- # default timeout (seconds) to wait for jobs to start
7
- DEFAULT_JOB_TIMEOUT = 10
8
-
9
- attr_accessible :title,
10
- :cformat,
11
- :parent_id,
12
- :job_id,
13
- :status,
14
- :result,
15
- :start_dt,
16
- :end_dt
17
-
18
- serialize :result, Hash
19
-
20
- validates_presence_of :title
21
-
22
- has_many :children, foreign_key: 'parent_id', class_name: "Marty::Promise"
23
- belongs_to :parent, class_name: "Marty::Promise"
24
-
25
- def raw_conn
26
- self.class.connection.raw_connection
27
- end
28
-
29
- def pg_notify
30
- raw_conn.async_exec("NOTIFY promise_#{id}")
31
- end
32
-
33
- def set_start
34
- log "LOGLOG #{Rails.logger}"
35
-
36
- if self.start_dt || self.result != {}
37
- Marty::Util.logger.error("promise already started: #{self}")
38
- return
39
- end
40
-
41
- # mark promise as started
42
- self.start_dt = DateTime.now
43
- self.save!
44
- end
45
-
46
- def set_result(res)
47
- log "SETRES #{Process.pid} #{res} #{self}"
48
-
49
- # promise must have been started and not yet ended
50
- if !self.start_dt || self.end_dt || self.result != {}
51
- log "SETERR #{Process.pid} #{self}"
52
- Marty::Util.logger.error("unexpected promise state: #{self}")
53
- return
54
- end
55
-
56
- raise "bad result" unless res.is_a?(Hash)
57
-
58
- self.status = res["error"].nil?
59
- self.result = res
60
-
61
- # update title/format from result hash (somewhat hacky)
62
- self.title = res["title"].to_s if res["title"]
63
- self.cformat = res["format"].to_s if res["format"]
64
-
65
- # mark promise as ended
66
- self.end_dt = DateTime.now
67
- self.save!
68
-
69
- log "NOTIFY #{Process.pid}"
70
- pg_notify
71
- end
72
-
73
- def to_s
74
- inspect
75
- end
76
-
77
- def log(msg)
78
- open('/tmp/dj.out', 'a') { |f| f.puts msg }
79
- end
80
-
81
- def wait_for_my_notify(timeout)
82
- while true do
83
- # FIXME: we keep using the same timeout. The timeout should be
84
- # reduced by total time spent here.
85
- n = raw_conn.wait_for_notify(timeout)
86
- return n if !n || n=="promise_#{id}"
87
- end
88
- end
89
-
90
- def latest
91
- # latest uncached version
92
- Marty::Promise.uncached {Marty::Promise.find(id)}
93
- end
94
-
95
- def work_off_job(job)
96
- # Create a temporary worker to work off the job
97
- Delayed::Job.where(id: job.id).
98
- update_all(locked_at: Delayed::Job.db_time_now, locked_by: "Temp")
99
- w = Delayed::Worker.new
100
- w.run(job)
101
- end
102
-
103
- def wait_for_result(timeout)
104
- return self.result if self.result != {}
105
-
106
- # FIXME: instead of using latest(), should look at how delayed
107
- # jobs are loaded. i.e. use reset+reload.
108
-
109
- begin
110
- # start listening on promise's notification
111
- raw_conn.exec("LISTEN promise_#{id}")
112
-
113
- last = latest
114
-
115
- # if job hasn't started yet, wait for it to start
116
- if !last.start_dt
117
- log "AAAA #{Process.pid} #{last}"
118
-
119
- job = Delayed::Job.find_by_id(last.job_id)
120
- job.reload if job # paranoid
121
-
122
- if !job && job.locked_at
123
- # job has been locked, so it looks like it started already
124
- # and we need to wait for it.
125
- wait_for_my_notify(Marty::Promise::DEFAULT_JOB_TIMEOUT)
126
- else
127
- # work off the job instead of waiting for a real worker to
128
- # pick it up.
129
- log "OFFF #{Process.pid} #{last}"
130
- work_off_job(job)
131
- end
132
-
133
- last = latest
134
-
135
- # we waited for it but it never started. So, mark it with a
136
- # timeout error.
137
- if !last.start_dt
138
- log "TO11 #{Process.pid} #{last}"
139
- return {"error" => "promise #{last.id} timed out (never started)"}
140
- end
141
- end
142
-
143
- # reload promise in case out copy doesn't have a result yet
144
- last = latest unless last.end_dt
145
-
146
- # at this point, we know the promise has already started
147
- if !last.end_dt
148
- wait_for_my_notify(timeout)
149
- log "UUUU #{Process.pid} #{id} #{Time.now.to_f}"
150
- last = latest
151
-
152
- log "XXXX #{Process.pid} #{Time.now.to_f} #{last}"
153
-
154
- if !last.end_dt
155
- log "TO22 #{Process.pid} #{last}"
156
- return {"error" => "promise #{last.id} timed out (didn't end)"}
157
- end
158
- end
159
-
160
- log "RRRR #{Process.pid} #{last} #{Time.now.to_f}"
161
-
162
- last.result
163
- ensure
164
- # Stop listening to the promise notifications
165
- raw_conn.exec("UNLISTEN promise_#{id}")
166
- end
167
-
168
- end
169
- end
@@ -1,86 +0,0 @@
1
- class Marty::Posting < Marty::Base
2
- has_mcfly append_only: true
3
-
4
- mcfly_validates_uniqueness_of :name
5
- validates_presence_of :name, :posting_type_id, :comment
6
-
7
- belongs_to :user, class_name: "Marty::User"
8
- belongs_to :posting_type
9
-
10
- def self.make_name(posting_type, dt)
11
- return 'NOW' if Mcfly.is_infinity(dt)
12
-
13
- return unless posting_type
14
-
15
- # If no dt is provided (which is the usual non-testing case), we
16
- # use Time.now.strftime to name the posting. This has the effect
17
- # of using the host's timezone. i.e. since we're in PST8PDT, names
18
- # will be based off of the Pacific TZ.
19
- dt ||= Time.now
20
- "#{posting_type.name}-#{dt.strftime('%Y%m%d-%H%M')}"
21
- end
22
-
23
- before_validation :set_posting_name
24
- def set_posting_name
25
- posting_type = Marty::PostingType.find_by_id(self.posting_type_id)
26
- self.name = self.class.make_name(posting_type, self.created_dt)
27
- true
28
- end
29
-
30
- def self.do_create(type_name, dt, comment)
31
- posting_type = Marty::PostingType.find_by_name(type_name)
32
-
33
- raise "unknown posting type #{name}" unless posting_type
34
-
35
- o = new
36
- o.posting_type = posting_type
37
- o.comment = comment
38
- o.created_dt = dt
39
- o.save!
40
- o
41
- end
42
-
43
- # Not using mcfly_lookup since we don't want these time-warp markers
44
- # time-warped. FIXME: perhaps this should use mcfly_lookup since we
45
- # may allow deletion of postings. i.e. a new one with same name
46
- # might be created. Or, use regular validates_uniqueness_of instead
47
- # of mcfly_validates_uniqueness_of.
48
- delorean_fn :lookup, sig: 1 do
49
- |name|
50
- self.find_by_name(name)
51
- end
52
-
53
- delorean_fn :lookup_dt, sig: 1 do
54
- |name|
55
- lookup(name).try(:created_dt)
56
- end
57
-
58
- delorean_fn :first_match, sig: 1 do
59
- |dt|
60
- where("created_dt <= ?", dt).order("created_dt DESC").first
61
- end
62
-
63
- delorean_fn :get_latest, sig: [1, 2] do
64
- |limit, is_test=nil|
65
- # IMPORTANT: is_test arg is ignored (KEEP for backward compat.)
66
-
67
- where("created_dt <> 'infinity'").
68
- order("created_dt DESC").limit(limit).to_a
69
- end
70
-
71
- delorean_fn :get_last, sig: [0, 1] do
72
- |posting_type=nil|
73
-
74
- raise "bad posting type" if
75
- posting_type && !posting_type.is_a?(Marty::PostingType)
76
-
77
- q = where("created_dt <> 'infinity'")
78
- q = q.where(posting_type_id: posting_type.id) if posting_type
79
- q.order("created_dt DESC").first
80
- end
81
-
82
- delorean_fn :is_today, sig: 1 do
83
- |posting|
84
- posting.created_dt.to_date == Date.today
85
- end
86
- end
@@ -1,21 +0,0 @@
1
- class Marty::PostingType < Marty::Base
2
- extend Marty::Enum
3
-
4
- <<<<<<< HEAD
5
- # attr_accessible :name
6
- =======
7
- >>>>>>> master
8
- validates_presence_of :name
9
- validates_uniqueness_of :name
10
-
11
- delorean_fn :lookup, sig: 1 do
12
- |name|
13
- self.find_by_name(name)
14
- end
15
-
16
- def self.seed
17
- ['BASE', 'CLOSE', 'INTRA', 'RULE'].each { |type|
18
- create name: type
19
- }
20
- end
21
- end
@@ -1,196 +0,0 @@
1
- class Marty::Promise < Marty::Base
2
- class MarshalResult
3
- def dump(v)
4
- Marshal.dump(v)
5
- end
6
-
7
- def load(v)
8
- # FIXME: Rails4 bytea interface seems to remove the trailing
9
- # \x00 char.
10
- v += "\x00" if v
11
-
12
- # Marshal.load can't handle nil
13
- v.nil? ? {} : Marshal.load(v)
14
- end
15
- end
16
-
17
- # default timeout (seconds) to wait for promise values
18
- DEFAULT_PROMISE_TIMEOUT = Rails.configuration.marty.promise_timeout || 30
19
-
20
- # default timeout (seconds) to wait for jobs to start
21
- DEFAULT_JOB_TIMEOUT = Rails.configuration.marty.job_timeout || 10
22
-
23
- lazy_load :result
24
-
25
- serialize :result, MarshalResult.new
26
-
27
- validates_presence_of :title
28
-
29
- has_many :children,
30
- foreign_key: 'parent_id',
31
- class_name: "Marty::Promise",
32
- dependent: :destroy
33
-
34
- belongs_to :parent, class_name: "Marty::Promise"
35
- belongs_to :user, class_name: "Marty::User"
36
-
37
- def self.cleanup(all=false)
38
- begin
39
- where('start_dt < ? AND parent_id IS NULL',
40
- DateTime.now - (all ? 0.hours : 2.hours)).destroy_all
41
- rescue => exc
42
- Marty::Util.logger.error("promise GC error: #{exc}")
43
- end
44
- end
45
-
46
- def raw_conn
47
- self.class.connection.raw_connection
48
- end
49
-
50
- def pg_notify
51
- raw_conn.async_exec("NOTIFY promise_#{id}")
52
- end
53
-
54
- def set_start
55
- if self.start_dt || self.result != {}
56
- Marty::Util.logger.error("promise already started: #{self}")
57
- return
58
- end
59
-
60
- # mark promise as started
61
- self.start_dt = DateTime.now
62
- self.save!
63
- end
64
-
65
- def set_result(res)
66
- # log "SETRES #{Process.pid} #{res} #{self}"
67
-
68
- # promise must have been started and not yet ended
69
- if !self.start_dt || self.end_dt || self.result != {}
70
- # log "SETERR #{Process.pid} #{self}"
71
- Marty::Util.logger.error("unexpected promise state: #{self}")
72
- return
73
- end
74
-
75
- raise "bad result" unless res.is_a?(Hash)
76
-
77
- self.status = res["error"].nil?
78
- self.result = res
79
-
80
- # update title/format from result hash (somewhat hacky)
81
- self.title = res["title"].to_s if res["title"]
82
- self.cformat = res["format"].to_s if res["format"]
83
-
84
- # mark promise as ended
85
- self.end_dt = DateTime.now
86
- self.save!
87
-
88
- # log "NOTIFY #{Process.pid}"
89
- pg_notify
90
- end
91
-
92
- def to_s
93
- inspect
94
- end
95
-
96
- # def log(msg)
97
- # open('/tmp/dj.out', 'a') { |f| f.puts msg }
98
- # end
99
-
100
- def wait_for_my_notify(timeout)
101
- while true do
102
- # FIXME: we keep using the same timeout. The timeout should be
103
- # reduced by total time spent here.
104
- n = raw_conn.wait_for_notify(timeout)
105
- return n if !n || n=="promise_#{id}"
106
- end
107
- end
108
-
109
- def latest
110
- # FIXME: Not sure if this is idiomatic. What's the best way to
111
- # force AR to reload the promise object? reset+reload doesn't
112
- # seems to work.
113
-
114
- # get latest uncached version
115
- Marty::Promise.uncached {Marty::Promise.find(id)}
116
- end
117
-
118
- def work_off_job(job)
119
- # Create a temporary worker to work off the job
120
- Delayed::Job.where(id: job.id).
121
- update_all(locked_at: Delayed::Job.db_time_now, locked_by: "Temp")
122
- w = Delayed::Worker.new
123
- w.run(job)
124
- end
125
-
126
- def wait_for_result(timeout)
127
- return self.result if self.result != {}
128
-
129
- begin
130
- # start listening on promise's notification
131
- raw_conn.exec("LISTEN promise_#{id}")
132
-
133
- last = latest
134
-
135
- # if job hasn't started yet, wait for it to start
136
- if !last.start_dt
137
- # log "AAAA #{Process.pid} #{last}"
138
-
139
- job = Delayed::Job.find_by_id(last.job_id)
140
- job.reload if job # paranoid
141
-
142
- if !job && job.locked_at
143
- # job has been locked, so it looks like it started already
144
- # and we need to wait for it.
145
- wait_for_my_notify(Marty::Promise::DEFAULT_JOB_TIMEOUT)
146
- else
147
- # work off the job instead of waiting for a real worker to
148
- # pick it up.
149
- # log "OFF0 #{Process.pid} #{last}"
150
- begin
151
- work_off_job(job)
152
- rescue => exc
153
- # log "OFFERR #{exc}"
154
- res = Delorean::Engine.grok_runtime_exception(exc)
155
- last.set_result(res)
156
- end
157
- # log "OFF1 #{Process.pid} #{last}"
158
- end
159
-
160
- last = latest
161
-
162
- # we waited for it but it never started. So, mark it with a
163
- # timeout error.
164
- if !last.start_dt
165
- # log "TO11 #{Process.pid} #{last}"
166
- return {"error" => "promise #{last.id} timed out (never started)"}
167
- end
168
- end
169
-
170
- # reload promise in case out copy doesn't have a result yet
171
- last = latest unless last.end_dt
172
-
173
- # at this point, we know the promise has already started
174
- if !last.end_dt
175
- wait_for_my_notify(timeout)
176
- # log "UUUU #{Process.pid} #{id} #{Time.now.to_f}"
177
- last = latest
178
-
179
- # log "XXXX #{Process.pid} #{Time.now.to_f} #{last}"
180
-
181
- if !last.end_dt
182
- # log "TO22 #{Process.pid} #{last}"
183
- return {"error" => "promise #{last.id} timed out (didn't end)"}
184
- end
185
- end
186
-
187
- # log "RRRR #{Process.pid} #{last} #{Time.now.to_f}"
188
-
189
- last.result
190
- ensure
191
- # Stop listening to the promise notifications
192
- raw_conn.exec("UNLISTEN promise_#{id}")
193
- end
194
-
195
- end
196
- end