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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c0900603f68de4a2d16347f8cd75cad55f92761
4
- data.tar.gz: 0f927363e84dbdb332939f907f2396c766b3ecf2
3
+ metadata.gz: ee57ec5bc50741eda9ea8b4cc109221e1cf2e57c
4
+ data.tar.gz: 486efeb1bdde157cf0eff2ce7c0f67e5999727ae
5
5
  SHA512:
6
- metadata.gz: 620c234afb693a0aea7480e7c299c854f39210256dedf43f6d1e1e0823c9cea7f36b0010dd0b4bffa7d3395375cdc02de3730c9692fda4db5ffc3100bb7d1860
7
- data.tar.gz: 632298cbb98def0ddb8698fcb05eb77c37827169a9483d8a0e57a576b31a75c4f6c4359f7e7fb35d9b480314f8533dd820dd7ba02eb68bc8ecc983d5d8d35cda
6
+ metadata.gz: 44701d2a49b44b9f433e7b32f79d17d1db6d7ea1af47d660721e8f678278a919bf1f5482d6c6321ee5feef7a7b0dff751f2a50713d968a98c9adc4165091ecf8
7
+ data.tar.gz: c16b0685d1a63cd10afd0e3b6b8fefa0336c792bceba6df7946e9ed11a80c5833032728cd866354fe263da42a181d28b03ec11385fe5144ead71cbec16f55a8d
data/.gitignore ADDED
@@ -0,0 +1,27 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ spec/dummy/db/*.sqlite3
5
+ spec/dummy/log/*.log
6
+ spec/dummy/tmp/
7
+ spec/dummy/.sass-cache
8
+
9
+ # Ignore schema
10
+ /db/schema.rb
11
+ /spec/dummy/db/schema.rb
12
+ /spec/dummy/config/database.yml
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*.log
16
+ /spec/dummy/log/*.log
17
+ /tmp
18
+ .rvmrc
19
+ .ruby-version
20
+ .ruby-gemset
21
+ *~
22
+ *.komodoproject
23
+ /pkg
24
+ /Gemfile.lock
25
+ *.swp
26
+
27
+ /spec/dummy/public/extjs
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ -t ~netzke_testing
data/.travis.yml ADDED
@@ -0,0 +1,23 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.2.2
5
+
6
+ before_script:
7
+ - cp config/database.yml.travis spec/dummy/config/database.yml
8
+ - psql -c 'create database travis_ci_test;' -U postgres
9
+ - bundle exec rake db:migrate
10
+
11
+ - sh -e /etc/init.d/xvfb start
12
+
13
+ - wget http://cdn.sencha.com/ext/gpl/ext-5.1.0-gpl.zip
14
+ - unzip -q ext-5.1.0-gpl.zip
15
+ - mv ext-5.1.0 spec/dummy/public/extjs
16
+ - ln -s `pwd`/spec/dummy/public/icons/ spec/dummy/public/extjs/icons
17
+
18
+ script:
19
+ - export DISPLAY=:99.0
20
+ - bundle exec rake
21
+
22
+ addons:
23
+ postgresql: "9.4"
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Declare your gem's dependencies in marty.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ gem 'responders'
9
+ gem 'delayed_job_active_record'
10
+ gem 'daemons', '~> 1.1.9'
11
+
12
+ group :development, :test do
13
+ gem 'rails', '~> 4.2.1'
14
+ gem 'pry-rails'
15
+ gem 'rspec-rails', '~>3.0'
16
+ gem 'capybara'
17
+ gem 'selenium-webdriver'
18
+ gem 'timecop'
19
+ gem 'database_cleaner'
20
+ gem 'netzke-core'
21
+ gem 'netzke-basepack'
22
+ gem 'netzke-testing' #, path: File.expand_path('../../netzke-testing', __FILE__)
23
+ end
@@ -0,0 +1,23 @@
1
+ # Things that may affect how independent Marty can be as a gem:
2
+
3
+ * User model tests shouldn't rely on `User.last` as an alternate user type.
4
+
5
+ ## Things pulled from Gemini:
6
+
7
+ * Some of the category models and migrations. This may be OK since they are
8
+ only used in testing.
9
+ * Some/most/all of the models/gemini/extras. Perhaps these are more appropriate
10
+ as Marty items anyway.
11
+ * The `DataReport` and `Fields` (only parts) delorean scripts were copied into the dummy job for
12
+ the data_import spec. Currently Gemini won't be using this version of these scripts and the
13
+ data_import spec *must* remain in Gemini somewhere so we don't lose test coverage on what's
14
+ being used in production. This will probably need to remain this way until we have a solution
15
+ for making Marty be the source for these scripts (plus some of the others coming across).
16
+ At that time these scripts should be removed from the dummy job and the tests should refer
17
+ to them in their location in the Marty production code.
18
+
19
+ Also, the `Fields` script used here is a small subset of only the stuff needed by `DataReport`.
20
+ When the Marty-specific scripts are moved from Gemini `Fields will need to be broken into it's
21
+ Marty-specific and Gemini-specific parts.
22
+ * Along with those Delorean scripts more gemini models were needed (and put into spec/dummy/app/models)
23
+ Amongst them: `LoanProgram`, `*Type`, etc. They also got migrations.
File without changes
@@ -102,10 +102,15 @@ class Marty::ReportForm < Marty::Form
102
102
  js_configure do |c|
103
103
  # FIXME: Can we use POST instead of get:
104
104
  # http://stackoverflow.com/questions/133925/javascript-post-request-like-a-form-submit
105
- # arman -- I tried this solution. However, with a POST the
106
- # component no longer has the session cookie. Therefore, we don't
107
- # have access to the selected script. We likely needs to rethink
108
- # this whole mechanism.
105
+ # arman -- I tried this solution. However, with a POST, the
106
+ # component doesn't get the session cookie. Therefore, we don't
107
+ # have access to the selected script. QQQ: why not just send the
108
+ # session cookie in the header?? also see -- using XMLHttpRequest
109
+ # http://stackoverflow.com/questions/9516865/how-to-set-a-header-field-on-post-a-form
110
+ # http://stackoverflow.com/questions/9713058/sending-post-data-with-a-xmlhttprequest
111
+ # Or, perhaps use ExtJS Ajax:
112
+ # http://stackoverflow.com/questions/2917581/how-to-post-json-data-with-extjs
113
+
109
114
  c.on_foreground = <<-JS
110
115
  function() {
111
116
  var values = this.getForm().getValues();
@@ -0,0 +1,6 @@
1
+ Things to remove/deprecate from Gemini
2
+
3
+ - The duplicate tests from the `marty` spec subdirectories:
4
+ - `lib` - `data_import_spec.rb` needs to remain in Gemini for the time being (see notes in INDEPENDENCE_ISSUES). `data_blame_spec.rb`
5
+ is Gemini-specific and should be moved into the Gemini specs proper. All other specs should be removed.
6
+ - `features/javascript` - this doesn't appear to do anything useful and can probably be removed.
@@ -179,6 +179,105 @@ class Marty::DataChange
179
179
 
180
180
  ######################################################################
181
181
 
182
+ # Given a class and an array of records (hashes), figure out the
183
+ # differences in the current records for the class and the records.
184
+ # Produces a result hash with the following format:
185
+ #
186
+ # {"only_source" => [...],
187
+ # "only_input" => [...],
188
+ # "different" => [...],
189
+ # "same" => [...]}
190
+ #
191
+ # The "only_input" are hashes found in input which were not found in
192
+ # source. "same" are input hashes which were found in source and
193
+ # have identical information for fields provided. "only_source" is
194
+ # an array of source objects for which no equivalent was found in
195
+ # the input data. "different" denotes the set of objects which were
196
+ # found in both input and source but differed on some attribute
197
+ # values. "different" is an array of hashes. Array element look
198
+ # like:
199
+ #
200
+ # {"source" => {k1=>v1, k2=>v2, ...},
201
+ # "input" => {k1=>v1, k2=>v2, ...}}
202
+
203
+ delorean_fn :diff, sig: [2, 3] do
204
+ |klass, input_data, ts='infinity'|
205
+
206
+ ts = Mcfly.normalize_infinity(ts)
207
+ keys = Marty::DataConversion.assoc_keys(klass).map(&:to_s).to_set
208
+
209
+ only_source, only_input, different, same = [], [], [], []
210
+ found_sources = Set[]
211
+
212
+ input_data.each do
213
+ |input|
214
+
215
+ input_keys = input.keys
216
+
217
+ raise "non-String keys in input data" unless
218
+ input_keys.all? { |x| String === x }
219
+
220
+ begin
221
+ # convert record -- if there's an exception, it's likely that
222
+ # an association lookup failed => don't have some association
223
+ # in source. FIXME: it could be that we get an conversion
224
+ # error through not finding a non-key association. Ideally,
225
+ # if we can find the key in source, we should report this as
226
+ # "different".
227
+ conv =
228
+ Marty::DataConversion.convert_row(klass, input, ts)
229
+ rescue => exc
230
+ only_input << input
231
+ next
232
+ end
233
+
234
+ key_hash = conv.reject { |k, v| !keys.member?(k) }
235
+
236
+ source = Marty::DataConversion.find_row(klass, key_hash, ts)
237
+
238
+ if !source
239
+ # lookup of keys failed => don't have this in source
240
+ only_input << input
241
+ next
242
+ end
243
+
244
+ found_sources << source
245
+
246
+ non_key_hash = conv.reject { |k, v| keys.member?(k) }
247
+
248
+ # is source same as converted input?
249
+ if non_key_hash.all? { |k, v| v == source.send(k) }
250
+ same << input
251
+ next
252
+ end
253
+
254
+ source_export = Marty::DataExporter.export_obj(source) % input_keys
255
+
256
+ different << [
257
+ {"_origin_" => "source"} + source_export,
258
+ {"_origin_" => "input"} + input,
259
+ ]
260
+ end
261
+
262
+ # now find any live source object which have not been visited
263
+ query = klass
264
+
265
+ query = query.where("obsoleted_dt >= ? AND created_dt < ?", ts, ts) if
266
+ Mcfly.has_mcfly?(klass)
267
+
268
+ query = query.where.not(id: found_sources.map(&:id))
269
+
270
+ {
271
+ "different" => different,
272
+ "same" => same,
273
+ "only_input" => only_input,
274
+ "only_source" => Marty::DataExporter.
275
+ do_export_query_result(klass, query),
276
+ }
277
+ end
278
+
279
+ ######################################################################
280
+
182
281
  # Given a Mcfly class_name, find all of the obsoleted Mcfly objects
183
282
  # which are referenced by live (non-obsoleted) class instances.
184
283
  delorean_fn :dead_refs, sig: 2 do
@@ -58,10 +58,18 @@ class Marty::DataConversion
58
58
  # convert them to dates. FIXME: 'infinity' as a date in
59
59
  # Rails 3.2 appears to be broken. Setting a date field to
60
60
  # 'infinity' sets it to nil.
61
- v =~ FLOAT_PAT ? EXCEL_START_DATE + v.to_f :
62
- Mcfly.is_infinity(v) ? 'infinity' : v.to_date
61
+ begin
62
+ v =~ FLOAT_PAT ? EXCEL_START_DATE + v.to_f :
63
+ Mcfly.is_infinity(v) ? 'infinity' : v.to_date
64
+ rescue => exc
65
+ raise "date conversion failed for #{v}"
66
+ end
63
67
  when :datetime
64
- Mcfly.is_infinity(v) ? 'infinity' : v.to_datetime
68
+ begin
69
+ Mcfly.is_infinity(v) ? 'infinity' : v.to_datetime
70
+ rescue => exc
71
+ raise "datetime conversion failed for #{v}"
72
+ end
65
73
  when :numrange, :int4range, :int8range
66
74
  v.to_s
67
75
  when :float_array, :json, :jsonb
@@ -147,4 +147,13 @@ class Marty::DataExporter
147
147
 
148
148
  header + qres.map {|obj| export_attrs(klass, obj).flatten(1)}
149
149
  end
150
+
151
+ # Export a single object to hash -- FIXME: inefficient
152
+ # implementation
153
+ def self.export_obj(obj)
154
+ klass = obj.class
155
+ headers = export_headers(klass)
156
+ rec = export_attrs(klass, obj).flatten
157
+ Hash[ headers.zip(rec) ]
158
+ end
150
159
  end
data/lib/marty/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = "0.5.15"
2
+ VERSION = "0.5.16"
3
3
  end
data/marty.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require "marty/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "marty"
7
+ s.version = Marty::VERSION
8
+ s.authors = [
9
+ "Arman Bostani",
10
+ "Eric Litwin",
11
+ "Brian VanLoo",
12
+ "Iliana Toneva",
13
+ "Chad Edie",
14
+ ]
15
+ s.email = ["arman.bostani@pnmac.com"]
16
+ s.homepage = "https://github.com/arman000/marty"
17
+ s.summary = "A framework for working with versioned data"
18
+ s.description = s.summary
19
+ s.files = `git ls-files`.split($\)
20
+ s.licenses = ['MIT']
21
+
22
+ s.add_dependency "pg", "~> 0.17"
23
+
24
+ s.add_dependency 'netzke-core', '~> 0.12.2'
25
+ s.add_dependency 'netzke-basepack', '~> 0.12.6'
26
+
27
+ s.add_dependency 'axlsx', '2.1.0pre'
28
+
29
+ s.add_dependency 'delorean_lang', '~> 0.1'
30
+ s.add_dependency 'mcfly', '0.0.17'
31
+
32
+ s.add_dependency 'coderay'
33
+ s.add_dependency 'net-ldap'
34
+ s.add_dependency 'rubyzip'
35
+ end
data/script/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/marty/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module Marty
4
+ describe ApplicationController do
5
+ before(:each) do
6
+ subject.logout_user
7
+ expect(Marty::User.current).to be_nil
8
+ end
9
+
10
+ describe 'authentication' do
11
+ it "should allow a registered user to log in" do
12
+ allow(Rails.configuration.marty).to receive(:auth_source).
13
+ and_return('local')
14
+
15
+ user = Marty::User.try_to_login('marty', 'marty')
16
+ subject.set_user(user)
17
+ expect(Marty::User.current).to_not be_nil
18
+
19
+ subject.logout_user
20
+ expect(Marty::User.current).to be_nil
21
+ end
22
+
23
+ it "should allow a registered user to log in when the database " +
24
+ "is in recovery mode" do
25
+ allow(Marty::Util).to receive(:db_in_recovery?).and_return(true)
26
+ allow(Rails.configuration.marty).to receive(:auth_source).
27
+ and_return('local')
28
+
29
+ user = Marty::User.try_to_login('marty', 'marty')
30
+ subject.set_user(user)
31
+ expect(Marty::User.current).to_not be_nil
32
+
33
+ subject.logout_user
34
+ expect(Marty::User.current).to be_nil
35
+ end
36
+
37
+ it "should prevent a non-registered user from logging in" do
38
+ user = Marty::User.try_to_login('unknown_marty', 'invalid_password')
39
+ expect(user).to be_nil
40
+ expect(Marty::User.current).to be_nil
41
+ end
42
+
43
+ it "should prevent a non-registered user from logging in when the " +
44
+ "database is in recovery mode" do
45
+ allow(Marty::Util).to receive(:db_in_recovery?).and_return(true)
46
+ user = Marty::User.try_to_login('unknown_marty', 'invalid_password')
47
+ expect(user).to be_nil
48
+ expect(Marty::User.current).to be_nil
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,226 @@
1
+ require 'spec_helper'
2
+ require 'marty'
3
+ require 'delorean_lang'
4
+ require 'benchmark'
5
+ require 'job_helper'
6
+
7
+ describe Marty::JobController, slow: true do
8
+ before(:each) { @routes = Marty::Engine.routes }
9
+
10
+ before(:all) do
11
+ @clean_file = "/tmp/clean_#{Process.pid}.psql"
12
+ save_clean_db(@clean_file)
13
+ # transactional fixtures interfere with queueing jobs
14
+ self.use_transactional_fixtures = false
15
+
16
+ # Needed here because shutting transactional fixtures off
17
+ # means we lose the globally set uesr
18
+ Mcfly.whodunnit = UserHelpers.system_user
19
+
20
+ Marty::Script.load_script_bodies(promise_bodies, Date.today)
21
+
22
+ start_delayed_job
23
+ end
24
+
25
+ after(:each) do
26
+ Marty::Promise.where('parent_id IS NULL').destroy_all
27
+ end
28
+
29
+ after(:all) do
30
+ restore_clean_db(@clean_file)
31
+ stop_delayed_job
32
+ end
33
+
34
+ it "should be able to evaluate in the foreground " do
35
+ engine = Marty::ScriptSet.new.get_engine(NAME_A)
36
+
37
+ res = engine.evaluate("Y", "d")
38
+ expect(res).to eq([
39
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>0.125}}},
40
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>1.125}}},
41
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>2.125}}},
42
+ ])
43
+
44
+ expect(Marty::Promise.unscoped.where(start_dt: nil).count).to eq 0
45
+
46
+ expect {
47
+ res = engine.evaluate("Y", "d", {"s" => 1})
48
+ # force res to be evaluated
49
+ res.to_s
50
+ }.to raise_error(RuntimeError)
51
+
52
+ sleep 5
53
+
54
+ expect(Marty::Promise.unscoped.where(start_dt: nil).count).to eq 0
55
+ end
56
+
57
+ it "should be able to run long-running tasks in separate jobs" do
58
+ engine = Marty::ScriptSet.new.get_engine(NAME_A)
59
+
60
+ # NOTE: can't make this too small since the default
61
+ # Delayed::Worker::sleep_delay is 5 seconds
62
+ # However - delayed_job initializer config sets this to 1 for test
63
+ slp = 5
64
+
65
+ exp_res = {"d"=>[
66
+ {"z"=>slp,"a"=>{"b"=>{"e"=>1-slp}}},
67
+ {"z"=>slp,"a"=>{"b"=>{"e"=>2-slp}}},
68
+ {"z"=>slp,"a"=>{"b"=>{"e"=>3-slp}}},
69
+ ]}
70
+
71
+ bench = Benchmark.measure {
72
+ res = engine.evaluate("Y", "f", {"s" => slp})
73
+ expect(res).to eq exp_res
74
+ }
75
+
76
+ # If "f" is evaluated in serial fashion, then the process would
77
+ # take slp*3+ seconds. Make sure that we had some parallel
78
+ # behavior.
79
+ expect(bench.real).to be >= slp
80
+ expect(bench.real).to be < slp*2
81
+ end
82
+
83
+ it "should be able to run long-running tasks in separate jobs (2)" do
84
+ engine = Marty::ScriptSet.new.get_engine(NAME_H)
85
+
86
+ slp = 5 # hard-coded in script
87
+ exp_res = {"d"=>[{"a"=>1}, {"a"=>4}]}
88
+
89
+ bench = Benchmark.measure {
90
+ res = engine.background_eval("Y", {}, ["d"])
91
+
92
+ expect(res).to eq exp_res
93
+ }
94
+
95
+ expect(bench.real).to be >= slp
96
+ expect(bench.real).to be < slp*2
97
+ end
98
+
99
+ it "should be to handle non-serializable errors" do
100
+ engine = Marty::ScriptSet.new.get_engine(NAME_C)
101
+ engine.background_eval("Z", {"p_title" => NAME_C}, ["result"])
102
+ sleep 5
103
+
104
+ promise = Marty::Promise.find_by_title(NAME_C)
105
+
106
+ get 'download', {
107
+ job_id: promise.id,
108
+ }
109
+
110
+ expect(response.content_type).to eq 'application/json'
111
+ expect(JSON.parse(response.body).keys.member?("error")).to be true
112
+ end
113
+
114
+ it "promise proxies should be stored lazily (not expanded)" do
115
+ engine = Marty::ScriptSet.new.get_engine(NAME_E)
116
+ engine.background_eval("Z", {"p_title" => NAME_E}, ["result"])
117
+ sleep 5
118
+
119
+ promise = Marty::Promise.find_by_title(NAME_E)
120
+
121
+ res = promise.result["result"]
122
+
123
+ expect(res.length).to eq 6
124
+
125
+ # make sure that the contents are promise proxies (i.e. have
126
+ # __force__) and therefore lazy.
127
+ res.each { |r| expect(r.respond_to?(:__force__)).to be true }
128
+ end
129
+
130
+ it "should not leave zombie promises when we have exceptions" do
131
+ engine = Marty::ScriptSet.new.get_engine(NAME_D)
132
+ engine.background_eval("Z", {"p_title" => NAME_D}, ["result"])
133
+ sleep 5
134
+
135
+ pl = Marty::Promise.unscoped.where(title: NAME_D)
136
+
137
+ expect(pl.count).to eq 2
138
+
139
+ pl.each { |p|
140
+ expect(p.result["error"]).not_to eq nil
141
+ expect(p.end_dt).not_to eq nil
142
+ }
143
+ end
144
+
145
+ it "should be able to ask controller for job result" do
146
+ title = "BG RPC"
147
+ engine = Marty::ScriptSet.new.get_engine(NAME_A)
148
+ engine.background_eval("Y", {"p_title" => title}, ["d"])
149
+ sleep 5
150
+
151
+ promise = Marty::Promise.find_by_title(title)
152
+
153
+ res = {"d"=>[
154
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>0.125}}},
155
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>1.125}}},
156
+ {"z"=>0.875, "a"=>{"b"=>{"e"=>2.125}}},
157
+ ]}
158
+
159
+ expect(promise.result).to eq res
160
+
161
+ get 'download', {
162
+ format: :json,
163
+ job_id: promise.id,
164
+ }
165
+ expect(response.body).to eq res.to_json
166
+ end
167
+
168
+ it "should be able to get zip results" do
169
+ title = "BG ZIP"
170
+ engine = Marty::ScriptSet.new.get_engine(NAME_B)
171
+ engine.background_eval("Z",
172
+ {},
173
+ ["result", "format", "title"],
174
+ )
175
+ sleep 5
176
+
177
+ promise = Marty::Promise.find_by_title(NAME_B)
178
+
179
+ expect(promise.result).to eq({
180
+ "result"=>[{"a"=>1, "b"=>1},{"a"=>2, "b"=>4},{"a"=>3, "b"=>9}],
181
+ "format"=>"csv",
182
+ "title"=>"PromiseB",
183
+ })
184
+
185
+ get 'download', {
186
+ job_id: promise.id,
187
+ }
188
+
189
+ expect_csv = "a,b\r\n1,1\r\n2,4\r\n3,9\r\n"
190
+ expect(response.body).to eq expect_csv
191
+ expect(response.content_type).to eq "text/csv"
192
+
193
+ get 'download', {
194
+ job_id: promise.parent_id,
195
+ }
196
+
197
+ expect(response.content_type).to eq "application/zip"
198
+
199
+ Zip::InputStream.open(StringIO.new(response.body)) {
200
+ |io|
201
+
202
+ count = 0
203
+ while (entry = io.get_next_entry)
204
+ expect(entry.name).to match /PromiseB.*\.csv/
205
+ expect(io.read).to eq expect_csv
206
+ count += 1
207
+ end
208
+ expect(count).to eq 3
209
+ }
210
+ end
211
+
212
+ it "should be able to start promises on imported nodes" do
213
+ engine = Marty::ScriptSet.new.get_engine(NAME_F)
214
+ res = engine.evaluate("Z", "result", {})
215
+ expect(res).to eq "x"*10
216
+ end
217
+
218
+ it "promises called from regular node calls should pass parent promise id" do
219
+ engine = Marty::ScriptSet.new.get_engine(NAME_G)
220
+ res = engine.evaluate("V", "result", {})
221
+ expect(res).to eq [123]
222
+ p1 = Marty::Promise.find_by_title("#{NAME_G}")
223
+ p2 = Marty::Promise.find_by_title("#{NAME_G}2")
224
+ expect(p2.parent_id).to eq p1.id
225
+ end
226
+ end