incline 0.1.5

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 (303) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +186 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +208 -0
  7. data/Rakefile +37 -0
  8. data/app/assets/fonts/incline/.keep +0 -0
  9. data/app/assets/images/incline/.keep +0 -0
  10. data/app/assets/images/incline/barcode-B.svg +181 -0
  11. data/app/assets/javascripts/incline/activate_classed_items.js +11 -0
  12. data/app/assets/javascripts/incline/application.js +30 -0
  13. data/app/assets/javascripts/incline/bootstrap-datepicker.js +1800 -0
  14. data/app/assets/javascripts/incline/datatables.js +22193 -0
  15. data/app/assets/javascripts/incline/escapeHtml.js +10 -0
  16. data/app/assets/javascripts/incline/inline_actions.js +479 -0
  17. data/app/assets/javascripts/incline/jquery.doubleScroll.js +112 -0
  18. data/app/assets/javascripts/incline/jquery.number.js +764 -0
  19. data/app/assets/javascripts/incline/regexMask.js +27 -0
  20. data/app/assets/javascripts/incline/select2/i18n/en.js +3 -0
  21. data/app/assets/javascripts/incline/select2/select2.full.js +6436 -0
  22. data/app/assets/stylesheets/incline/application.css +18 -0
  23. data/app/assets/stylesheets/incline/bootstrap-theme.min.css +5 -0
  24. data/app/assets/stylesheets/incline/custom.scss +279 -0
  25. data/app/assets/stylesheets/incline/datatables.css +494 -0
  26. data/app/assets/stylesheets/incline/datepicker3.css +790 -0
  27. data/app/assets/stylesheets/incline/select2.css +484 -0
  28. data/app/controllers/incline/access_groups_controller.rb +127 -0
  29. data/app/controllers/incline/access_test_controller.rb +30 -0
  30. data/app/controllers/incline/account_activations_controller.rb +28 -0
  31. data/app/controllers/incline/application_controller.rb +11 -0
  32. data/app/controllers/incline/contact_controller.rb +34 -0
  33. data/app/controllers/incline/password_resets_controller.rb +113 -0
  34. data/app/controllers/incline/security_controller.rb +100 -0
  35. data/app/controllers/incline/sessions_controller.rb +50 -0
  36. data/app/controllers/incline/users_controller.rb +304 -0
  37. data/app/controllers/incline/welcome_controller.rb +19 -0
  38. data/app/helpers/incline/.keep +0 -0
  39. data/app/mailers/incline/application_mailer_base.rb +11 -0
  40. data/app/mailers/incline/contact_form.rb +19 -0
  41. data/app/mailers/incline/user_mailer.rb +45 -0
  42. data/app/models/incline/access_group.rb +121 -0
  43. data/app/models/incline/access_group_group_member.rb +12 -0
  44. data/app/models/incline/access_group_user_member.rb +10 -0
  45. data/app/models/incline/action_group.rb +12 -0
  46. data/app/models/incline/action_security.rb +222 -0
  47. data/app/models/incline/contact_message.rb +37 -0
  48. data/app/models/incline/disable_info.rb +20 -0
  49. data/app/models/incline/password_reset.rb +14 -0
  50. data/app/models/incline/password_reset_request.rb +14 -0
  51. data/app/models/incline/user.rb +437 -0
  52. data/app/models/incline/user_login_history.rb +30 -0
  53. data/app/views/incline/access_groups/_details.json.jbuilder +10 -0
  54. data/app/views/incline/access_groups/_form.html.erb +19 -0
  55. data/app/views/incline/access_groups/_list.html.erb +60 -0
  56. data/app/views/incline/access_groups/_messages.json.jbuilder +6 -0
  57. data/app/views/incline/access_groups/edit.html.erb +2 -0
  58. data/app/views/incline/access_groups/index.html.erb +6 -0
  59. data/app/views/incline/access_groups/index.json.jbuilder +16 -0
  60. data/app/views/incline/access_groups/new.html.erb +2 -0
  61. data/app/views/incline/access_groups/show.html.erb +9 -0
  62. data/app/views/incline/access_groups/show.json.jbuilder +11 -0
  63. data/app/views/incline/contact/new.html.erb +22 -0
  64. data/app/views/incline/contact_form/contact.html.erb +16 -0
  65. data/app/views/incline/contact_form/contact.text.erb +13 -0
  66. data/app/views/incline/password_resets/edit.html.erb +16 -0
  67. data/app/views/incline/password_resets/new.html.erb +12 -0
  68. data/app/views/incline/security/_details.json.jbuilder +7 -0
  69. data/app/views/incline/security/_form.html.erb +20 -0
  70. data/app/views/incline/security/_list.html.erb +89 -0
  71. data/app/views/incline/security/_messages.json.jbuilder +6 -0
  72. data/app/views/incline/security/edit.html.erb +2 -0
  73. data/app/views/incline/security/index.html.erb +6 -0
  74. data/app/views/incline/security/index.json.jbuilder +16 -0
  75. data/app/views/incline/security/show.html.erb +31 -0
  76. data/app/views/incline/security/show.json.jbuilder +11 -0
  77. data/app/views/incline/sessions/new.html.erb +26 -0
  78. data/app/views/incline/user_mailer/account_activation.html.erb +7 -0
  79. data/app/views/incline/user_mailer/account_activation.text.erb +6 -0
  80. data/app/views/incline/user_mailer/invalid_password_reset.html.erb +3 -0
  81. data/app/views/incline/user_mailer/invalid_password_reset.text.erb +5 -0
  82. data/app/views/incline/user_mailer/password_reset.html.erb +8 -0
  83. data/app/views/incline/user_mailer/password_reset.text.erb +7 -0
  84. data/app/views/incline/users/_details.json.jbuilder +32 -0
  85. data/app/views/incline/users/_form.html.erb +21 -0
  86. data/app/views/incline/users/_list.html.erb +102 -0
  87. data/app/views/incline/users/_messages.json.jbuilder +6 -0
  88. data/app/views/incline/users/disable_confirm.html.erb +19 -0
  89. data/app/views/incline/users/edit.html.erb +5 -0
  90. data/app/views/incline/users/index.html.erb +6 -0
  91. data/app/views/incline/users/index.json.jbuilder +16 -0
  92. data/app/views/incline/users/new.html.erb +5 -0
  93. data/app/views/incline/users/show.html.erb +12 -0
  94. data/app/views/incline/users/show.json.jbuilder +11 -0
  95. data/app/views/incline/welcome/home.html.erb +5 -0
  96. data/app/views/layouts/application.html.erb +1 -0
  97. data/app/views/layouts/incline/_account_menu.html.erb +18 -0
  98. data/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  99. data/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  100. data/app/views/layouts/incline/_footer.html.erb +13 -0
  101. data/app/views/layouts/incline/_header.html.erb +21 -0
  102. data/app/views/layouts/incline/_html_mailer.html.erb +5 -0
  103. data/app/views/layouts/incline/_incline_app.html.erb +25 -0
  104. data/app/views/layouts/incline/_messages.html.erb +3 -0
  105. data/app/views/layouts/incline/_shim.html.erb +3 -0
  106. data/app/views/layouts/incline/_text_mailer.text.erb +1 -0
  107. data/app/views/layouts/incline/application.html.erb +1 -0
  108. data/app/views/layouts/mailer.html.erb +2 -0
  109. data/app/views/layouts/mailer.text.erb +2 -0
  110. data/bin/rails +12 -0
  111. data/bin/test_scaffold.sh +10 -0
  112. data/config/routes.rb +61 -0
  113. data/db/migrate/20170511230126_create_incline_users.rb +26 -0
  114. data/db/migrate/20170515003052_create_incline_access_groups.rb +10 -0
  115. data/db/migrate/20170515003221_create_incline_user_login_histories.rb +12 -0
  116. data/db/migrate/20170515150908_create_incline_access_group_user_members.rb +11 -0
  117. data/db/migrate/20170515151058_create_incline_access_group_group_members.rb +11 -0
  118. data/db/migrate/20170517193432_add_comments_to_incline_user.rb +5 -0
  119. data/db/migrate/20170622132700_create_incline_action_securities.rb +16 -0
  120. data/db/migrate/20170622172712_create_incline_action_groups.rb +11 -0
  121. data/db/migrate/20170622195742_add_non_standard_to_action_security.rb +5 -0
  122. data/db/migrate/20170622230422_add_visible_to_action_security.rb +5 -0
  123. data/db/seeds.rb +81 -0
  124. data/exe/new_incline_app +42 -0
  125. data/lib/generators/incline/install_generator.rb +259 -0
  126. data/lib/generators/incline/templates/_app_menu_anon.html.erb +1 -0
  127. data/lib/generators/incline/templates/_app_menu_authenticated.html.erb +1 -0
  128. data/lib/generators/incline/templates/incline_application.css +17 -0
  129. data/lib/generators/incline/templates/incline_application.html.erb +1 -0
  130. data/lib/generators/incline/templates/incline_application.js +12 -0
  131. data/lib/generators/incline/templates/incline_database.yml +25 -0
  132. data/lib/generators/incline/templates/incline_email.yml +20 -0
  133. data/lib/generators/incline/templates/incline_mailer.html.erb +2 -0
  134. data/lib/generators/incline/templates/incline_mailer.text.erb +2 -0
  135. data/lib/generators/incline/templates/incline_users.yml +64 -0
  136. data/lib/generators/incline/templates/incline_version.rb +3 -0
  137. data/lib/incline/auth_engine_base.rb +52 -0
  138. data/lib/incline/data_tables_request.rb +336 -0
  139. data/lib/incline/date_time_formats.rb +6 -0
  140. data/lib/incline/engine.rb +212 -0
  141. data/lib/incline/errors.rb +15 -0
  142. data/lib/incline/extensions/action_controller_base.rb +526 -0
  143. data/lib/incline/extensions/action_mailer_base.rb +66 -0
  144. data/lib/incline/extensions/action_view_base.rb +489 -0
  145. data/lib/incline/extensions/active_record_base.rb +308 -0
  146. data/lib/incline/extensions/application.rb +137 -0
  147. data/lib/incline/extensions/application_configuration.rb +50 -0
  148. data/lib/incline/extensions/connection_adapter.rb +55 -0
  149. data/lib/incline/extensions/date_time_value.rb +123 -0
  150. data/lib/incline/extensions/date_value.rb +77 -0
  151. data/lib/incline/extensions/decimal_value.rb +55 -0
  152. data/lib/incline/extensions/erb_scaffold_generator.rb +31 -0
  153. data/lib/incline/extensions/float_value.rb +59 -0
  154. data/lib/incline/extensions/form_builder.rb +617 -0
  155. data/lib/incline/extensions/integer_value.rb +54 -0
  156. data/lib/incline/extensions/jbuilder_generator.rb +38 -0
  157. data/lib/incline/extensions/jbuilder_template.rb +39 -0
  158. data/lib/incline/extensions/main_app.rb +40 -0
  159. data/lib/incline/extensions/numeric.rb +63 -0
  160. data/lib/incline/extensions/object.rb +31 -0
  161. data/lib/incline/extensions/resource_route_generator.rb +53 -0
  162. data/lib/incline/extensions/session.rb +113 -0
  163. data/lib/incline/extensions/string.rb +50 -0
  164. data/lib/incline/extensions/test_case.rb +764 -0
  165. data/lib/incline/extensions/time_zone_converter.rb +40 -0
  166. data/lib/incline/global_status.rb +236 -0
  167. data/lib/incline/helpers/route_hash_formatter.rb +46 -0
  168. data/lib/incline/json_log_formatter.rb +96 -0
  169. data/lib/incline/json_logger.rb +17 -0
  170. data/lib/incline/log.rb +153 -0
  171. data/lib/incline/number_formats.rb +17 -0
  172. data/lib/incline/recaptcha.rb +346 -0
  173. data/lib/incline/user_manager.rb +212 -0
  174. data/lib/incline/validators/email_validator.rb +45 -0
  175. data/lib/incline/validators/ip_address_validator.rb +32 -0
  176. data/lib/incline/validators/recaptcha_validator.rb +37 -0
  177. data/lib/incline/validators/safe_name_validator.rb +31 -0
  178. data/lib/incline/version.rb +3 -0
  179. data/lib/incline/work_path.rb +75 -0
  180. data/lib/incline.rb +197 -0
  181. data/lib/tasks/incline_tasks.rake +4 -0
  182. data/lib/templates/erb/scaffold/_form.html.erb +43 -0
  183. data/lib/templates/erb/scaffold/_list.html.erb +81 -0
  184. data/lib/templates/erb/scaffold/edit.html.erb +1 -0
  185. data/lib/templates/erb/scaffold/index.html.erb +6 -0
  186. data/lib/templates/erb/scaffold/new.html.erb +1 -0
  187. data/lib/templates/erb/scaffold/show.html.erb +34 -0
  188. data/lib/templates/jbuilder/scaffold/_details.json.jbuilder +20 -0
  189. data/lib/templates/jbuilder/scaffold/index.json.jbuilder +16 -0
  190. data/lib/templates/jbuilder/scaffold/show.json.jbuilder +16 -0
  191. data/lib/templates/rails/scaffold_controller/controller.rb +128 -0
  192. data/test/controllers/incline/access_groups_controller_test.rb +65 -0
  193. data/test/controllers/incline/access_test_controller_test.rb +53 -0
  194. data/test/controllers/incline/contact_controller_test.rb +32 -0
  195. data/test/controllers/incline/security_controller_test.rb +39 -0
  196. data/test/controllers/incline/welcome_controller_test.rb +16 -0
  197. data/test/dummy/README.rdoc +28 -0
  198. data/test/dummy/Rakefile +6 -0
  199. data/test/dummy/app/assets/images/.keep +0 -0
  200. data/test/dummy/app/assets/javascripts/application.js +12 -0
  201. data/test/dummy/app/assets/stylesheets/application.css +17 -0
  202. data/test/dummy/app/controllers/application_controller.rb +5 -0
  203. data/test/dummy/app/controllers/concerns/.keep +0 -0
  204. data/test/dummy/app/helpers/application_helper.rb +2 -0
  205. data/test/dummy/app/mailers/.keep +0 -0
  206. data/test/dummy/app/models/.keep +0 -0
  207. data/test/dummy/app/models/concerns/.keep +0 -0
  208. data/test/dummy/app/views/layouts/application.html.erb +1 -0
  209. data/test/dummy/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  210. data/test/dummy/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  211. data/test/dummy/app/views/layouts/mailer.html.erb +2 -0
  212. data/test/dummy/app/views/layouts/mailer.text.erb +2 -0
  213. data/test/dummy/bin/bundle +3 -0
  214. data/test/dummy/bin/rails +4 -0
  215. data/test/dummy/bin/rake +4 -0
  216. data/test/dummy/bin/setup +29 -0
  217. data/test/dummy/config/application.rb +38 -0
  218. data/test/dummy/config/boot.rb +5 -0
  219. data/test/dummy/config/database.yml +34 -0
  220. data/test/dummy/config/email.yml +24 -0
  221. data/test/dummy/config/environment.rb +5 -0
  222. data/test/dummy/config/environments/development.rb +45 -0
  223. data/test/dummy/config/environments/production.rb +85 -0
  224. data/test/dummy/config/environments/test.rb +44 -0
  225. data/test/dummy/config/initializers/assets.rb +11 -0
  226. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  227. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  228. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  229. data/test/dummy/config/initializers/inflections.rb +16 -0
  230. data/test/dummy/config/initializers/mime_types.rb +4 -0
  231. data/test/dummy/config/initializers/session_store.rb +3 -0
  232. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  233. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  234. data/test/dummy/config/locales/en.yml +23 -0
  235. data/test/dummy/config/routes.rb +6 -0
  236. data/test/dummy/config.ru +4 -0
  237. data/test/dummy/db/schema.rb +108 -0
  238. data/test/dummy/lib/assets/.keep +0 -0
  239. data/test/dummy/log/.keep +0 -0
  240. data/test/dummy/public/404.html +67 -0
  241. data/test/dummy/public/422.html +67 -0
  242. data/test/dummy/public/500.html +66 -0
  243. data/test/dummy/public/favicon.ico +0 -0
  244. data/test/extensions/action_controller_base_extensions_test.rb +21 -0
  245. data/test/extensions/action_mailer_base_extensions_test.rb +20 -0
  246. data/test/extensions/action_view_base_extensions_test.rb +267 -0
  247. data/test/extensions/active_record_extensions_test.rb +173 -0
  248. data/test/extensions/application_configuration_extensions_test.rb +46 -0
  249. data/test/extensions/application_extensions_test.rb +23 -0
  250. data/test/extensions/connection_adapter_extensions_test.rb +54 -0
  251. data/test/extensions/date_time_value_extensions_test.rb +104 -0
  252. data/test/extensions/date_value_extensions_test.rb +102 -0
  253. data/test/extensions/decimal_value_extensions_test.rb +85 -0
  254. data/test/extensions/erb_scaffold_generator_extensions_test.rb +17 -0
  255. data/test/extensions/float_value_extensions_test.rb +78 -0
  256. data/test/extensions/form_builder_extensions_test.rb +28 -0
  257. data/test/extensions/integer_value_extensions_test.rb +78 -0
  258. data/test/extensions/jbuilder_generator_extensions_test.rb +21 -0
  259. data/test/extensions/jbuilder_template_extensions_test.rb +47 -0
  260. data/test/extensions/main_app_extensions_test.rb +55 -0
  261. data/test/extensions/numeric_extensions_test.rb +76 -0
  262. data/test/extensions/object_extensions_test.rb +104 -0
  263. data/test/extensions/session_extensions_test.rb +69 -0
  264. data/test/extensions/string_extensions_test.rb +32 -0
  265. data/test/extensions/test_case_extensions_test.rb +538 -0
  266. data/test/extensions/time_zone_converter_extensions_test.rb +10 -0
  267. data/test/fixtures/incline/access_group_group_members.yml +1 -0
  268. data/test/fixtures/incline/access_group_user_members.yml +1 -0
  269. data/test/fixtures/incline/access_groups.yml +13 -0
  270. data/test/fixtures/incline/action_groups.yml +6 -0
  271. data/test/fixtures/incline/action_securities.yml +18 -0
  272. data/test/fixtures/incline/user_login_histories.yml +1 -0
  273. data/test/fixtures/incline/users.yml +64 -0
  274. data/test/incline_test.rb +63 -0
  275. data/test/integration/incline/users_edit_test.rb +180 -0
  276. data/test/integration/incline/users_login_test.rb +105 -0
  277. data/test/integration/incline/users_signup_test.rb +147 -0
  278. data/test/integration/navigation_test.rb +11 -0
  279. data/test/lib/data_tables_request_test.rb +245 -0
  280. data/test/lib/date_time_formats_test.rb +111 -0
  281. data/test/lib/global_status_test.rb +89 -0
  282. data/test/lib/json_log_formatter_test.rb +43 -0
  283. data/test/lib/log_test.rb +36 -0
  284. data/test/lib/recaptcha_test.rb +75 -0
  285. data/test/lib/user_manager_test.rb +47 -0
  286. data/test/lib/work_path_test.rb +18 -0
  287. data/test/models/incline/access_group_group_member_test.rb +30 -0
  288. data/test/models/incline/access_group_test.rb +60 -0
  289. data/test/models/incline/access_group_user_member_test.rb +29 -0
  290. data/test/models/incline/action_group_test.rb +27 -0
  291. data/test/models/incline/action_security_test.rb +176 -0
  292. data/test/models/incline/contact_message_test.rb +66 -0
  293. data/test/models/incline/disable_info_test.rb +29 -0
  294. data/test/models/incline/password_reset_request_test.rb +35 -0
  295. data/test/models/incline/password_reset_test.rb +51 -0
  296. data/test/models/incline/user_login_history_test.rb +31 -0
  297. data/test/models/incline/user_test.rb +91 -0
  298. data/test/test_helper.rb +42 -0
  299. data/test/validators/email_validator_test.rb +102 -0
  300. data/test/validators/ip_address_validator_test.rb +107 -0
  301. data/test/validators/recaptcha_validator_test.rb +57 -0
  302. data/test/validators/safe_name_validator_test.rb +101 -0
  303. metadata +584 -0
@@ -0,0 +1,40 @@
1
+ require 'active_record'
2
+
3
+ module Incline::Extensions
4
+ ##
5
+ # Patches the TimeZoneConverter to call super.
6
+ module TimeZoneConverter
7
+
8
+ ##
9
+ # Patches the TimeZoneConverter to call super.
10
+ def self.included(base)
11
+ base.class_eval do
12
+
13
+ undef type_cast_from_user
14
+
15
+ def type_cast_from_user(value)
16
+ if value.is_a?(::Array)
17
+ value.map { |v| type_cast_from_user(v) }
18
+ else
19
+ # Convert to time first.
20
+ value = super
21
+
22
+ # Then convert the time zone if necessary.
23
+ if value.respond_to?(:in_time_zone)
24
+ begin
25
+ value.in_time_zone
26
+ rescue ArgumentError
27
+ nil
28
+ end
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter.include Incline::Extensions::TimeZoneConverter
@@ -0,0 +1,236 @@
1
+ module Incline
2
+ ##
3
+ # An interface to a global status/lock file.
4
+ #
5
+ # The global status/lock file is a simple two line file.
6
+ # The first line is the global status message.
7
+ # The second line is the global status progress.
8
+ #
9
+ # The real magic comes when we take advantage of exclusive locks.
10
+ # The process that will be managing the status takes an exclusive lock on the status/lock file.
11
+ # This prevents any other process from taking an exclusive lock.
12
+ # It does not prevent other processes from reading from the file.
13
+ #
14
+ # So the main process can update the file at any time, until it releases the lock.
15
+ # The other processes can read the file at any time, and test for the lock state to determine if the main
16
+ # process is still busy.
17
+ #
18
+ #
19
+ class GlobalStatus
20
+
21
+ ##
22
+ # Creates a new GlobalStatus object.
23
+ def initialize
24
+ @handle = nil
25
+ end
26
+
27
+ ##
28
+ # Gets the path to the global status/lock file.
29
+ def status_file_path
30
+ @status_file_path ||= WorkPath.path_for('global_lock')
31
+ end
32
+
33
+ ##
34
+ # Determines if this instance has a lock on the status/lock file.
35
+ def have_lock?
36
+ !!@handle
37
+ end
38
+
39
+ ##
40
+ # Determines if any process has a lock on the status/lock file.
41
+ def is_locked?
42
+ return true if have_lock?
43
+ begin
44
+ return true unless acquire_lock
45
+ ensure
46
+ release_lock
47
+ end
48
+ false
49
+ end
50
+
51
+ ##
52
+ # Gets the current status message from the status/lock file.
53
+ def get_message
54
+ get_status[:message]
55
+ end
56
+
57
+ ##
58
+ # Gets the current progress from the status/lock file.
59
+ def get_percentage
60
+ r = get_status[:percent]
61
+ r.blank? ? nil : r.to_i
62
+ end
63
+
64
+ ##
65
+ # Gets the current status from the status/lock file.
66
+ #
67
+ # Returns a hash with three elements:
68
+ #
69
+ # message::
70
+ # The current status message.
71
+ #
72
+ # percent::
73
+ # The current status progress.
74
+ #
75
+ # locked::
76
+ # The current lock state of the status/lock file. (true for locked, false for unlocked)
77
+ #
78
+ def get_status
79
+ r = {}
80
+ if have_lock?
81
+ @handle.rewind
82
+ r[:message] = (@handle.eof? ? 'The current process is busy.' : @handle.readline.strip)
83
+ r[:percent] = (@handle.eof? ? '' : @handle.readline.strip)
84
+ r[:locked] = true
85
+ elsif is_locked?
86
+ if File.exist?(status_file_path)
87
+ begin
88
+ File.open(status_file_path, 'r') do |f|
89
+ r[:message] = (f.eof? ? 'The system is busy.' : f.readline.strip)
90
+ r[:percent] = (f.eof? ? '' : f.readline.strip)
91
+ end
92
+ rescue
93
+ r[:message] = 'The system appears busy.'
94
+ r[:percent] = ''
95
+ end
96
+ else
97
+ r[:message] = 'No status file.'
98
+ r[:percent] = ''
99
+ end
100
+ r[:locked] = true
101
+ else
102
+ r[:message] = 'The system is no longer busy.'
103
+ r[:percent] = '-'
104
+ r[:locked] = false
105
+ end
106
+ r
107
+ end
108
+
109
+ ##
110
+ # Sets the status message if this instance has a lock on the status/lock file.
111
+ #
112
+ # Returns true after successfully setting the message.
113
+ # Returns false if this instance does not currently hold the lock.
114
+ #
115
+ def set_message(value)
116
+ return false unless have_lock?
117
+ cur = get_status
118
+ set_status(value, cur[:percent])
119
+ end
120
+
121
+ ##
122
+ # Sets the status progress if this instance has a lock on the status/lock file.
123
+ #
124
+ # Returns true after successfully setting the progress.
125
+ # Returns false if this instance does not currently hold the lock.
126
+ #
127
+ def set_percentage(value)
128
+ return false unless have_lock?
129
+ cur = get_status
130
+ set_status(cur[:message], value)
131
+ end
132
+
133
+ ##
134
+ # Sets the status message and progress if this instance has a lock on the status/lock file.
135
+ #
136
+ # Returns true after successfully setting the status.
137
+ # Returns false if this instance does not currently hold the lock.
138
+ #
139
+ def set_status(message, percentage)
140
+ return false unless have_lock?
141
+ @handle.rewind
142
+ @handle.truncate 0
143
+ @handle.write(message.to_s.strip + "\n")
144
+ @handle.write(percentage.to_s.strip + "\n")
145
+ @handle.flush
146
+ true
147
+ end
148
+
149
+ ##
150
+ # Releases the lock on the status/lock file if this instance holds the lock.
151
+ #
152
+ # Returns true.
153
+ #
154
+ def release_lock
155
+ return true unless @handle
156
+ set_message ''
157
+ @handle.flock(File::LOCK_UN)
158
+ @handle.close
159
+ @handle = nil
160
+ true
161
+ end
162
+
163
+ ##
164
+ # Acquires the lock on the status/lock file.
165
+ #
166
+ # Returns true on success or if this instance already holds the lock.
167
+ # Returns false if another process holds the lock.
168
+ #
169
+ def acquire_lock
170
+ return true if @handle
171
+ begin
172
+ @handle = File.open(status_file_path, File::RDWR | File::CREAT)
173
+ raise StandardError.new('Already locked') unless @handle.flock(File::LOCK_EX | File::LOCK_NB)
174
+ @handle.rewind
175
+ @handle.truncate 0
176
+ rescue
177
+ if @handle
178
+ @handle.flock(File::LOCK_UN)
179
+ @handle.close
180
+ end
181
+ @handle = nil
182
+ end
183
+ !!@handle
184
+ end
185
+
186
+ ##
187
+ # Determines if any process currently holds the lock on the status/lock file.
188
+ #
189
+ # Returns true if the file is locked, otherwise returns false.
190
+ #
191
+ def self.locked?
192
+ global_instance.is_locked?
193
+ end
194
+
195
+ ##
196
+ # Gets the current status from the status/lock file.
197
+ #
198
+ # See #get_status for a description of the returned hash.
199
+ #
200
+ def self.current
201
+ global_instance.get_status
202
+ end
203
+
204
+ ##
205
+ # Runs the provided block with a lock on the status/lock file.
206
+ #
207
+ # If a lock can be acquired, a GlobalStatus object is yielded to the block.
208
+ # The lock will automatically be released when the block exits.
209
+ #
210
+ # If a lock cannot be acquire, then false is yielded to the block.
211
+ # The block needs to test for this case to ensure that the appropriate
212
+ # error handling is performed.
213
+ #
214
+ def self.lock_for(&block)
215
+ return unless block_given?
216
+ status = GlobalStatus.new
217
+ if status.acquire_lock
218
+ begin
219
+ yield status
220
+ ensure
221
+ status.release_lock
222
+ end
223
+ else
224
+ yield false
225
+ end
226
+ end
227
+
228
+ private
229
+
230
+ def self.global_instance
231
+ @global_instance ||= GlobalStatus.new
232
+ end
233
+
234
+ end
235
+ end
236
+
@@ -0,0 +1,46 @@
1
+ module Incline::Helpers
2
+ ##
3
+ # A "formatter" that simply collects formatted route data.
4
+ class RouteHashFormatter
5
+
6
+ ##
7
+ # Creates a new hash formatter for the route inspector.
8
+ def initialize
9
+ @buffer = []
10
+ @engine = ''
11
+ end
12
+
13
+ ##
14
+ # Gets the resulting hash from the route inspector.
15
+ def result
16
+ @buffer
17
+ end
18
+
19
+ ##
20
+ # Analyzes the section title to get the current engine name.
21
+ def section_title(title)
22
+ @engine = title.include?(' ') ? title.rpartition(' ')[2] : title
23
+ end
24
+
25
+ ##
26
+ # Does nothing for this formatter.
27
+ def header(routes)
28
+ # no need for a header
29
+ end
30
+
31
+ ##
32
+ # Does nothing for this formatter.
33
+ def no_routes
34
+ # no need to do anything here either.
35
+ end
36
+
37
+ ##
38
+ # Adds the specified routes to the resulting hash.
39
+ def section(routes)
40
+ routes.each do |r|
41
+ @buffer << r.symbolize_keys.merge(engine: @engine)
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,96 @@
1
+ require 'logger'
2
+
3
+ module Incline
4
+ ##
5
+ # A log formatter that writes entries in JSON format (each line is a valid JSON object).
6
+ class JsonLogFormatter < ::Logger::Formatter
7
+
8
+ ##
9
+ # Regular expressions used to auto-classify any matching message as a debug message.
10
+ AUTO_DEBUG_PATTERNS = [
11
+ /^rendered\s/i,
12
+ /started\sget\s"\/assets/i
13
+ ]
14
+
15
+ ##
16
+ # Overrides the default formatter behavior to log a JSON line.
17
+ def call(sev, time, _, msg) #:nodoc:
18
+ level = ({
19
+ Logger::DEBUG => 'DEBUG',
20
+ Logger::INFO => 'INFO',
21
+ Logger::WARN => 'WARN',
22
+ Logger::ERROR => 'ERROR',
23
+ Logger::FATAL => 'FATAL',
24
+ }[sev] || sev.to_s).upcase
25
+
26
+ if msg.present? && AUTO_DEBUG_PATTERNS.find{|pattern| msg =~ pattern}
27
+ return '' if debug_skip?
28
+ level = 'DEBUG'
29
+ end
30
+
31
+ if msg.present?
32
+ # And we'll expand exceptions so we get as much info as possible.
33
+ # If you just want the message, make sure you just pass the message.
34
+ if msg.is_a?(::Exception)
35
+ msg = "#{msg.message} (#{msg.class})\n#{(msg.backtrace || []).join("\n")}"
36
+ elsif !msg.is_a?(::String)
37
+ msg = msg.inspect
38
+ end
39
+
40
+ msg = rm_fmt msg
41
+
42
+ {
43
+ level: level,
44
+ time: time.strftime('%Y-%m-%d %H:%M:%S'),
45
+ message: msg,
46
+ app_name: app_name,
47
+ app_version: app_version,
48
+ process_id: Process.pid,
49
+ }.to_json + "\r\n"
50
+ else
51
+ ''
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def app_name
58
+ if Object.const_defined?(:Rails)
59
+ Rails&.application&.app_name || 'Unknown'
60
+ else
61
+ 'Unknown'
62
+ end
63
+ end
64
+
65
+ def app_version
66
+ if Object.const_defined?(:Rails)
67
+ Rails&.application&.app_version || '0.0.0'
68
+ else
69
+ '0.0.0'
70
+ end
71
+ end
72
+
73
+ def debug_skip?
74
+ if Object.const_defined?(:Rails)
75
+ (Rails&.logger&.level).to_s.to_i > 0
76
+ else
77
+ false
78
+ end
79
+ end
80
+
81
+ def rm_fmt(msg)
82
+ msg
83
+ .gsub(/\e\[(\d+;?)*[ABCDEFGHfu]/, "\n") # any of the "set cursor position" CSI commands.
84
+ .gsub(/\e\[=?(\d+;?)*[A-Za-z]/,'') # \e[#;#;#A or \e[=#;#;#A basically all the CSI commands except ...
85
+ .gsub(/\e\[(\d+;"[^"]+";?)+p/, '') # \e[#;"A"p
86
+ .gsub(/\e[NOc]./,'?') # any of the alternate character set commands.
87
+ .gsub(/\e[P_\]^X][^\e\a]*(\a|(\e\\))/,'') # any string command
88
+ .gsub(/[\x00\x08\x0B\x0C\x0E-\x1F]/, '') # any non-printable characters (notice \x0A (LF) and \x0D (CR) are left as is).
89
+ .gsub("\t", ' ') # turn tabs into spaces.
90
+ .gsub("\r\n", "\n") # all CRLF to LF
91
+ .gsub("\r", "\n") # all CR to LF
92
+ .strip # remove trailing and leading whitespace
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_support/logger'
2
+ require 'incline/json_log_formatter'
3
+
4
+ module Incline
5
+ ##
6
+ # Overrides the default formatter for the base logger.
7
+ class JsonLogger < ::ActiveSupport::Logger
8
+
9
+ ##
10
+ # Sets the formatter to Incline::JsonLogFormatter.
11
+ def initialize(*args)
12
+ super
13
+ @formatter = ::Incline::JsonLogFormatter.new
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,153 @@
1
+ require 'ansi/code'
2
+
3
+ module Incline
4
+ ##
5
+ # A logging wrapper to tag log messages with location information.
6
+ class Log
7
+
8
+ ##
9
+ # Logs a debug message.
10
+ def self.debug(msg = nil, &block)
11
+ safe_log :debug, msg, &block
12
+ end
13
+
14
+ ##
15
+ # Logs an info message.
16
+ def self.info(msg = nil, &block)
17
+ safe_log :info, msg, &block
18
+ end
19
+
20
+ ##
21
+ # Logs a warning message.
22
+ def self.warn(msg = nil, &block)
23
+ safe_log :warn, msg, &block
24
+ end
25
+
26
+ ##
27
+ # Logs an error message.
28
+ def self.error(msg = nil, &block)
29
+ safe_log :error, msg, &block
30
+ end
31
+
32
+ ##
33
+ # Gets a list of paths that are considered root paths for logging purposes.
34
+ def self.root_paths
35
+ @root_paths ||=
36
+ begin
37
+ [
38
+ Rails.root.to_s,
39
+ File.expand_path('../../../', __FILE__)
40
+ ]
41
+ .map{|v| v[-1] == '/' ? v : "#{v}/"}
42
+ end
43
+ end
44
+
45
+ ##
46
+ # Set output to go to a file.
47
+ #
48
+ # If a +file+ is specified, it will be used for output. This will bypass Rails logging.
49
+ # If +file+ is set to false or nil then the default logging behavior will be used.
50
+ def self.set_output(file)
51
+ if file
52
+ if file.respond_to?(:puts)
53
+ @output = file
54
+ @rails = nil
55
+ elsif file.is_a?(::String)
56
+ @output = File.open(file, 'wt')
57
+ @rails = nil
58
+ else
59
+ raise ArgumentError, 'The file parameter must be an IO-like object, a string path, or a false value.'
60
+ end
61
+ else
62
+ # reset behavior
63
+ remove_instance_variable(:@output) if instance_variable_defined?(:@output)
64
+ remove_instance_variable(:@rails) if instance_variable_defined?(:@rails)
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Gets the current logging output.
70
+ def self.get_output
71
+ rails&.logger || output
72
+ end
73
+
74
+ private
75
+
76
+ def self.output
77
+ # always returns something.
78
+ (instance_variable_defined?(:@output) ? instance_variable_get(:@output) : $stderr) || $stderr
79
+ end
80
+
81
+ def self.skip?(level)
82
+ filt_level = (log_level || 0)
83
+ if filt_level > 0
84
+ return true if level == :debug
85
+ return true if level == :info && filt_level > 1
86
+ return true if level == :warn && filt_level > 2
87
+ end
88
+ false
89
+ end
90
+
91
+ def self.rails
92
+ unless instance_variable_defined?(:@rails)
93
+ @rails = Object.const_defined?(:Rails) ? Object.const_get(:Rails) : nil
94
+ end
95
+ @rails
96
+ end
97
+
98
+ def self.log_level
99
+ rails&.logger&.level
100
+ end
101
+
102
+ def self.safe_log(level, msg)
103
+ return '' if skip?(level)
104
+
105
+ # by allowing a block, we can defer message processing and skip it altogether when the level is being silenced.
106
+ msg = yield if block_given?
107
+
108
+ c = caller_locations(2,1)[0]
109
+ c = "#{relative_path(c.path)}:#{c.lineno}:in `#{c.base_label}`"
110
+ msg = "[#{c}] #{msg_to_str(msg)}"
111
+
112
+ if rails&.logger
113
+ rails.logger.send level, msg
114
+ else
115
+ level = case level
116
+ when :error
117
+ ANSI.ansi level.to_s.upcase, :bright, :red
118
+ when :warn
119
+ ANSI.ansi level.to_s.upcase, :yellow
120
+ when :info
121
+ ANSI.ansi level.to_s.upcase, :bright, :white
122
+ else
123
+ level.to_s.upcase
124
+ end
125
+
126
+ output.puts "#{level}: #{msg}"
127
+ end
128
+
129
+ msg
130
+ end
131
+
132
+ def self.relative_path(path)
133
+ path = path.to_s
134
+ root_paths.each do |rp|
135
+ if path[rp]
136
+ return path[rp.length..-1]
137
+ end
138
+ end
139
+ path
140
+ end
141
+
142
+ def self.msg_to_str(msg)
143
+ if msg.is_a?(::Exception)
144
+ "#{msg.message} (#{msg.class})\n#{(msg.backtrace || []).join("\n")}"
145
+ elsif msg.is_a?(::String)
146
+ msg
147
+ else
148
+ msg.inspect
149
+ end
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,17 @@
1
+ module Incline
2
+ module NumberFormats
3
+
4
+ ##
5
+ # Verifies a number with comma delimiters included.
6
+ #
7
+ # 1,234,567.89e0
8
+ WITH_DELIMITERS = /\A[+-]?(0|[1-9][0-9]{0,2}(,[0-9]{3})*)(\.[0-9]*)?(e\d+)?\z/i
9
+
10
+ ##
11
+ # Verifies a number without comma delimiters included.
12
+ #
13
+ # 1234567.89e0
14
+ WITHOUT_DELIMITERS = /\A[+-]?([0-9]+)(\.[0-9]*)?(e\d+)?\z/i
15
+
16
+ end
17
+ end