tramway 0.1.0 → 0.1.1

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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +889 -15
  4. data/Rakefile +8 -8
  5. data/app/assets/config/tramway_core_manifest.js +2 -0
  6. data/app/assets/images/tramway/mona_lisa_from_prado.jpg +0 -0
  7. data/app/assets/images/tramway/mona_lisa_from_prado_square.jpg +0 -0
  8. data/app/assets/javascripts/bootstrap-datepicker-1.8.0.js +2035 -0
  9. data/app/assets/javascripts/bootstrap-datepicker-1.8.0.ru.min.js +64 -0
  10. data/app/assets/javascripts/ckeditor/config.js +30 -0
  11. data/app/assets/javascripts/ckeditor/plugins/image/dialogs/image.js +1259 -0
  12. data/app/assets/javascripts/ckeditor/plugins/image/icons/hidpi/image.png +0 -0
  13. data/app/assets/javascripts/ckeditor/plugins/image/icons/image.png +0 -0
  14. data/app/assets/javascripts/ckeditor/plugins/image/images/noimage.png +0 -0
  15. data/app/assets/javascripts/ckeditor/plugins/image/lang/en.js +25 -0
  16. data/app/assets/javascripts/ckeditor/plugins/image/lang/ru.js +25 -0
  17. data/app/assets/javascripts/ckeditor/plugins/image/plugin.js +184 -0
  18. data/app/assets/javascripts/ckeditor/plugins/youtube/images/icon-hdpi.png +0 -0
  19. data/app/assets/javascripts/ckeditor/plugins/youtube/images/icon.png +0 -0
  20. data/app/assets/javascripts/ckeditor/plugins/youtube/lang/en.js +25 -0
  21. data/app/assets/javascripts/ckeditor/plugins/youtube/lang/ru.js +25 -0
  22. data/app/assets/javascripts/ckeditor/plugins/youtube/plugin.js +449 -0
  23. data/app/assets/javascripts/tramway/application.js +60 -0
  24. data/app/assets/stylesheets/tramway/application.sass +60 -0
  25. data/app/assets/stylesheets/tramway/bootstrap-datepicker-1.8.0.css +477 -0
  26. data/app/controllers/concerns/auth_management.rb +26 -0
  27. data/app/controllers/concerns/filtering.rb +43 -0
  28. data/app/controllers/tramway/application_controller.rb +142 -0
  29. data/app/controllers/tramway/export/application_controller.rb +5 -0
  30. data/app/controllers/tramway/has_and_belongs_to_many_records_controller.rb +26 -0
  31. data/app/controllers/tramway/records_controller.rb +86 -0
  32. data/app/controllers/tramway/sessions_controller.rb +49 -0
  33. data/app/controllers/tramway/singletons_controller.rb +48 -0
  34. data/app/controllers/tramway/welcome_controller.rb +17 -0
  35. data/app/decorators/tramway/application_decorated_collection.rb +15 -0
  36. data/app/decorators/tramway/application_decorator.rb +124 -0
  37. data/app/decorators/tramway/associations/class_helper.rb +45 -0
  38. data/app/decorators/tramway/associations/object_helper.rb +58 -0
  39. data/app/decorators/tramway/attributes/view_helper.rb +30 -0
  40. data/app/decorators/tramway/concerns/attributes_decorator_helper.rb +97 -0
  41. data/app/decorators/tramway/concerns/table_builder.rb +33 -0
  42. data/app/decorators/tramway/default/values_helper.rb +23 -0
  43. data/app/decorators/tramway/delegating/class_helper.rb +9 -0
  44. data/app/decorators/tramway/user_decorator.rb +49 -0
  45. data/app/forms/admin/tramway/user_form.rb +24 -0
  46. data/app/forms/tramway/application_form.rb +124 -0
  47. data/app/forms/tramway/application_forms/association_class_helpers.rb +7 -0
  48. data/app/forms/tramway/application_forms/association_object_helpers.rb +36 -0
  49. data/app/forms/tramway/application_forms/constant_class_actions.rb +7 -0
  50. data/app/forms/tramway/application_forms/constant_object_actions.rb +20 -0
  51. data/app/forms/tramway/application_forms/frontend.rb +12 -0
  52. data/app/forms/tramway/application_forms/object_helpers.rb +15 -0
  53. data/app/forms/tramway/application_forms/properties_object_helper.rb +16 -0
  54. data/app/forms/tramway/application_forms/submit_helper.rb +26 -0
  55. data/app/forms/tramway/extendable_form.rb +15 -0
  56. data/app/forms/tramway/extendable_forms_helpers/class_builder.rb +34 -0
  57. data/app/forms/tramway/extendable_forms_helpers/ignored_properties_helper.rb +11 -0
  58. data/app/forms/tramway/extendable_forms_helpers/more_properties_helper.rb +31 -0
  59. data/app/forms/tramway/extendable_forms_helpers/properties_helper.rb +16 -0
  60. data/app/forms/tramway/extendable_forms_helpers/submit/class_helpers.rb +18 -0
  61. data/app/forms/tramway/extendable_forms_helpers/submit/object_helpers.rb +21 -0
  62. data/app/forms/tramway/extendable_forms_helpers/validators.rb +40 -0
  63. data/app/forms/tramway/extended_application_form.rb +23 -0
  64. data/app/forms/tramway/form_creator.rb +7 -0
  65. data/app/forms/tramway/session_form.rb +26 -0
  66. data/app/helpers/tramway/actions_helper.rb +45 -0
  67. data/app/helpers/tramway/additional_buttons_builder.rb +12 -0
  68. data/app/helpers/tramway/application_helper.rb +27 -0
  69. data/app/helpers/tramway/cases_helper.rb +13 -0
  70. data/app/helpers/tramway/copy_to_clipboard_helper.rb +11 -0
  71. data/app/helpers/tramway/focus_generator_helper.rb +10 -0
  72. data/app/helpers/tramway/frontend_helper.rb +26 -0
  73. data/app/helpers/tramway/inputs/associations_helper.rb +30 -0
  74. data/app/helpers/tramway/inputs/polymorphic_associations_helper.rb +24 -0
  75. data/app/helpers/tramway/inputs_helper.rb +96 -0
  76. data/app/helpers/tramway/model_helper.rb +7 -0
  77. data/app/helpers/tramway/navbar_helper.rb +11 -0
  78. data/app/helpers/tramway/records_helper.rb +120 -0
  79. data/app/helpers/tramway/russian_cases_helper.rb +26 -0
  80. data/app/helpers/tramway/singleton_helper.rb +12 -0
  81. data/app/helpers/tramway/state_machine_buttons_helper.rb +61 -0
  82. data/app/helpers/tramway/title_helper.rb +25 -0
  83. data/app/inputs/date_picker_input.rb +4 -0
  84. data/app/inputs/multiple_file_input.rb +7 -0
  85. data/app/models/tramway/application_record.rb +60 -0
  86. data/app/models/tramway/user.rb +23 -0
  87. data/app/uploaders/application_uploader.rb +22 -0
  88. data/app/uploaders/file_uploader.rb +4 -0
  89. data/app/uploaders/ico_uploader.rb +7 -0
  90. data/app/uploaders/image_defaults.rb +14 -0
  91. data/app/uploaders/photo_uploader.rb +54 -0
  92. data/app/views/layouts/tramway/application.html.haml +32 -0
  93. data/app/views/layouts/tramway/shared/_navbar.html.haml +46 -0
  94. data/app/views/tramway/404.haml +1 -0
  95. data/app/views/tramway/records/_form.html.haml +22 -0
  96. data/app/views/tramway/records/_list.html.haml +38 -0
  97. data/app/views/tramway/records/_search.html.haml +34 -0
  98. data/app/views/tramway/records/edit.html.haml +1 -0
  99. data/app/views/tramway/records/index.html.haml +31 -0
  100. data/app/views/tramway/records/new.html.haml +1 -0
  101. data/app/views/tramway/records/show.html.haml +1 -0
  102. data/app/views/tramway/sessions/new.html.haml +9 -0
  103. data/app/views/tramway/shared/_input.html.haml +34 -0
  104. data/app/views/tramway/shared/_input_extended.html.haml +14 -0
  105. data/app/views/tramway/shared/_messages.html.haml +10 -0
  106. data/app/views/tramway/shared/_show.html.haml +35 -0
  107. data/app/views/tramway/shared/errors/server_error.html.haml +12 -0
  108. data/app/views/tramway/shared/input_extended_types/_checkbox.html.haml +1 -0
  109. data/app/views/tramway/shared/input_extended_types/_select.html.haml +16 -0
  110. data/app/views/tramway/shared/input_extended_types/_simple.html.haml +4 -0
  111. data/app/views/tramway/shared/input_extended_types/_yes_no.html.haml +4 -0
  112. data/app/views/tramway/shared/show/_attribute_tr.html.haml +9 -0
  113. data/app/views/tramway/shared/show/associations/_habtm_row.html.haml +14 -0
  114. data/app/views/tramway/shared/show/associations/_list.html.haml +21 -0
  115. data/app/views/tramway/shared/show/associations/_row.html.haml +19 -0
  116. data/app/views/tramway/shared/show/associations/_table_row.html.haml +21 -0
  117. data/app/views/tramway/singletons/_form.html.haml +15 -0
  118. data/app/views/tramway/singletons/edit.html.haml +1 -0
  119. data/app/views/tramway/singletons/new.html.haml +1 -0
  120. data/app/views/tramway/singletons/show.html.haml +1 -0
  121. data/app/views/tramway/welcome/index.html.haml +4 -0
  122. data/config/initializers/assets.rb +7 -0
  123. data/config/initializers/carrierwave.rb +5 -0
  124. data/config/initializers/ckeditor.rb +9 -0
  125. data/config/initializers/plurals.rb +25 -0
  126. data/config/locales/en/collections.yml +4 -0
  127. data/config/locales/en/date.yml +16 -0
  128. data/config/locales/en/dates.yml +10 -0
  129. data/config/locales/en/helpers.yml +26 -0
  130. data/config/locales/en/locale.yml +11 -0
  131. data/config/locales/en/messages.yml +7 -0
  132. data/config/locales/en/models.yml +9 -0
  133. data/config/locales/en/simple_form_extension.yml +8 -0
  134. data/config/locales/en/state_machines.yml +8 -0
  135. data/config/locales/ru/collections.yml +4 -0
  136. data/config/locales/ru/date.yml +16 -0
  137. data/config/locales/ru/dates.yml +10 -0
  138. data/config/locales/ru/helpers.yml +30 -0
  139. data/config/locales/ru/locale.yml +6 -0
  140. data/config/locales/ru/messages.yml +7 -0
  141. data/config/locales/ru/models.yml +21 -0
  142. data/config/locales/ru/simple_form_extension.yml +8 -0
  143. data/config/locales/ru/state_machines.yml +8 -0
  144. data/config/routes.rb +13 -0
  145. data/lib/string.rb +18 -0
  146. data/lib/tramway/application.rb +10 -0
  147. data/lib/tramway/class_name_helpers.rb +15 -0
  148. data/lib/tramway/collection.rb +9 -0
  149. data/lib/tramway/collections/helper.rb +21 -0
  150. data/lib/tramway/collections.rb +4 -0
  151. data/lib/tramway/engine.rb +14 -0
  152. data/lib/tramway/error.rb +32 -0
  153. data/lib/tramway/forms.rb +5 -0
  154. data/lib/tramway/generators/install_generator.rb +49 -0
  155. data/lib/tramway/generators/model_generator.rb +105 -0
  156. data/lib/tramway/generators/templates/create_tramway_users.rb +18 -0
  157. data/lib/tramway/generators/templates/decorator.rb.erb +56 -0
  158. data/lib/tramway/generators/templates/form.rb.erb +22 -0
  159. data/lib/tramway/generators/templates/initializers/simple_form.rb +23 -0
  160. data/lib/tramway/generators/templates/initializers/simple_form_bootstrap.rb +133 -0
  161. data/lib/tramway/generators.rb +4 -0
  162. data/lib/tramway/navbar.rb +44 -0
  163. data/lib/tramway/notifications.rb +12 -0
  164. data/lib/tramway/record_routes_helper.rb +23 -0
  165. data/lib/tramway/records_models.rb +52 -0
  166. data/lib/tramway/singleton_models.rb +32 -0
  167. data/lib/tramway/spec/helpers/navbar_helper.rb +7 -0
  168. data/lib/tramway/spec/helpers/tramway_helpers.rb +47 -0
  169. data/lib/tramway/version.rb +3 -1
  170. data/lib/tramway/welcome_page_actions.rb +5 -0
  171. data/lib/tramway/yaml/errors.yml +49 -0
  172. data/lib/tramway.rb +143 -2
  173. data/lib/validators/presence_validator.rb +9 -0
  174. metadata +582 -25
  175. data/.gitignore +0 -9
  176. data/.travis.yml +0 -5
  177. data/Gemfile +0 -6
  178. data/bin/console +0 -14
  179. data/bin/setup +0 -8
  180. data/tramway.gemspec +0 -26
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::ApplicationHelper
4
+ include FontAwesome5::Rails::IconHelper
5
+ include SmartButtons
6
+ include Tramway::AuthManagement
7
+
8
+ helpers = Dir["#{Tramway.root}/app/helpers/tramway/*_helper.rb"]
9
+ helpers.each do |helper|
10
+ module_name = helper.split('/')[-2..].join('/')[0..-4].camelize.constantize
11
+
12
+ include module_name unless Tramway::ApplicationHelper
13
+ end
14
+
15
+ def object_type(object)
16
+ object_class_name = if object.class.ancestors.include? ::Tramway::ApplicationDecorator
17
+ object.class.model_class.name
18
+ else
19
+ object.class.name
20
+ end
21
+ if Tramway.available_models_for(@application_engine || @application.name).map(&:to_s).include?(object_class_name)
22
+ :record
23
+ else
24
+ :singleton
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::CasesHelper
4
+ def plural(word)
5
+ if I18n.locale == :ru
6
+ russian_plural word
7
+ elsif word.respond_to?(:model_name)
8
+ word.model_name.human.pluralize(I18n.locale)
9
+ else
10
+ word.human.pluralize(I18n.locale)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::CopyToClipboardHelper
4
+ def copy_to_clipboard(id)
5
+ button_tag class: 'btn btn-info clipboard-btn',
6
+ data: { clipboard_action: 'copy', clipboard_target: "##{id}" },
7
+ style: 'margin-left: 15px' do
8
+ fa_icon 'copy'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::Tramway
4
+ module FocusGeneratorHelper
5
+ # FIXME: create independent focus generator
6
+ def focus_selector(index)
7
+ "table:nth-child(2)>tbody>tr:nth-child(#{index - 1})"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::FrontendHelper
4
+ def react_params(form, action, method)
5
+ form.properties.each_with_object({ action: react_params_url(form, action), method: method,
6
+ authenticity_token: form_authenticity_token }) do |property, hash|
7
+ case property[1]
8
+ when :association
9
+ hash.merge!(
10
+ property[0] => {
11
+ collection: build_collection_for_association(form, property[0])
12
+ }
13
+ )
14
+ end
15
+ end.merge model: form.model.attributes
16
+ end
17
+
18
+ def react_params_url(form, action)
19
+ case action
20
+ when :create
21
+ Tramway::Engine.routes.url_helpers.records_path(model: form.model.class)
22
+ else
23
+ Tramway::Engine.routes.url_helpers.record_path(form.model.id, model: form.model.class)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Inputs::AssociationsHelper
4
+ def build_collection_for_association(form_object, property)
5
+ full_class_name_association = form_object.class.full_class_name_association(property)
6
+ check_valid_association full_class_name_association
7
+ full_class_name_association.send("#{current_user.role}_scope", current_user.id).map do |obj|
8
+ decorator_class(full_class_name_association).decorate obj
9
+ end.sort_by do |association|
10
+ association.name || "#{association.class.name} ##{association.id}"
11
+ end
12
+ end
13
+
14
+ def build_value_for_association(form_object, property, value)
15
+ form_object.send(property) || form_object.model.send("#{property}_id") || value
16
+ end
17
+
18
+ def check_valid_association(full_class_name_association)
19
+ unless full_class_name_association
20
+ Tramway::Error.raise_error(
21
+ :tramway, :inputs_helpers, :association_params, :defined_with_property_method, property: property
22
+ )
23
+ end
24
+ return unless full_class_name_association.is_a? Array
25
+
26
+ Tramway::Error.raise_error(
27
+ :tramway, :inputs_helpers, :association_params, :used_polymorphic_association, property: property
28
+ )
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Inputs::PolymorphicAssociationsHelper
4
+ def build_collection_for_polymorphic_association(form_object, property)
5
+ object_names = full_class_names(form_object, property).map do |class_name|
6
+ class_name.send("#{current_user.role}_scope", current_user.id).map do |obj|
7
+ decorator_class(class_name).decorate obj
8
+ end
9
+ end.flatten
10
+ object_names.sort_by { |obj| obj.name.to_s }
11
+ end
12
+
13
+ def build_value_for_polymorphic_association(form_object, property, value)
14
+ if form_object.send(property).present?
15
+ "#{form_object.send(property).class.to_s.underscore}_#{form_object.send(property).id}"
16
+ elsif value[:type].present? && value[:id].present?
17
+ "#{value[:type]&.underscore}_#{value[:id]}"
18
+ end
19
+ end
20
+
21
+ def full_class_names(form_object, property)
22
+ form_object.model.class.send("#{property}_type").values.map(&:constantize)
23
+ end
24
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::InputsHelper
4
+ include Tramway::Inputs::AssociationsHelper
5
+ include Tramway::Inputs::PolymorphicAssociationsHelper
6
+
7
+ def association_params(form_object:, property:, value:, object:, options: {})
8
+ full_class_name_association = form_object.class.full_class_name_association(property)
9
+
10
+ value = current_user&.id if full_class_name_association.to_s == 'Tramway::User'
11
+
12
+ build_input_attributes(object: object, property: property, options: options,
13
+ value: build_value_for_association(form_object, property, value),
14
+ collection: build_collection_for_association(form_object, property),
15
+ include_blank: true,
16
+ selected: form_object.model.send("#{property}_id") || value)
17
+ end
18
+
19
+ def polymorphic_association_params(object:, form_object:, property:, value:, options: {})
20
+ build_input_attributes object: object, property: property,
21
+ selected: build_value_for_polymorphic_association(form_object, property, value),
22
+ value: build_value_for_polymorphic_association(form_object, property, value),
23
+ collection: build_collection_for_polymorphic_association(form_object, property),
24
+ options: options.merge(
25
+ as: :select,
26
+ include_blank: true,
27
+ label_method: ->(obj) { "#{obj.class.model_name.human} | #{obj.name}" },
28
+ value_method: lambda { |obj|
29
+ "#{obj.class.to_s.underscore.sub(/_decorator$/, '')}_#{obj.id}"
30
+ }
31
+ )
32
+ end
33
+
34
+ def build_input_attributes(**options)
35
+ {
36
+ label: false,
37
+ input_html: {
38
+ name: "#{options[:object]}[#{options[:property]}]",
39
+ id: "#{options[:object]}_#{options[:property]}",
40
+ value: options[:value]
41
+ },
42
+ selected: options[:selected],
43
+ collection: options[:collection]
44
+ }.merge options[:options]
45
+ end
46
+
47
+ def value_from_params(model_class:, property:, type:)
48
+ case type
49
+ when :polymorphic_association, 'polymorphic_association'
50
+ {
51
+ id: params.dig(model_class.to_s.underscore, property.to_s),
52
+ type: params.dig(model_class.to_s.underscore, "#{property}_type")
53
+ }
54
+ else
55
+ params.dig(model_class.to_s.underscore, property.to_s)
56
+ end
57
+ end
58
+
59
+ def default_params(property:, object:, form_object:, value:, options: {})
60
+ {
61
+ label: false,
62
+ input_html: {
63
+ name: "#{object}[#{property}]",
64
+ id: "#{object}_#{property}",
65
+ value: (form_object.send(property) || form_object.model.send(property) || value)
66
+ },
67
+ selected: (form_object.model.send(property) || value)
68
+ }.merge options
69
+ end
70
+
71
+ def simple_params(**options)
72
+ builded_options = { as: options[:type], label: false,
73
+ input_html: {
74
+ name: "#{options[:object]}[#{options[:property]}]",
75
+ id: "#{options[:object]}_#{options[:property]}",
76
+ value: build_simple_value(
77
+ *options.values_at(:form_object, :property, :value),
78
+ options.dig(:options, :input_html, :value)
79
+ )
80
+ } }
81
+ merge_with_user_options builded_options, options
82
+ end
83
+
84
+ def build_simple_value(form_object, property, value, input_html_value)
85
+ value_to_add = input_html_value || value
86
+ value_to_add || form_object.send(property)
87
+ end
88
+
89
+ def merge_with_user_options(builded_options, options)
90
+ if options[:options].present?
91
+ options[:options][:input_html]&.delete(:value)
92
+ builded_options.merge!(options) || {}
93
+ end
94
+ builded_options
95
+ end
96
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::ModelHelper
4
+ def tramway_model?(model_class)
5
+ model_class.ancestors.include? Tramway::ApplicationRecord
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::NavbarHelper
4
+ def customized_admin_navbar_given?
5
+ customized_admin_navbar.present?
6
+ end
7
+
8
+ def customized_admin_navbar
9
+ ::Tramway.customized_admin_navbar
10
+ end
11
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::RecordsHelper
4
+ # FIXME: replace to module
5
+ def current_model_record_path(*args, **options)
6
+ record_path args, options.merge(model: params[:model])
7
+ end
8
+
9
+ def edit_current_model_record_path(*args, **options)
10
+ edit_record_path args, options.merge(model: params[:model])
11
+ end
12
+
13
+ def new_current_model_record_path(*args, **options)
14
+ new_record_path args, options.merge(model: params[:model])
15
+ end
16
+
17
+ def current_model_records_path(*args, **options)
18
+ records_path args, options.merge(model: params[:model])
19
+ end
20
+
21
+ def public_path(record)
22
+ record.public_path || try("#{record.class.name.underscore.gsub('/', '_')}_path", record)
23
+ end
24
+
25
+ def model_class
26
+ params[:model].constantize
27
+ end
28
+
29
+ def decorator_class(model_name = nil)
30
+ "#{model_name || model_class}Decorator".constantize
31
+ end
32
+
33
+ def to_path(constant)
34
+ constant.name.underscore.gsub '/', '_'
35
+ end
36
+
37
+ def search_tab_title(count)
38
+ "#{t('helpers.scope.found')} / #{count}"
39
+ end
40
+
41
+ def searchable_model?(model_class)
42
+ model_class.methods.include? :full_text_search
43
+ end
44
+
45
+ def build_options_for_select(name, collection)
46
+ selected_value = params[:list_filters].present? ? params[:list_filters][name] : nil
47
+ options_for_select(collection, selected_value)
48
+ end
49
+
50
+ def index_path_of_model(model_class, tab, filter)
51
+ if tab
52
+ records_path model: model_class, filter: filter, scope: tab
53
+ else
54
+ records_path model: model_class, filter: filter
55
+ end
56
+ end
57
+
58
+ def collection_human_name(model_name:, collection_name:)
59
+ if t("default.collections.#{collection_name}").include?('<span')
60
+ t("collections.#{model_name}.#{collection_name}").pluralize(:ru)
61
+ else
62
+ t("default.collections.#{collection_name}")
63
+ end
64
+ end
65
+
66
+ def tab_title(model_class, tab, count, _state_method = :state)
67
+ model = model_class.name.underscore
68
+ name = collection_human_name model_name: model, collection_name: tab
69
+ "#{name} / #{count}"
70
+ end
71
+
72
+ def active_tab(tab, index)
73
+ return :active if params[:scope].nil? && index.zero?
74
+ return :active if params[:search].nil? && params[:scope].to_s == tab.to_s
75
+ end
76
+
77
+ def new_associated_record_path(object:, association:, as:)
78
+ unless association.options[:class_name].present?
79
+ raise "You should set `class_name` for #{association.name} association"
80
+ end
81
+
82
+ new_record_path(**new_record_path_options(object, association, as))
83
+ end
84
+
85
+ def new_record_path_options(object, association, as)
86
+ hash = {
87
+ model: association.class_name,
88
+ redirect: current_model_record_path(object.model)
89
+ }
90
+
91
+ if association.options[:as].present? # polymorphic? conditiion
92
+ hash.merge! options_for_polymorphic_link object, association
93
+ else
94
+ hash.merge! options_for_isomorphic_link object, association, as
95
+ end
96
+
97
+ hash
98
+ end
99
+
100
+ def options_for_polymorphic_link(object, association)
101
+ {
102
+ association.options[:class_name].underscore => {
103
+ association.options[:as] => object.id,
104
+ association.type => object.class.model_name
105
+ }
106
+ }
107
+ end
108
+
109
+ def options_for_isomorphic_link(object, association, as)
110
+ {
111
+ association.options[:class_name].underscore => {
112
+ as || object.model.class.name.underscore.gsub('/', '_') => object.id
113
+ }
114
+ }
115
+ end
116
+
117
+ def there_any_filters?(model_class)
118
+ decorator_class(model_class).list_filters&.any?
119
+ end
120
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::RussianCasesHelper
4
+ def case_word(model_name, case_name)
5
+ word_case = I18n.t("cases.#{model_name.name.underscore}.#{case_name}")
6
+ return word_case if word_case.present?
7
+
8
+ raise "There is not #{case_name} implementation for \"#{model_name}\""
9
+ end
10
+
11
+ def genitive(word)
12
+ case_word word, :genitive
13
+ end
14
+
15
+ def instrumental(word)
16
+ case_word word, :instrumental
17
+ end
18
+
19
+ def dative(word)
20
+ case_word word, :dative
21
+ end
22
+
23
+ def russian_plural(word)
24
+ case_word word, :plural
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::SingletonHelper
4
+ # FIXME: replace to module
5
+ def current_model_singleton_path(*args, **options)
6
+ singleton_path args, options.merge(model: params[:model])
7
+ end
8
+
9
+ def edit_current_model_singleton_path(*args, **options)
10
+ edit_singleton_path args, options.merge(model: params[:model])
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::StateMachineButtonsHelper
4
+ def state_events_buttons(object, **options)
5
+ options.each do |(key, value)|
6
+ define_singleton_method(key) { value }
7
+ end
8
+ content_tag(:div, class: 'btn-group-vertical') do
9
+ transitions(object, state_method, options[:without]).each do |event|
10
+ button(
11
+ object,
12
+ event: event[:event],
13
+ model_name: object.class.model_name.name.underscore,
14
+ form_options: button_options,
15
+ **options
16
+ )
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def excepted_actions(without)
24
+ return unless without
25
+
26
+ without.is_a?(Array) ? without.map(&:to_sym) : [without.to_sym]
27
+ end
28
+
29
+ def transitions(object, state_method, without)
30
+ object.model.aasm(state_method).permitted_transitions.reject do |t|
31
+ excepted_actions(without).present? && excepted_actions(without).include?(t[:event])
32
+ end
33
+ end
34
+
35
+ def button(object, **options)
36
+ event = options[:event]
37
+
38
+ concat(
39
+ patch_button(patch_button_options(object, event, options)) do
40
+ model = object.model
41
+ class_name = model.class.name.underscore
42
+ actual_event = model.aasm(options[:state_method]).events.select { |ev| ev.name == event }.first.name
43
+ I18n.t("state_machines.#{class_name}.#{options[:state_method]}.events.#{actual_event}")
44
+ end
45
+ )
46
+ end
47
+
48
+ def patch_button_options(object, event, options)
49
+ attributes = { aasm_event: event }
50
+ css_class = "btn btn-sm btn-xs btn-#{object.public_send("#{options[:state_method]}_button_color", event)}"
51
+
52
+ {
53
+ record: object.model,
54
+ attributes: attributes,
55
+ model_name: options[:model_name],
56
+ button_options: { class: css_class },
57
+ form_options: options[:form_options],
58
+ url: Tramway::Engine.routes.url_helpers.record_path(object.id, options[:parameters])
59
+ }
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::TitleHelper
4
+ def title(page_title = default_title)
5
+ @application ||= Tramway.application&.model_class&.first || Tramway.application
6
+ if @application.present?
7
+ title_text = "#{page_title} | #{@application.try(:title) || @application.public_name}"
8
+ content_for(:title) { title_text }
9
+ else
10
+ Tramway::Error.raise_error(:tramway, :title_helper, :title, :you_should_set_tramway_core_application)
11
+ end
12
+ end
13
+
14
+ def default_title
15
+ t('.title')
16
+ end
17
+
18
+ def page_title(action, model_name)
19
+ if I18n.locale == :ru
20
+ "#{t("helpers.actions.#{action}")} #{genitive(model_name)}"
21
+ else
22
+ "#{t("helpers.actions.#{action}")} #{model_name.model_name.human.downcase}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DatePickerInput < SimpleForm::Inputs::StringInput
4
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MultipleFileInput < SimpleForm::Inputs::FileInput
4
+ def input(wrapper_options)
5
+ super(wrapper_options.merge! multiple: true)
6
+ end
7
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'carrierwave/orm/activerecord' if defined?(CarrierWave::Mount)
4
+
5
+ class Tramway::ApplicationRecord < ActiveRecord::Base
6
+ self.abstract_class = true
7
+ audited
8
+ extend ::Enumerize
9
+ include AASM
10
+ acts_as_paranoid
11
+
12
+ scope :created_by_user, lambda { |user_id|
13
+ joins(:audits).where('audits.action = \'create\' AND audits.user_id = ?', user_id)
14
+ }
15
+ scope :admin_scope, ->(_arg) { all }
16
+
17
+ include ::PgSearch::Model
18
+
19
+ def creator
20
+ creation_event = audits.where(action: :create).first
21
+ creation_event.user if creation_event.user_id.present?
22
+ end
23
+
24
+ # FIXME: detect inhertited locales
25
+ class << self
26
+ def human_attribute_name(attribute_name, *_args)
27
+ excepted_attributes = %w[created_at updated_at]
28
+ if attribute_name.to_s.in? excepted_attributes
29
+ I18n.t "activerecord.attributes.tramway/application_record.#{attribute_name}"
30
+ else
31
+ super attribute_name
32
+ end
33
+ end
34
+
35
+ def search_by(*attributes, **associations)
36
+ pg_search_scope :full_text_search,
37
+ against: attributes,
38
+ associated_against: associations,
39
+ using: %i[tsearch trigram]
40
+ end
41
+
42
+ def uploader(attribute_name, uploader_name, **options)
43
+ mount_uploader attribute_name, "#{uploader_name.to_s.camelize}Uploader".constantize
44
+ @versions = options[:versions] if uploader_name == :photo
45
+ @extensions = options[:extensions]
46
+ end
47
+
48
+ def photo_versions
49
+ @versions
50
+ end
51
+
52
+ def file_extensions
53
+ @extensions
54
+ end
55
+
56
+ def state_machines_names
57
+ AASM::StateMachineStore.fetch(self).machine_names
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Tramway::User < ::Tramway::ApplicationRecord
4
+ has_secure_password
5
+
6
+ if defined? Tramway::Conference
7
+ has_many :social_networks, as: :record,
8
+ class_name: 'Tramway::Profiles::SocialNetwork'
9
+ end
10
+
11
+ scope :admins, -> { where role: :admin }
12
+ scope :simple_users, -> { where role: :user }
13
+
14
+ enumerize :role, in: %i[user admin], default: :admin
15
+
16
+ def admin?
17
+ role.admin?
18
+ end
19
+
20
+ def full_name
21
+ "#{first_name} #{last_name}"
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'carrierwave'
4
+
5
+ class ApplicationUploader < CarrierWave::Uploader::Base
6
+ storage :file
7
+
8
+ def store_dir
9
+ "system/uploads/#{model.class.model_name.to_s.underscore}/#{mounted_as}/#{id_directory}"
10
+ end
11
+
12
+ private
13
+
14
+ def id_directory
15
+ if model.respond_to?(:uuid)
16
+ model.reload unless model.uuid.present?
17
+ model.uuid
18
+ else
19
+ model.id
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FileUploader < ApplicationUploader
4
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class IcoUploader < ApplicationUploader
4
+ def extension_allowlist
5
+ model.class.file_extensions || %w[ico]
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImageDefaults
4
+ include CarrierWave::MiniMagick
5
+
6
+ def default_url
7
+ "/images/fallback/#{model.class.model_name.to_s.underscore}/" <<
8
+ [mounted_as, version_name].compact.join('_') << '.gif'
9
+ end
10
+
11
+ def extension_allowlist
12
+ model.class.file_extensions || %w[jpg jpeg gif png]
13
+ end
14
+ end