localtower 0.5.0 → 2.0.0

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 (234) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +129 -33
  3. data/app/assets/stylesheets/application.css +68 -0
  4. data/app/controllers/localtower/pages_controller.rb +30 -30
  5. data/app/javascript/application.js +46 -0
  6. data/app/javascript/components/DarkSelect.js +115 -0
  7. data/app/javascript/components/ModernTooltip.js +26 -0
  8. data/app/javascript/components/NewMigrationForm.js +687 -0
  9. data/app/javascript/components/NewModelForm.js +432 -0
  10. data/app/javascript/components/data/Stores.js +294 -0
  11. data/app/views/layouts/localtower/application.html.erb +77 -80
  12. data/app/views/localtower/_migration.html.erb +30 -0
  13. data/app/views/localtower/pages/_alert_no_models.html.erb +3 -0
  14. data/app/views/localtower/pages/migrations.html.erb +25 -87
  15. data/app/views/localtower/pages/new_migration.html.erb +40 -0
  16. data/app/views/localtower/pages/new_model.html.erb +21 -0
  17. data/config/routes.rb +5 -5
  18. data/lib/localtower/engine.rb +5 -2
  19. data/lib/localtower/generators/migration.rb +219 -161
  20. data/lib/localtower/generators/model.rb +59 -26
  21. data/lib/localtower/generators/service_objects/insert_array.rb +25 -0
  22. data/lib/localtower/generators/service_objects/insert_defaults.rb +85 -43
  23. data/lib/localtower/generators/service_objects/insert_foreign_keys.rb +35 -0
  24. data/lib/localtower/generators/service_objects/insert_indexes.rb +103 -0
  25. data/lib/localtower/generators/service_objects/insert_nullable.rb +25 -0
  26. data/lib/localtower/status.rb +4 -2
  27. data/lib/localtower/tools.rb +31 -23
  28. data/lib/localtower/version.rb +1 -1
  29. data/lib/localtower.rb +0 -6
  30. data/public/com/favicon-64.png +0 -0
  31. data/public/com/twitter-cover-1.png +0 -0
  32. data/public/com/twitter-cover-2.png +0 -0
  33. data/public/fonts/Circular/CircularStd-Black.otf +0 -0
  34. data/public/fonts/Circular/CircularStd-BlackItalic.otf +0 -0
  35. data/public/fonts/Circular/CircularStd-Bold.otf +0 -0
  36. data/public/fonts/Circular/CircularStd-BoldItalic.otf +0 -0
  37. data/public/fonts/Circular/CircularStd-Book.otf +0 -0
  38. data/public/fonts/Circular/CircularStd-BookItalic.otf +0 -0
  39. data/public/fonts/Circular/CircularStd-Light Italic.otf +0 -0
  40. data/public/fonts/Circular/CircularStd-Light.otf +0 -0
  41. data/public/fonts/Circular/CircularStd-Medium.otf +0 -0
  42. data/public/fonts/Circular/CircularStd-MediumItalic.otf +0 -0
  43. data/public/fonts/Circular/Read Me.txt +5 -0
  44. data/public/fonts/Circular/www.dfonts.org.url +2 -0
  45. data/public/javascripts/application.js +30830 -0
  46. data/public/javascripts/application.js.map +7 -0
  47. data/public/screenshots/v0.1.1/1_schema.png +0 -0
  48. data/public/screenshots/v0.1.1/2_models_1.png +0 -0
  49. data/public/screenshots/v0.1.1/2_models_2.png +0 -0
  50. data/public/screenshots/v0.1.1/3_relations.png +0 -0
  51. data/public/screenshots/v0.1.1/4_migrations.png +0 -0
  52. data/public/screenshots/v0.1.6/1_schema.png +0 -0
  53. data/public/screenshots/v0.1.6/2_models_1.png +0 -0
  54. data/public/screenshots/v0.1.6/3_relations.png +0 -0
  55. data/public/screenshots/v0.1.6/4_migrations.png +0 -0
  56. data/public/screenshots/v0.1.6/5_capture.png +0 -0
  57. data/public/screenshots/v1.0.0/migrations.png +0 -0
  58. data/public/screenshots/v1.0.0/models.png +0 -0
  59. data/public/screenshots/v1.0.0/new_migration.png +0 -0
  60. data/public/screenshots/v1.0.0/new_model.png +0 -0
  61. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.44.30.jpg +0 -0
  62. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.44.42.jpg +0 -0
  63. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.44.54.jpg +0 -0
  64. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.48.21.jpg +0 -0
  65. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.48.28.jpg +0 -0
  66. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.48.51.jpg +0 -0
  67. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.49.09.jpg +0 -0
  68. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.50.04.jpg +0 -0
  69. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.50.11.jpg +0 -0
  70. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.50.19.jpg +0 -0
  71. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.51.47.jpg +0 -0
  72. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.51.48.jpg +0 -0
  73. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.52.02.jpg +0 -0
  74. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.52.18.jpg +0 -0
  75. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.52.26.jpg +0 -0
  76. data/public/screenshots/v2.0.0/Screenshot 2024-11-18 at 22.52.38.jpg +0 -0
  77. data/public/screenshots/v2.0.0/migrations.png +0 -0
  78. data/public/screenshots/v2.0.0/new_migration.png +0 -0
  79. data/public/screenshots/v2.0.0/new_model.png +0 -0
  80. data/public/stylesheets/application.css +1179 -0
  81. data/public/vendor/jquery-3.7.1.min.js +2 -0
  82. data/public/vendor/lucide.min.js +12 -0
  83. data/public/vendor/monokai-sublime.css +80 -0
  84. data/public/vendor/rails-ujs.min.js +8 -0
  85. data/spec/dummy/Gemfile +1 -3
  86. data/spec/dummy/Gemfile.lock +140 -111
  87. data/spec/dummy/config/database.yml +4 -6
  88. data/spec/dummy/config/environments/development.rb +1 -0
  89. data/spec/dummy/db/migrate/20241118233214_create_users.rb +9 -0
  90. data/spec/dummy/db/migrate/20241118233233_add_column_titme_for_user.rb +6 -0
  91. data/spec/dummy/db/schema.rb +4 -8
  92. data/spec/dummy/lib/tasks/reset.rake +16 -0
  93. data/spec/dummy/log/development.log +21437 -4475
  94. data/spec/dummy/log/localtower.log +586 -139
  95. data/spec/dummy/log/test.log +716 -0
  96. data/spec/dummy/test/index.html +38 -0
  97. data/spec/dummy/tmp/cache/sessions/localtower/726/FE1/_session_id%3A2%3A%3A69d6764784f832ea25700ea2f242210f3f8d94b48912b0f09ffc44311f0376eb +0 -0
  98. data/spec/dummy/tmp/cache/sessions/localtower/77E/121/_session_id%3A2%3A%3A0b3c1fe7154461ba023ba6c81dd1b999348757880b505f4b082faed263a627f9 +1 -0
  99. data/spec/dummy/tmp/cache/sessions/localtower/7EB/8A1/_session_id%3A2%3A%3Af88237c1d157546a5dcde894fd6c1410162a8eba318748d4ac573ee75e40df16 +0 -0
  100. data/spec/dummy/tmp/local_secret.txt +1 -0
  101. data/spec/dummy/tmp/pids/server.pid +1 -0
  102. data/spec/factories/migration.rb +87 -66
  103. data/spec/factories/model.rb +54 -20
  104. data/spec/lib/localtower/generators/migration_spec.rb +28 -63
  105. data/spec/lib/localtower/generators/model_spec.rb +96 -24
  106. data/spec/lib/localtower/generators/service_objects/insert_array_spec.rb +47 -0
  107. data/spec/lib/localtower/generators/service_objects/insert_defaults_spec.rb +30 -35
  108. data/spec/lib/localtower/generators/service_objects/insert_foreign_keys_spec.rb +101 -0
  109. data/spec/lib/localtower/generators/service_objects/insert_indexes_spec.rb +90 -0
  110. data/spec/lib/localtower/generators/service_objects/insert_nullable_spec.rb +61 -0
  111. data/spec/lib/localtower/tools_spec.rb +1 -11
  112. data/spec/spec_helper.rb +10 -6
  113. metadata +130 -237
  114. data/app/views/localtower/pages/_migrations.html.erb +0 -57
  115. data/app/views/localtower/pages/models.html.erb +0 -80
  116. data/app/views/localtower/pages/schema.html.erb +0 -67
  117. data/public/css/app.css +0 -104
  118. data/public/js/app.js +0 -192
  119. data/public/light-bootstrap-dashboard-master/assets/css/animate.min.css +0 -6
  120. data/public/light-bootstrap-dashboard-master/assets/css/bootstrap.min.css +0 -5
  121. data/public/light-bootstrap-dashboard-master/assets/css/demo.css +0 -61
  122. data/public/light-bootstrap-dashboard-master/assets/css/light-bootstrap-dashboard.css +0 -2789
  123. data/public/light-bootstrap-dashboard-master/assets/css/pe-icon-7-stroke.css +0 -632
  124. data/public/light-bootstrap-dashboard-master/assets/fonts/Pe-icon-7-stroke.eot +0 -0
  125. data/public/light-bootstrap-dashboard-master/assets/fonts/Pe-icon-7-stroke.svg +0 -212
  126. data/public/light-bootstrap-dashboard-master/assets/fonts/Pe-icon-7-stroke.ttf +0 -0
  127. data/public/light-bootstrap-dashboard-master/assets/fonts/Pe-icon-7-stroke.woff +0 -0
  128. data/public/light-bootstrap-dashboard-master/assets/img/default-avatar.png +0 -0
  129. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-0.jpg +0 -0
  130. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-1.jpg +0 -0
  131. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-2.jpg +0 -0
  132. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-3.jpg +0 -0
  133. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-4.jpg +0 -0
  134. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-5.jpg +0 -0
  135. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-6.jpg +0 -0
  136. data/public/light-bootstrap-dashboard-master/assets/img/faces/face-7.jpg +0 -0
  137. data/public/light-bootstrap-dashboard-master/assets/img/faces/tim_vector.jpe +0 -0
  138. data/public/light-bootstrap-dashboard-master/assets/img/favicon.ico +0 -0
  139. data/public/light-bootstrap-dashboard-master/assets/img/loading-bubbles.svg +0 -14
  140. data/public/light-bootstrap-dashboard-master/assets/img/mask.png +0 -0
  141. data/public/light-bootstrap-dashboard-master/assets/img/new_logo.png +0 -0
  142. data/public/light-bootstrap-dashboard-master/assets/img/sidebar-1.jpg +0 -0
  143. data/public/light-bootstrap-dashboard-master/assets/img/sidebar-2.jpg +0 -0
  144. data/public/light-bootstrap-dashboard-master/assets/img/sidebar-3.jpg +0 -0
  145. data/public/light-bootstrap-dashboard-master/assets/img/sidebar-4.jpg +0 -0
  146. data/public/light-bootstrap-dashboard-master/assets/img/sidebar-5.jpg +0 -0
  147. data/public/light-bootstrap-dashboard-master/assets/img/tim_80x80.png +0 -0
  148. data/public/light-bootstrap-dashboard-master/assets/js/bootstrap-checkbox-radio-switch.js +0 -502
  149. data/public/light-bootstrap-dashboard-master/assets/js/bootstrap-notify.js +0 -404
  150. data/public/light-bootstrap-dashboard-master/assets/js/bootstrap-select.js +0 -438
  151. data/public/light-bootstrap-dashboard-master/assets/js/bootstrap.min.js +0 -7
  152. data/public/light-bootstrap-dashboard-master/assets/js/chartist.min.js +0 -9
  153. data/public/light-bootstrap-dashboard-master/assets/js/demo.js +0 -152
  154. data/public/light-bootstrap-dashboard-master/assets/js/jquery-1.10.2.js +0 -9789
  155. data/public/light-bootstrap-dashboard-master/assets/js/light-bootstrap-dashboard.js +0 -179
  156. data/public/logo-localtower-white-300.png +0 -0
  157. data/public/logo-localtower-white.png +0 -0
  158. data/public/logo-localtower.png +0 -0
  159. data/public/vendor/font-awesome.min.css +0 -4
  160. data/public/vendor/highlight-js-default.min.css +0 -9
  161. data/spec/dummy/db/migrate/20221115190039_create_users.rb +0 -13
  162. data/spec/dummy/db/migrate/20221115193020_change_users.rb +0 -8
  163. data/spec/dummy/db/migrate/20221115193532_change_users_at1668540931.rb +0 -5
  164. data/spec/dummy/db/migrate/20221115193605_change_users_at1668540964.rb +0 -5
  165. data/spec/dummy/db/migrate/20221115193637_change_users_at1668540996.rb +0 -5
  166. data/spec/dummy/db/migrate/20221115193642_change_users_at1668541001.rb +0 -5
  167. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/-Y/-YOiiBKqc2UODHFjctm8xc7xFoZaL7zOjWQj6qQ2wyE.cache +0 -1
  168. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/-d/-dwueM4vmPt8L51S3jeSyg_AjGDcj0GUN6pDpCA1gCg.cache +0 -3
  169. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/0g/0gaJnJQdtd2ACbihXxn8OnjLWlDjnQ_WxfgOpbiLzhg.cache +0 -1
  170. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/0r/0rFCsCV9kZnEYtZZ6sfig8329OU31bqjecDFqSVank8.cache +0 -1
  171. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/1J/1J2k_CpnQE3d-PZAQwOVGQALGkta4qVvhdsKjgG0e4Q.cache +0 -0
  172. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/2p/2pYVH2Z_syqh6ok8QYxJNKxXpx1Iwppf6JGElZI0gpw.cache +0 -0
  173. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/3W/3WBmqd-2V6q5N-jvbyp-tlcfn3aHYMwBppbOUm7x4qg.cache +0 -1
  174. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/4K/4KFPlHkhdDW0riGUmlbaR-kmDz6JUnQvY6fwW8qsdaE.cache +0 -3
  175. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/6x/6xMeRWmLpNK_flx6-JA3KazvUxSCxyPxHv9Zm3eC26Q.cache +0 -1
  176. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/7d/7dNqI_dCDJLJmI1oM4xwFp9nRRNOem-4P4OD7PMyz2E.cache +0 -0
  177. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/8X/8XquhxVcp5A8QquLtxO8NgKTMJch0eqQmzFmRGIZP6I.cache +0 -1
  178. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Ah/AhdfXXtU63kXS4TnsH2Vi1yWgVkIEeV8Z8XA68hrNQc.cache +0 -0
  179. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Bt/BtAePnwLSGw82xUGI7wuhWDfuwarOQVS91YqCsweMcs.cache +0 -0
  180. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Bx/BxzoG0KxeNLac4xTNeJv3qfeytbNBw58xj2zD-xdbrE.cache +0 -2
  181. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/CC/CCD0ROPX0yxHwNpVZmuP8ZNtgQpaVCMXpdzC0Wb5n24.cache +0 -1
  182. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/DS/DS43oxBg6K5PMWLn2mTy_4EnxI03ehHkxpjV7NCi8yM.cache +0 -1
  183. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Do/Do83AoOat5W-c1g7piDGy2GMmffsY6JY0Qfuh5PY3GM.cache +0 -2
  184. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/EI/EI0Nxk-VRATWstMuVCVc0_GdYlQ-a0dx6n2g0l3vIik.cache +0 -0
  185. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/EN/ENhJnzJAU2IK-7aHqubj9N8Jo_UTmjG_VEQqyIkolQ8.cache +0 -1
  186. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Fc/FcS6VUHN3Bd4pHGqd5NHA8jA4OLwWrU94s3b4GGxX9c.cache +0 -1
  187. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/G0/G0kukI-r0q0Vbrg6e_jnYJoYcOX8K__h-mwKbCf4twE.cache +0 -0
  188. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/I1/I1Jw08-mz8xzgrgi6giCzpf1UmzGTSbl4eJEw4DAoJM.cache +0 -1
  189. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/IV/IVOO1dXBmgjieDk__g57p6aYt0Z3CmfTa32jhegoyko.cache +0 -3
  190. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/JY/JYPmv5WP4wxdI9EKEEPkK_fVtqeSLfmint_5E8fQLQ8.cache +0 -1
  191. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/MX/MXI3KxaQPZGImSvCNZ_TbQVruWCJ3E0xiVxza1ZCAjM.cache +0 -1
  192. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/OU/OUoioCqXALK909jXPV3VSyCJIdNC7bsogUfdnRTpc5o.cache +0 -1
  193. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/PH/PHjtqNLUPAUDiSlu5AbPjlIo20mOGNm0uNjMLhX2NvM.cache +0 -0
  194. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/RC/RC9vfALY5K634pTeau0BAhTHl7d5_5yA3tM-QTLiKtI.cache +0 -0
  195. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/RG/RG1PrmlixwaUlG8BV0kcm_3F7OQekxsrLYjBdf403-k.cache +0 -2
  196. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Ut/UtoMO6n6FHTpRCGk9VfxlfTI2Ao2GYJ_6kMzx3B9VH8.cache +0 -1
  197. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/VE/VExnbpDk3LxYdPk3htUrZQXPI8NK_zlKtSFfVXJlxU4.cache +0 -1
  198. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Wm/WmKTjykiU-Tx_Faf5zduEeEQ-DozAs5omKToM2l1cZM.cache +0 -0
  199. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/X-/X-KjhDPd0nI155N_FaxFSgaOiYo0_ytqlmkBlLM7ayw.cache +0 -1
  200. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/XX/XXVhj-O91tJ5c8pz7DQlTABOv8TIRVg5haw9VOq9HuI.cache +0 -1
  201. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/YC/YCiifo5NCMwDChFqYFiV4EaYEx8hy3Efle1PsGbIdak.cache +0 -1
  202. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Ya/YayMISAqD-Y3vBFywwKrXbUovGf5o77HUF5s8mnQgO0.cache +0 -0
  203. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/Zl/Zl-NEb0aDLmZ9gN4gdY1OYlbUXD4JhkFY_y23m55zmo.cache +0 -0
  204. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/_h/_hnjPptBeD3GP5B0iJR6pvyVkiMiq-o2TxaYmGf9KJo.cache +0 -3
  205. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/_p/_poEF6QbTPYJiQCFGoXxIXjmvcn6T0I87ElzJGHguk0.cache +0 -1
  206. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/aa/aaSj2zt8ddc87nZqTpOI9gRCKVnoalXKFBww7t4t3Kg.cache +0 -0
  207. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/bZ/bZHWUkrOLp0WM_Ogo36Qjt4cxDt-rOFgtcj4LNB-BVE.cache +0 -1
  208. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/cP/cPSNG_663TNT944Rke919luRS6FtpMwVc_7aDph645w.cache +0 -1
  209. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/ce/ceq8fcZGIoqWrCl9vrCRrywEdx42iy9PrGG9CwFSIF4.cache +0 -0
  210. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/cl/cljhP0Jc018nLnG1rpdg-FUf6mlIqrQYvXTNXTNG9Bw.cache +0 -0
  211. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/dm/dmod3fTZdoKso2FGM5RTQtb4kTB6vywsiXLWDygYFN4.cache +0 -1
  212. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/dy/dySDGcUTqlKx2MTvOmIEP-WzhyhCfU5xhN9qgxbz3rI.cache +0 -0
  213. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/gQ/gQYdmEzbLjTcFCnsfzXzEmeminOchF263snAk3IvXM0.cache +0 -1
  214. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/gh/ghvuMAqccBGljlmEaI3Gt5tH5rEg6hseKYMYfRMkHdY.cache +0 -1
  215. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/gt/gt8QC7k92A4hXA3HlCpQulW73tqd6x63I3uT-YrGMj0.cache +0 -1
  216. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/hf/hfVg-y7dPSQKbb7V3I7M5w7IpOhDiIB2H31d2tmbZMU.cache +0 -0
  217. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/iF/iFLwyhZIu1Jxm77NUT2qWeTDMmyELW4U85t9rGE_KVg.cache +0 -0
  218. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/kF/kFlIcBBnSyKE00OjpM90pqSn93mLMUv9esz86nrO-Cs.cache +0 -1
  219. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/ma/MazNt_5OaYexRuZnccZZ_7AONlxn1a4W3KdWyHyBdws.cache +0 -0
  220. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/ma/ma3w08gnDTeqwY2-C9BlToxA6-AS8bvXlu-nBiz0UYs.cache +0 -1
  221. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/n_/n_xYqQYhwEMQknb3jFQnjlxxBE9TzMNHCdJ-bEyZFIw.cache +0 -2
  222. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/nx/nxTv3sKVUQZADJyM3dPaVmUA78MIsMLD_K279yN_GsI.cache +0 -2
  223. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/pv/pv6tV3CDkIAmLXdU8EPLlNEkXAKJPufVD4VP30o4fWo.cache +0 -0
  224. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/qP/qPmv5snMrDw830S6hSICDcnIy7kVEWoFKXhGKT38lG4.cache +0 -2
  225. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/sY/sYvTb1uPWWmnHJNXKuW_xSco-aUb-rN2f0J35zSIzuc.cache +0 -1
  226. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/su/suQ_jjDxRiIn4VEqFJYKaBWJILaeGKBvoTv49XNX0vo.cache +0 -1
  227. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/u8/u8IrW5IIQ7espwk2Vpk23zheL9YZA0tnbKq9X7E-WA0.cache +0 -1
  228. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/wh/whzETExjcZCkn4msasD2aylgfhfpKPZrNxTkY-SOIDg.cache +0 -2
  229. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/x7/x7PYh8DJvPykcEqpVab2vcY9-GFz-3cqtoMlRAu94Uc.cache +0 -2
  230. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/y6/y6oxsJD3pAY9ph1_5M-77uiEjTVw8BheLZNygCKPm1g.cache +0 -1
  231. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/yH/yHZ-a1J23ZCf2n5mEHINMz23Iec2cLGTKauW7k4yGTE.cache +0 -0
  232. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/z7/z7KfUWYoSaGq8tQ5mbHwyfXyT3Pa4FWKJswHcIdJuds.cache +0 -1
  233. data/spec/dummy/tmp/cache/assets/sprockets/v4.0.0/zu/zuExWc5WHxeOUPZUKHl9a9ZRmN50g7jMOD28macn6M0.cache +0 -0
  234. data/spec/lib/localtower/generators/relation_spec.rb +0 -65
@@ -0,0 +1,21 @@
1
+ <% content_for :title do %>New Model<% end %>
2
+ <div>
3
+ <%= form_tag models_path, class: "" do %>
4
+ <div class="flex flex-col gap-2">
5
+ <div class="block text-localtower-100">Model name <span class="text-localtower-400">(required)</span></div>
6
+ <div class="w-80">
7
+ <input autocomplete="off" type="text" data-selector="modelName" name="model[model_name]" class="px-2 py-1.5 rounded bg-localtower-600 border border-localtower-500 inline-block w-full outline-none focus:border-localtower-400 transition-colors text-localtower-100 placeholder:italic placeholder:text-localtower-450 focus:placeholder:text-localtower-600" placeholder="Ex: User" required>
8
+ </div>
9
+ <input type="hidden" id="form_attributes" name="model[attributes]">
10
+ </div>
11
+ <div class="py-4 flex flex-col gap-2">
12
+ <label class="block text-localtower-100">Columns</label>
13
+ <div id="react-container-new-model"></div>
14
+ </div>
15
+ <div>
16
+ <button type="submit" value="true" class="px-3 py-1.5 rounded-md bg-green-800 text-localtower-100 border border-green-700 inline-block outline-none hover:border-green-500 hover:bg-green-700 hover:text-white active:opacity-60 transition-colors text-md">
17
+ Generate Model
18
+ </button>
19
+ </div>
20
+ <% end %>
21
+ </div>
data/config/routes.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  Localtower::Engine.routes.draw do
2
+ get 'migrations/new', to: 'pages#new_migration', as: :new_migration
2
3
  get 'migrations', to: 'pages#migrations', as: :migrations
3
4
  post 'migrations', to: 'pages#post_migrations'
4
5
 
5
- get 'models', to: 'pages#models', as: :models
6
+ get 'models/new', to: 'pages#new_model', as: :new_model
6
7
  post 'models', to: 'pages#post_models'
7
8
 
8
- # get ':v/:asset', to: 'pages#asset_render', as: 'asset_render', :constraints => { :v => /[^\/]*/, :asset => /[^\/]*/ }
9
- get 'schema', to: 'pages#schema', as: 'schema'
10
- post 'actions', to: 'pages#post_actions', as: 'actions'
9
+ post 'actions', to: 'pages#post_actions', as: :actions
10
+ post 'rm_file', to: 'pages#rm_file', as: :rm_file
11
11
 
12
- root :to => redirect('models')
12
+ root :to => redirect('models/new')
13
13
  end
@@ -2,8 +2,11 @@ module Localtower
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace Localtower
4
4
 
5
- initializer 'static assets' do |app|
6
- app.middleware.use ::ActionDispatch::Static, "#{root}/public"
5
+ initializer 'localtower.add_static_assets', before: :build_middleware_stack do |app|
6
+ app.config.middleware.use Rack::Deflater
7
+ app.config.middleware.use ::ActionDispatch::Static, "#{root}/public"
8
+ # Later: have a better assets pipeline to be able to version and cache assets forever
9
+ # app.config.middleware.use ::ActionDispatch::Static, "#{root}/public", headers: { "Cache-Control" => "public, max-age=31536000" }
7
10
  end
8
11
  end
9
12
  end
@@ -1,120 +1,115 @@
1
+ #
2
+ # @see https://github.com/rails/rails/blob/main/activerecord/lib/active_record/migration.rb
3
+ # @see https://github.com/rails/rails/blob/main/activerecord/lib/active_record/migration/command_recorder.rb
4
+ # @see https://api.rubyonrails.org/v7.2.1/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html
5
+ #
1
6
  module Localtower
2
7
  module Generators
3
8
  class ThorGeneratorMigration < Thor
9
+ SPACE_PADDING = " "
10
+
4
11
  include Thor::Base
5
12
  include Thor::Actions
6
13
 
7
14
  no_commands do
8
- # data = {last_migration_file: "", column_type: "", table_name: "", column: "", nullable: true, index: true}
9
15
  def migration_add_column(data)
10
- file = data["last_migration_file"]
16
+ raise "No table name provided" if data['table_name'].blank?
17
+ raise "No attribute name provided" if data['column_name'].blank?
11
18
 
12
19
  # Special case for array
13
- if %w(array).include?(data["column_type"])
14
- line = " add_column :#{data['table_name']}, :#{data['column']}, :text"
15
- line << ", default: []"
20
+ if %w[array].include?(data["column_type"])
21
+ line = "#{SPACE_PADDING}add_column :#{data['table_name']}, :#{data['column_name']}, :string"
22
+ line << ", default: #{data['default']}" if data['default'].present?
16
23
  line << ", array: true"
17
-
18
- # Add an gin inde as well:
19
- line << "\n"
20
- line << " add_index :#{data['table_name']}, :#{data['column']}, using: :gin"
21
24
  else
22
- line = " add_column :#{data['table_name']}, :#{data['column']}, :#{data['column_type']}"
25
+ line = "#{SPACE_PADDING}add_column :#{data['table_name']}, :#{data['column_name']}, :#{data['column_type']}"
23
26
  line << ", default: #{data['default']}" if data['default'].present?
24
- line << ", null: false" if not data['nullable']
25
- line << ", index: true" if data['index'] == true
27
+ line << ", null: false" if data['nullable'] == false
26
28
  end
27
29
 
28
30
  line << "\n"
29
31
 
30
32
  inject_in_migration(file, line)
33
+
34
+ if data["index"].present?
35
+ migration_add_index_to_column(data)
36
+ end
31
37
  end
32
38
 
33
39
  def migration_remove_column(data)
34
- file = data['last_migration_file']
40
+ raise "No table name provided" if data['table_name'].blank?
41
+ raise "No attribute name provided" if data['column_name'].blank?
35
42
 
36
- line = " remove_column :#{data['table_name']}, :#{data['column']}\n"
43
+ line = "#{SPACE_PADDING}remove_column :#{data['table_name']}, :#{data['column_name']}\n"
37
44
 
38
45
  inject_in_migration(file, line)
39
46
  end
40
47
 
41
48
  def migration_rename_column(data)
42
- file = data['last_migration_file']
49
+ raise "No table name provided" if data['table_name'].blank?
50
+ raise "No attribute name provided" if data['column_name'].blank?
51
+ raise "No new attribute name provided" if data['new_column_name'].blank?
43
52
 
44
- line = " rename_column :#{data['table_name']}, :#{data['column']}, :#{data['new_column_name']}\n"
53
+ line = "#{SPACE_PADDING}rename_column :#{data['table_name']}, :#{data['column_name']}, :#{data['new_column_name']}\n"
45
54
 
46
55
  inject_in_migration(file, line)
47
56
  end
48
57
 
49
58
  def migration_change_column_type(data)
50
- file = data['last_migration_file']
59
+ raise "No table name provided" if data['table_name'].blank?
60
+ raise "No attribute name provided" if data['column_name'].blank?
61
+ raise "No new attribute type provided" if data['new_column_type'].blank?
51
62
 
52
- line = " change_column :#{data['table_name']}, :#{data['column']}, :#{data['new_column_type']}\n"
63
+ line = "#{SPACE_PADDING}change_column :#{data['table_name']}, :#{data['column_name']}, :#{data['new_column_type']}\n"
53
64
 
54
65
  inject_in_migration(file, line)
55
66
  end
56
67
 
57
68
  def migration_add_index_to_column(data)
58
- file = data['last_migration_file']
69
+ raise "No table name provided" if data['table_name'].blank?
70
+ raise "No attribute name provided" if data['column_name'].blank?
71
+ raise "No index provided" if data['index'].blank?
59
72
 
60
- line = " add_index :#{data["table_name"]}, :#{data["column"]}\n"
73
+ line = "#{SPACE_PADDING}add_index :#{data['table_name']}, :#{data['column_name']}\n"
61
74
 
62
75
  inject_in_migration(file, line)
63
- end
64
-
65
- # def migration_add_index_to_column_combined(data)
66
- # file = data["last_migration_file"]
67
- # attr_names = data["column"].split(",")
68
76
 
69
- # # add_index :relationships, [:follower_id, :followed_id], unique: true
70
- # line = " add_index :#{data["table_name"]}, [#{attr_names.map { |i| ":#{i}"}.join(', ')}]"
71
- # line << ", unique: true" if data["unique"]
72
- # line << "\n"
73
-
74
- # inject_in_migration(file, line)
75
- # end
77
+ # Custom options:
78
+ params = [data]
79
+ .select { |attr| attr["index"].present? }
80
+ .each_with_object([]) do |attr, arr|
81
+ arr << Hash[attr['column_name'],
82
+ { "using" => attr['index'], "algorithm" => attr['index_algorithm'], "unique" => attr['unique'] }]
83
+ end
84
+ ::Localtower::Generators::ServiceObjects::InsertIndexes.new(params).call
85
+ end
76
86
 
77
87
  def migration_remove_index_to_column(data)
78
- file = data["last_migration_file"]
88
+ raise "No table name provided" if data['table_name'].blank?
89
+ raise "No index_name provided" if data['index_name'].blank?
79
90
 
80
- line = " remove_index :#{data["table_name"]}, :#{data["column"]}\n"
91
+ line = "#{SPACE_PADDING}remove_index :#{data['table_name']}, name: :#{data['index_name']}\n"
81
92
 
82
93
  inject_in_migration(file, line)
83
94
  end
84
95
 
85
96
  def migration_belongs_to(data)
86
- file = data["last_migration_file"]
87
-
88
- line = " add_reference :#{data["table_name"]}, :#{data["column"]}, foreign_key: true, index: true\n"
89
-
90
- inject_in_migration(file, line)
97
+ raise "No table name provided" if data['table_name'].blank?
98
+ raise "No attribute name provided" if data['column_name'].blank?
91
99
 
92
- file = "#{Rails.root}/app/models/#{data["table_name"].underscore.singularize}.rb"
93
- after = /class #{data["table_name"].camelize.singularize} < ApplicationRecord\n/
94
- line1 = " belongs_to :#{data["column"].singularize.underscore}\n"
100
+ line = "#{SPACE_PADDING}add_reference :#{data['table_name']}, :#{data['column_name']}"
95
101
 
96
- ::Localtower::Tools::ThorTools.new.insert_after_word(file, after, line1)
97
-
98
-
99
- file = "#{Rails.root}/app/models/#{data["column"].underscore.singularize}.rb"
100
- after = /class #{data["column"].camelize.singularize} < ApplicationRecord\n/
101
- line1 = " has_many :#{data["table_name"].pluralize.underscore}\n"
102
-
103
- ::Localtower::Tools::ThorTools.new.insert_after_word(file, after, line1)
104
- end
105
-
106
- def migration_create_table(data)
107
- file = data["last_migration_file"]
108
-
109
- line = " create_table :#{data["table_name"]}\n"
102
+ line << ", foreign_key: true" if data['foreign_key'].present?
103
+ line << ", index: true"
104
+ line << "\n"
110
105
 
111
106
  inject_in_migration(file, line)
112
107
  end
113
108
 
114
109
  def migration_drop_table(data)
115
- file = data["last_migration_file"]
110
+ raise "No table name provided" if data['table_name'].blank?
116
111
 
117
- line = " drop_table :#{data["table_name"]}, force: :cascade\n"
112
+ line = "#{SPACE_PADDING}drop_table :#{data['table_name']}\n"
118
113
 
119
114
  inject_in_migration(file, line)
120
115
  end
@@ -122,133 +117,196 @@ module Localtower
122
117
  private
123
118
 
124
119
  def inject_in_migration(file, line)
125
- inject_into_file(file, line, after: "def change\n")
120
+ inject_into_file(file, line, before: " end\nend")
121
+ end
122
+
123
+ def file
124
+ Localtower::Tools.last_migration_pending
126
125
  end
127
126
  end
128
127
  end
129
128
 
130
129
  class Migration
131
- TYPES = %w(string datetime text uuid integer float json jsonb decimal binary boolean array references).freeze
130
+ TYPES = [
131
+ {
132
+ name: "string",
133
+ example: "'foo'",
134
+ },
135
+ {
136
+ name: "text",
137
+ example: "'long foo'",
138
+ },
139
+ {
140
+ name: "datetime",
141
+ example: "2024-07-22 12:28:31.487318",
142
+ },
143
+ {
144
+ name: "date",
145
+ example: "2024-09-24",
146
+ },
147
+ {
148
+ name: "uuid",
149
+ example: "'860f5e00-0000-0000-0000-000000000000'",
150
+ },
151
+ {
152
+ name: "integer",
153
+ example: "123",
154
+ },
155
+ {
156
+ name: "bigint",
157
+ example: "123",
158
+ },
159
+ {
160
+ name: "float",
161
+ example: "30.12",
162
+ },
163
+ {
164
+ name: "numeric",
165
+ example: "30.12",
166
+ },
167
+ {
168
+ name: "decimal",
169
+ example: "30.12",
170
+ },
171
+ {
172
+ name: "json",
173
+ example: "{\"foo\": \"bar\"}",
174
+ },
175
+ {
176
+ name: "jsonb",
177
+ example: "{\"foo\": \"bar\"}",
178
+ },
179
+ {
180
+ name: "binary",
181
+ example: "01010001",
182
+ },
183
+ {
184
+ name: "boolean",
185
+ example: "true/false",
186
+ },
187
+ {
188
+ name: "array",
189
+ example: "[1, 2, 3]",
190
+ },
191
+ {
192
+ name: "blob",
193
+ example: "x3F7F2A9",
194
+ },
195
+ {
196
+ name: "references",
197
+ example: "MyModel",
198
+ }
199
+ ]
200
+
132
201
  ACTIONS = [
133
- 'add_column',
134
- 'remove_column',
135
- 'rename_column',
136
- 'change_column_type',
137
- 'add_index_to_column',
138
- 'belongs_to',
139
- 'remove_index_to_column',
140
- 'drop_table',
141
- ].freeze
142
-
143
- DEFAULTS = [
144
- "true", "false", "nil", "0"
202
+ {
203
+ name: "add_column",
204
+ label: "Add column",
205
+ example: "add_column :users, :name, :string",
206
+ },
207
+ {
208
+ name: "add_index_to_column",
209
+ label: "Add index",
210
+ example: "add_index :users, :name",
211
+ },
212
+ {
213
+ name: "belongs_to",
214
+ label: "Add reference",
215
+ example: "add_reference :posts, :user",
216
+ },
217
+ {
218
+ name: "remove_index_to_column",
219
+ label: "Remove index",
220
+ example: "remove_index :users, :name",
221
+ },
222
+ {
223
+ name: "remove_column",
224
+ label: "Remove column",
225
+ example: "remove_column :users, :name",
226
+ },
227
+ {
228
+ name: "change_column_type",
229
+ label: "Change column type",
230
+ example: "change_column :users, :name, :string",
231
+ },
232
+ {
233
+ name: "rename_column",
234
+ label: "Rename column",
235
+ example: "rename_column :users, :name, :new_name",
236
+ },
237
+ {
238
+ name: "drop_table",
239
+ label: "Drop table",
240
+ example: "drop_table :users",
241
+ }
145
242
  ]
146
243
 
147
244
  # @opts =
148
- def initialize(opts)
245
+ def initialize(migrations, migration_name = nil)
149
246
  @thor = ThorGeneratorMigration.new
150
- @opts = JSON[opts.to_json]
151
- end
152
247
 
153
- # @opts['migrations'] = []
154
- # @opts['run_migrate'] = true
155
- def run
156
- model_names = @opts['migrations'].map { |line| line['table_name'].camelize }.join
157
-
158
- cmd = "Change#{model_names}#{@opts['migration_name']}At#{Time.now.to_i}"
159
- ::Localtower::Tools.perform_migration(cmd, true)
160
-
161
- @opts['migrations'].each do |action_line|
162
- next unless action_line['action'].present?
163
-
164
- # table_name = action_line['table_name']
165
- # column = action_line['column']
166
- # index = action_line['index']
167
- # default = action_line['default']
168
- # null = action_line['null']
169
- # unique = action_line['unique']
170
- # column_type = action_line['column_type']
171
- # new_column_type = action_line['new_column_type']
172
- # new_column_name = action_line['new_column_name']
173
- action_line["last_migration_file"] = last_migration_file
174
-
175
- {
176
- 'add_column' => -> { add_column(action_line) },
177
- 'remove_column' => -> { remove_column(action_line) },
178
- 'rename_column' => -> { rename_column(action_line) },
179
- 'change_column_type' => -> { change_column_type(action_line) },
180
- 'add_index_to_column' => -> { add_index_to_column(action_line) },
181
- 'belongs_to' => -> { belongs_to(action_line) },
182
- 'remove_index_to_column' => -> { remove_index_to_column(action_line) },
183
- 'create_table' => -> { create_table(action_line) },
184
- 'drop_table' => -> { drop_table(action_line) }
185
- }[action_line['action'].downcase].call
186
- end
187
-
188
- if @opts['run_migrate']
189
- run_migration_or_nil
190
- end
248
+ # stringify keys:
249
+ @migrations = JSON[migrations.to_json]
250
+ @migration_name = migration_name
191
251
  end
192
252
 
193
- #====================================
194
- def last_migration_file
195
- Dir["#{Rails.root}/db/migrate/*"].sort.last
196
- end
197
-
198
- # VALIDATION before continuing:
199
- private
253
+ # const DEFAULT_LINE = {
254
+ # model_name: MODELS[0] ? MODELS[0].value : '',
255
+ # table_name: MODELS[0] ? MODELS[0].table_name : '',
256
+ # action_name: 'add_column',
257
+ # column_type: 'string',
258
+ # column_name: '',
259
+ # new_column_name: '',
260
+ # new_column_type: '',
261
+ # default: '',
262
+ # unique: false,
263
+ # foreign_key: false,
264
+ # index: '',
265
+ # index_algorithm: 'default',
266
+ # index_name: '',
267
+ # nullable: true
268
+ # };
200
269
 
201
- def add_column(data)
202
- @thor.migration_add_column(data)
203
- end
204
-
205
- def remove_column(data)
206
- @thor.migration_remove_column(data)
207
- end
208
-
209
- def rename_column(data)
210
- @thor.migration_rename_column(data)
211
- end
212
-
213
- def change_column_type(data)
214
- @thor.migration_change_column_type(data)
215
- end
270
+ def run
271
+ if Localtower::Tools.pending_migrations.any?
272
+ # raise "There is a pending migration. You can't generate a migration until the pending migration is committed."
273
+ else
274
+ migration_name_final = if @migration_name.present?
275
+ @migration_name
276
+ else
277
+ action_names = migrations.map { |line| line['action_name'].camelize }.uniq.take(1).join
278
+ model_names = migrations.map { |line| line['model_name'].camelize }.uniq.take(2).join
279
+ column_names = migrations.map { |line| (line['column_name'].presence || "").camelize }.uniq.take(2).join
216
280
 
217
- def add_index_to_column(data)
218
- @thor.migration_add_index_to_column(data)
219
- end
281
+ "#{action_names}#{column_names}For#{model_names}"
282
+ end
220
283
 
221
- def belongs_to(data)
222
- data["column"] = data["belongs_to"].underscore.singularize
223
- data['column_type'] = "references"
284
+ ::Localtower::Tools.perform_migration(migration_name_final)
285
+ end
224
286
 
225
- @thor.migration_belongs_to(data)
226
- end
287
+ migrations.each do |action_line|
288
+ next if action_line['action_name'].blank?
227
289
 
228
- def remove_index_to_column(data)
229
- @thor.migration_remove_index_to_column(data)
230
- end
290
+ # action_line['table_name'] = action_line['model_name'].pluralize.underscore
231
291
 
232
- def create_table(data)
233
- @thor.migration_create_table(data)
234
- end
292
+ proc_var = {
293
+ 'add_column' => -> { thor.migration_add_column(action_line) },
294
+ 'remove_column' => -> { thor.migration_remove_column(action_line) },
295
+ 'rename_column' => -> { thor.migration_rename_column(action_line) },
296
+ 'change_column_type' => -> { thor.migration_change_column_type(action_line) },
297
+ 'add_index_to_column' => -> { thor.migration_add_index_to_column(action_line) },
298
+ 'belongs_to' => -> { thor.migration_belongs_to(action_line) },
299
+ 'remove_index_to_column' => -> { thor.migration_remove_index_to_column(action_line) },
300
+ 'drop_table' => -> { thor.migration_drop_table(action_line) }
301
+ }[action_line['action_name']]
235
302
 
236
- def drop_table(data)
237
- @thor.migration_drop_table(data)
238
- end
303
+ raise "No proc found for action_name #{action_line['action_name']}" unless proc_var
239
304
 
240
- def run_migration_or_nil
241
- if last_migration_changed?
242
- ::Localtower::Tools.perform_cmd('rake db:migrate')
243
- else
244
- File.delete(last_migration_file)
245
- nil
305
+ proc_var.call
246
306
  end
247
307
  end
248
308
 
249
- def last_migration_changed?
250
- File.readlines(last_migration_file).join != ~ /def change\n end/
251
- end
309
+ attr_reader :thor, :migrations
252
310
  end
253
311
  end
254
312
  end
@@ -3,52 +3,85 @@ module Localtower
3
3
  class Model
4
4
  def initialize(opts)
5
5
  @opts = JSON[opts.to_json] # stringify keys
6
+ @model_name = @opts['model_name']
7
+ @attributes = @opts['attributes']
8
+
9
+ raise "No model_name provided" if @model_name.blank?
10
+ raise "No attributes provided" if @attributes.blank?
6
11
  end
7
12
 
8
- # data = {attributes: "", model_name: ""}
9
13
  def run
10
- return nil if @opts['attributes'].blank?
11
- return nil if @opts['model_name'].blank?
14
+ attributes_str = parse_attributes.join(" ")
15
+ cmd = "rails generate model #{model_name.camelize} #{attributes_str} --force"
16
+ ::Localtower::Tools.perform_cmd(cmd)
12
17
 
13
- attributes_list = []
18
+ insert_non_nullable_values
19
+ insert_default_values
20
+ insert_array
21
+ insert_indexes
22
+ insert_foreign_keys
23
+ end
14
24
 
15
- @opts['attributes'].each do |attribute_data|
16
- str = "#{attribute_data["attribute_name"]}:#{attribute_data["attribute_type"]}"
17
- str << ":index" if attribute_data["index"]
25
+ private
18
26
 
19
- attributes_list << str
20
- end
27
+ attr_reader :model_name, :attributes
21
28
 
22
- attributes_str = attributes_list.join(" ")
23
- cmd = "rails g model #{@opts['model_name'].camelize} #{attributes_str}"
29
+ def parse_attributes
30
+ attributes.map do |attr_hash|
31
+ [
32
+ attr_hash['column_name'],
33
+ # we need this transformation of "array":
34
+ (attr_hash['column_type'] == 'array' ? 'string' : attr_hash['column_type']),
35
+ attr_hash["index"].present? ? 'index' : nil
36
+ ].compact.join(':')
37
+ end
38
+ end
24
39
 
25
- ::Localtower::Tools.perform_cmd(cmd, false)
40
+ def insert_array
41
+ params = attributes
42
+ .select { |attr| attr['column_type'] == 'array' }
43
+ .map { |attr| attr['column_name'] }
26
44
 
27
- if defaults_present?
28
- insert_default_values.call
29
- end
45
+ Localtower::Generators::ServiceObjects::InsertArray.new(params).call
46
+ end
30
47
 
31
- if @opts['run_migrate']
32
- ::Localtower::Tools.perform_cmd('rake db:migrate', false)
48
+ def insert_default_values
49
+ params = attributes
50
+ .select { |attr| attr['default'].present? }
51
+ .each_with_object([]) do |attr, arr|
52
+ arr << Hash[attr['column_name'], attr['default']]
33
53
  end
34
54
 
35
- self
55
+ Localtower::Generators::ServiceObjects::InsertDefaults.new(params).call
36
56
  end
37
57
 
38
- private
58
+ def insert_non_nullable_values
59
+ params = attributes
60
+ .select { |attr| attr['nullable'] == false }
61
+ .map { |attr| attr['column_name'] }
39
62
 
40
- def defaults_present?
41
- @opts['attributes'].any? { |attr| attr["defaults"].present? }
63
+ ::Localtower::Generators::ServiceObjects::InsertNullable.new(params).call
42
64
  end
43
65
 
44
- def params_for_defaults
45
- @opts['attributes'].each_with_object([]) do |attr, arr|
46
- arr << Hash[ attr['attribute_name'], attr['defaults'] ] unless attr['defaults'].empty?
66
+ def insert_indexes
67
+ params = attributes
68
+ .select { |attr| attr["index"].present? }
69
+ .each_with_object([]) do |attr, arr|
70
+ arr << Hash[attr['column_name'],
71
+ { "using" => attr['index'], "algorithm" => attr['index_algorithm'], "unique" => attr['unique'] }]
47
72
  end
73
+
74
+ ::Localtower::Generators::ServiceObjects::InsertIndexes.new(params).call
48
75
  end
49
76
 
50
- def insert_default_values
51
- ::Localtower::Generators::ServiceObjects::InsertDefaults.new(params_for_defaults)
77
+ def insert_foreign_keys
78
+ params = attributes
79
+ .each_with_object([]) do |attr, arr|
80
+ arr << Hash[attr['column_name'],
81
+ { "foreign_key" => attr['foreign_key'] }]
82
+ end
83
+
84
+ ::Localtower::Generators::ServiceObjects::InsertForeignKeys.new(params).call
52
85
  end
53
86
  end
54
87
  end