tramway-core 1.17.1 → 1.17.2.3

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +83 -2
  3. data/app/assets/javascripts/tramway/core/application.js +1 -1
  4. data/app/controllers/tramway/core/application_controller.rb +8 -12
  5. data/app/decorators/tramway/core/application_decorator.rb +16 -28
  6. data/app/decorators/tramway/core/associations/class_helper.rb +13 -13
  7. data/app/decorators/tramway/core/associations/object_helper.rb +31 -3
  8. data/app/decorators/tramway/core/attributes/view_helper.rb +26 -0
  9. data/app/decorators/tramway/core/concerns/attributes_decorator_helper.rb +47 -26
  10. data/app/decorators/tramway/core/delegating/class_helper.rb +9 -0
  11. data/app/forms/tramway/core/application_form.rb +83 -171
  12. data/app/forms/tramway/core/application_forms/association_object_helpers.rb +31 -0
  13. data/app/forms/tramway/core/application_forms/constant_class_actions.rb +7 -0
  14. data/app/forms/tramway/core/application_forms/constant_object_actions.rb +13 -0
  15. data/app/forms/tramway/core/application_forms/properties_object_helper.rb +23 -0
  16. data/app/forms/tramway/core/extendable_form.rb +3 -70
  17. data/app/forms/tramway/core/extendable_forms_helpers/class_builder.rb +34 -0
  18. data/app/forms/tramway/core/extendable_forms_helpers/ignored_properties_helper.rb +11 -0
  19. data/app/forms/tramway/core/extendable_forms_helpers/more_properties_helper.rb +27 -0
  20. data/app/forms/tramway/core/extendable_forms_helpers/properties_helper.rb +16 -0
  21. data/app/forms/tramway/core/extendable_forms_helpers/submit/class_helpers.rb +18 -0
  22. data/app/forms/tramway/core/extendable_forms_helpers/submit/object_helpers.rb +7 -0
  23. data/app/forms/tramway/core/extendable_forms_helpers/validators.rb +40 -0
  24. data/app/helpers/tramway/core/copy_to_clipboard_helper.rb +6 -10
  25. data/app/helpers/tramway/core/title_helper.rb +17 -22
  26. data/app/models/tramway/core/application_record.rb +38 -40
  27. data/app/uploaders/image_defaults.rb +2 -1
  28. data/app/uploaders/photo_uploader.rb +8 -8
  29. data/config/initializers/plurals.rb +2 -2
  30. data/lib/tramway/collection.rb +4 -6
  31. data/lib/tramway/collections.rb +4 -0
  32. data/lib/tramway/collections/helper.rb +15 -14
  33. data/lib/tramway/core.rb +22 -22
  34. data/lib/tramway/core/engine.rb +4 -8
  35. data/lib/tramway/core/generators.rb +4 -0
  36. data/lib/tramway/core/generators/install_generator.rb +17 -17
  37. data/lib/tramway/core/version.rb +1 -1
  38. data/lib/tramway/error.rb +12 -1
  39. data/lib/yaml/errors.yml +35 -0
  40. metadata +20 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4eb727078bfed96a01a6730fbc8e529cc8ad32ff1214e6032a38ca50ba482eea
4
- data.tar.gz: df33625fbd52d7072883ead0ba561c9d0b4c0e3504dc8413d945a2c7b9ebe2da
3
+ metadata.gz: e25693262017e7eb7b500db8ed733e26ed5c841f4e5eca94b036f422114c8f8e
4
+ data.tar.gz: 9cc1274352800f545610e01d2c02485c3ef2837bd2d413041818462682496e04
5
5
  SHA512:
6
- metadata.gz: b49d4439ca1f4e2d5320edb0e65c9eba652a5b5f378b0ac9d42c9e5442582d07ceae9968733a28f915199ea86286b8b5560903b2176d0d39e747ad8249bb87cc
7
- data.tar.gz: 75a7a039ffaa3de0c247a893daec0475d3ed9d16a39b7157df88c4ef46754b283515f7df017fff0a608f6bcbd9c77a5225a5070a3b60375500648fc4c3409929
6
+ metadata.gz: 796e723d4c64a741d72b2d25a769e1344d895805dda3ba55da9c6f4161252a416ade82f2439217599a60605c6d29e6292be4b165a56f0c26343eeeba6ca9ce85
7
+ data.tar.gz: 9991a6ecf790a5ce4ed3b0064d3cc2392336359bcf095416b91a9ee6d152e19d1487826ab9a85d7c1df4913a426375128955c602b1501ff5b273319ca49db211
data/README.md CHANGED
@@ -33,7 +33,53 @@ Tramway::Core.initialize_application model_class: ::Tramway::Conference::Unity #
33
33
  Rails.application.config.assets.precompile += %w( *.jpg *.png *.js )
34
34
  ```
35
35
  # Usage
36
+
36
37
  ## Decorators
38
+ ### Associations
39
+
40
+ Your can decorate association models. Supporting all types of association
41
+
42
+ *app/decorators/your_model_decorator.rb*
43
+ ```ruby
44
+ class YourModelDecorator < Tramway::Core::ApplicationDecorator
45
+ decorate_association :some_model
46
+ decorate_association :another_model, decorator: SpecificDecoratorForThisCase
47
+ decorate_association :another_one_model, as: :repeat_here_as_parameter_from_model
48
+ decorate_association :something_else_model, state_machines: [ :here_array_of_state_machines_you_want_to_see_in_YourModel_show_page ] # support from tramway-admin gem
49
+ end
50
+ ```
51
+
52
+ You can decorate a lot of models in one line
53
+
54
+ *app/decorators/your_model_decorator.rb*
55
+ ```ruby
56
+ class YourModelDecorator < Tramway::Core::ApplicationDecorator
57
+ decorate_associations :some_model, :another_model, :another_one_model, :something_else_model
58
+ end
59
+ ```
60
+
61
+ Also, you can configurate what associations you want to see in YourModel page in admin panel *support only for [tramway-admin](https://rubygems.org/gems/tramway-admin) gem*
62
+
63
+ *app/decorators/your_model_decorator.rb*
64
+ ```ruby
65
+ class YourModelDecorator < Tramway::Core::ApplicationDecorator
66
+ class << self
67
+ def show_associations
68
+ [ :some_model, :another_model, :another_one_model ]
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### Delegating attributes
75
+
76
+ *app/decorators/your_model_decorator.rb*
77
+ ```ruby
78
+ class YourModelDecorator < Tramway::Core::ApplicationDecorator
79
+ delegate_attributes :title, :something_else, :another_atttribute
80
+ end
81
+ ```
82
+
37
83
  ### Helper methods
38
84
 
39
85
  #### date_view
@@ -106,6 +152,39 @@ Something like this:
106
152
  copy_to_clipboard "some_id" # some_id is HTML id of element. Content of this element will be copied to the clipboard after pressing the button
107
153
  ```
108
154
 
155
+ ## How to create model that will be an Application Model for the Tramway
156
+
157
+ #### 1. Generate model that you to use. We create Organization, for example
158
+
159
+ ```shell
160
+ rails g model organization name:text public_name:text tagline:text address:text phone:text coordinates:point, state: text # remember! State field is required, if you use tramway-admin
161
+ rails db:migrate
162
+ ```
163
+
164
+ #### 2. Add model_class to Initializer
165
+
166
+ ```ruby
167
+ Tramway::Core.initialize_application model_class: Organization
168
+ ```
169
+
170
+ #### 3. Create 1 instance of Organization model
171
+
172
+ ```ruby
173
+ rails c
174
+ Organization.create! public_name: 'Tramway', name: :organization, tagline: 'Tramway is not buggy, LOL!'
175
+ ```
176
+
177
+ #### 4. Add model to singleton to the `tramway-admin` admin panel to be able to change its data
178
+
179
+ ```ruby
180
+ Tramway::Admin.set_singleton_models Organization, project: :organization # now you should use organization.name here
181
+ ```
182
+
183
+ #### 5. Then continue configuration of your model in admin panel with tramway-admin gem [instruction, starting from point 8](https://github.com/ulmic/tramway-dev/tree/develop/tramway-admin#8-configurate-navbar)
184
+
185
+ #### 6. Now you are able to change your application main info in admin panel
186
+
187
+ ## In Russian
109
188
 
110
189
  # Базовые классы
111
190
 
@@ -124,8 +203,10 @@ copy_to_clipboard "some_id" # some_id is HTML id of element. Content of this ele
124
203
  * models - часто используемые в моделях слова
125
204
  * state_machines - локализация состояний
126
205
 
127
- ## Contributing
128
- Contribution directions go here.
206
+ ## Contributors
207
+
208
+ * [Pavel Kalashnikov](https://github.com/kalashnikovisme)
209
+ * [moshinaan](https://github.com/moshinaan)
129
210
 
130
211
  ## License
131
212
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -28,7 +28,7 @@ $(document).ready(function() {
28
28
  console.log('You should set `window.current_locale` before all Javascript code');
29
29
  }
30
30
 
31
- if ($('.date_picker').length == 0) {
31
+ if ($('.date_picker').length != 0) {
32
32
  $('.date_picker').datepicker({
33
33
  format: window.current_locale.date_format,
34
34
  language: window.current_locale.locale
@@ -1,18 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Tramway
4
- module Core
5
- class ApplicationController < ActionController::Base
6
- before_action :application
7
- before_action :load_extensions
3
+ class Tramway::Core::ApplicationController < ActionController::Base
4
+ before_action :application
5
+ before_action :load_extensions
8
6
 
9
- def application
10
- @application = ::Tramway::Core.application_object
11
- end
7
+ def application
8
+ @application = ::Tramway::Core.application_object
9
+ end
12
10
 
13
- def load_extensions
14
- ::Tramway::Extensions.load if defined? ::Tramway::Extensions
15
- end
16
- end
11
+ def load_extensions
12
+ ::Tramway::Extensions.load if defined? ::Tramway::Extensions
17
13
  end
18
14
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'tramway/error'
4
+ require 'tramway/helpers/class_name_helpers'
4
5
 
5
6
  class Tramway::Core::ApplicationDecorator
6
7
  include ActionView::Helpers
@@ -8,6 +9,8 @@ class Tramway::Core::ApplicationDecorator
8
9
  include ::FontAwesome5::Rails::IconHelper
9
10
  include ::Tramway::Core::CopyToClipboardHelper
10
11
  include ::Tramway::Core::Associations::ObjectHelper
12
+ include ::Tramway::Core::Attributes::ViewHelper
13
+ include ::Tramway::ClassNameHelpers
11
14
 
12
15
  def initialize(object)
13
16
  @object = object
@@ -18,12 +21,18 @@ class Tramway::Core::ApplicationDecorator
18
21
  end
19
22
 
20
23
  def title
21
- error = Tramway::Error.new(plugin: :core, method: :title, message: "Please, implement `title` method in a #{self.class} or #{object.class}")
22
- raise error.message
24
+ Tramway::Error.raise_error(
25
+ :tramway, :core, :application_decorator, :title, :please_implement_title,
26
+ class_name: self.class, object_class: object.class
27
+ )
23
28
  end
24
29
 
30
+ delegate :id, to: :object
31
+ delegate :human_state_name, to: :object
32
+
25
33
  class << self
26
34
  include ::Tramway::Core::Associations::ClassHelper
35
+ include ::Tramway::Core::Delegating::ClassHelper
27
36
 
28
37
  def collections
29
38
  [:all]
@@ -61,19 +70,11 @@ class Tramway::Core::ApplicationDecorator
61
70
  end
62
71
  end
63
72
 
64
- delegate :id, to: :object
65
- delegate :human_state_name, to: :object
66
-
67
73
  def link
68
74
  if object.try :file
69
75
  object.file.url
70
76
  else
71
- error = Tramway::Error.new(
72
- plugin: :core,
73
- method: :link,
74
- message: "Method `link` uses `file` attribute of the decorated object. If decorated object doesn't contain `file`, you shouldn't use `link` method."
75
- )
76
- raise error.message
77
+ Tramway::Error.raise_error :tramway, :core, :application_decorator, :link, :method_link_uses_file_attribute
77
78
  end
78
79
  end
79
80
 
@@ -96,25 +97,12 @@ class Tramway::Core::ApplicationDecorator
96
97
  def attributes
97
98
  object.attributes.reduce({}) do |hash, attribute|
98
99
  if attribute[0].in? RESERVED_WORDS
99
- error = Tramway::Error.new(
100
- plugin: :core,
101
- method: :attributes,
102
- message: "Method `#{attribute[0]}` is reserved word. Please, create or delegate method in #{self.class.name} with another name."
100
+ Tramway::Error.raise_error(
101
+ :tramway, :core, :application_decorator, :attributes, :method_is_reserved_word,
102
+ attribute_name: attribute[0], class_name: self.class.name
103
103
  )
104
- raise error.message
105
- end
106
- value = try(attribute[0]) ? send(attribute[0]) : object.send(attribute[0])
107
- if attribute[0].to_s.in? object.class.state_machines.keys.map(&:to_s)
108
- hash.merge! attribute[0] => state_machine_view(object, attribute[0])
109
- elsif value.class.in? [ActiveSupport::TimeWithZone, DateTime, Time]
110
- hash.merge! attribute[0] => datetime_view(attribute[1])
111
- elsif value.class.superclass == ApplicationUploader
112
- hash.merge! attribute[0] => image_view(object.send(attribute[0]))
113
- elsif value.is_a? Enumerize::Value
114
- hash.merge! attribute[0] => enumerize_view(value)
115
- else
116
- hash.merge! attribute[0] => value
117
104
  end
105
+ hash.merge! attribute[0] => build_viewable_value(object, attribute)
118
106
  end
119
107
  end
120
108
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tramway::Core::Associations::ClassHelper
2
4
  def decorate_association(association_name, decorator: nil, as: nil, state_machines: [])
3
5
  @@decorated_associations ||= []
@@ -14,27 +16,25 @@ module Tramway::Core::Associations::ClassHelper
14
16
  end
15
17
 
16
18
  define_method "add_#{association_name}_form" do
17
- "Admin::#{object.class.to_s.pluralize}::Add#{association_name.to_s.camelize.singularize}Form".constantize.new object
19
+ add_association_form_class_name(object, association_name).new object
18
20
  end
19
21
  end
20
22
 
23
+ def decorate_associations(*association_names)
24
+ association_names.each do |association_name|
25
+ decorate_association association_name
26
+ end
27
+ end
21
28
 
22
29
  def define_main_association_method(association_name, decorator)
23
30
  define_method association_name do
24
31
  association = object.class.reflect_on_association(association_name)
25
- if association.nil?
26
- error = Tramway::Error.new(plugin: :core, method: :decorate_association, message: "Model #{object.class} does not have association named `#{association_name}`")
27
- raise error.message
28
- end
29
- decorator_class_name = decorator || "#{class_name(association).to_s.singularize}Decorator".constantize
30
- if association.class.in? [ ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasAndBelongsToManyReflection ]
31
- return object.send(association_name).active.map do |association_object|
32
- decorator_class_name.decorate association_object
33
- end
34
- end
35
- if association.class == ActiveRecord::Reflection::BelongsToReflection
36
- return decorator_class_name.decorate object.send association_name
32
+ check_association object, association_name, association
33
+ decorator_class_name = decorator || decorator_class_name(class_name(association))
34
+ if association_type(association).in? %i[has_many has_and_belongs_to_many]
35
+ return associations_collection(object, association_name, decorator_class_name)
37
36
  end
37
+ return decorator_class_name.decorate object.send association_name if association_type(association) == :belongs_to
38
38
  end
39
39
  end
40
40
  end
@@ -1,13 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tramway::Core::Associations::ObjectHelper
2
4
  def class_name(association)
3
5
  if association.polymorphic?
4
- object.send(association_name).class
6
+ object.send(association.name).class
5
7
  else
6
8
  unless association.options[:class_name]
7
- error = Tramway::Error.new(plugin: :core, method: :decorate_association, message: "Please, specify `#{association_name}` association class_name in #{object.class} model. For example: `has_many :#{association_name}, class_name: '#{association_name.to_s.singularize.camelize}'`")
8
- raise error.message
9
+ Tramway::Error.raise_error(
10
+ :tramway, :core, :associations, :object_helper, :please_specify_association_name,
11
+ association_name: association.name, object_class: object.class,
12
+ association_class_name: association.name.to_s.singularize.camelize
13
+ )
9
14
  end
10
15
  association.options[:class_name]
11
16
  end
12
17
  end
18
+
19
+ def check_association(object, association_name, association)
20
+ return unless association.nil?
21
+
22
+ Tramway::Error.raise_error(
23
+ :tramway, :core, :associations, :class_helper, :model_does_not_have_association,
24
+ object_class: object.class, association_name: association_name
25
+ )
26
+ end
27
+
28
+ def association_type(association)
29
+ association.class.to_s.split('::').last.sub(/Reflection$/, '').underscore.to_sym
30
+ end
31
+
32
+ def associations_collection(object, association_name, decorator_class_name)
33
+ object.send(association_name).active.map do |association_object|
34
+ decorator_class_name.decorate association_object
35
+ end
36
+ end
37
+
38
+ def add_association_form_class_name(object, association_name)
39
+ "Admin::#{object.class.to_s.pluralize}::Add#{association_name.to_s.camelize.singularize}Form".constantize
40
+ end
13
41
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::Attributes::ViewHelper
4
+ def build_viewable_value(object, attribute)
5
+ value = try(attribute[0]) ? send(attribute[0]) : object.send(attribute[0])
6
+ return state_machine_view(object, attribute[0]) if state_machine? object, attribute[0]
7
+
8
+ view_by_value object, value, attribute
9
+ end
10
+
11
+ def state_machine?(object, attribute_name)
12
+ attribute_name.to_s.in? object.class.state_machines.keys.map(&:to_s)
13
+ end
14
+
15
+ def view_by_value(object, value, attribute)
16
+ if value.class.in? [ActiveSupport::TimeWithZone, DateTime, Time]
17
+ datetime_view(attribute[1])
18
+ elsif value.class.superclass == ApplicationUploader
19
+ image_view(object.send(attribute[0]))
20
+ elsif value.is_a? Enumerize::Value
21
+ enumerize_view(value)
22
+ else
23
+ value
24
+ end
25
+ end
26
+ end
@@ -13,37 +13,58 @@ module Tramway::Core::Concerns::AttributesDecoratorHelper
13
13
  object.send "human_#{attribute_name}_name"
14
14
  end
15
15
 
16
+ BASE64_REGEXP = %r{^(?:[a-zA-Z0-9+/]{4})*(?:|(?:[a-zA-Z0-9+/]{3}=)|
17
+ (?:[a-zA-Z0-9+/]{2}==)|(?:[a-zA-Z0-9+/]{1}===))$}x.freeze
18
+
16
19
  def image_view(original, thumb: nil, filename: nil)
17
- if original.present?
18
- thumb ||= original.is_a?(CarrierWave::Uploader::Base) ? original.small : nil
19
- filename ||= original.is_a?(CarrierWave::Uploader::Base) ? original.path&.split('/')&.last : nil
20
- src_thumb = if thumb&.is_a?(CarrierWave::Uploader::Base)
21
- thumb.url
22
- elsif thumb&.match(%r{^(?:[a-zA-Z0-9+/]{4})*(?:|(?:[a-zA-Z0-9+/]{3}=)|(?:[a-zA-Z0-9+/]{2}==)|(?:[a-zA-Z0-9+/]{1}===))$})
23
- "data:image/jpeg;base64,#{thumb}"
24
- else
25
- thumb
26
- end
27
- src_original = if original.is_a?(CarrierWave::Uploader::Base)
28
- original.url
29
- elsif original.match(%r{^(?:[a-zA-Z0-9+/]{4})*(?:|(?:[a-zA-Z0-9+/]{3}=)|(?:[a-zA-Z0-9+/]{2}==)|(?:[a-zA-Z0-9+/]{1}===))$})
30
- "data:image/jpeg;base64,#{original}"
31
- else
32
- original
33
- end
34
- content_tag(:div) do
35
- begin
36
- concat image_tag src_thumb || src_original
37
- rescue NoMethodError => e
38
- error = Tramway::Error.new plugin: :core, method: :image_view, message: "You should mount PhotoUploader to your model. Just add `mount_uploader \#{attribute_name}, PhotoUploader` to your model. #{e.message}"
39
- raise error.message
40
- end
41
- concat link_to(fa_icon(:download), src_original, class: 'btn btn-success', download: filename) if filename
42
- end
20
+ return unless original.present?
21
+
22
+ filename ||= build_filename(original)
23
+ content_tag(:div) do
24
+ build_div_content src_original(original), src_thumb(original, thumb), filename || build_filename(original)
43
25
  end
44
26
  end
45
27
 
46
28
  def enumerize_view(value)
47
29
  value.text
48
30
  end
31
+
32
+ private
33
+
34
+ def src_thumb(original, thumb)
35
+ thumb ||= original.is_a?(CarrierWave::Uploader::Base) ? original.small : nil
36
+ if thumb&.is_a?(CarrierWave::Uploader::Base)
37
+ thumb.url
38
+ elsif thumb&.match(BASE64_REGEXP)
39
+ "data:image/jpeg;base64,#{thumb}"
40
+ else
41
+ thumb
42
+ end
43
+ end
44
+
45
+ def src_original(original)
46
+ if original.is_a?(CarrierWave::Uploader::Base)
47
+ original.url
48
+ elsif original.match(BASE64_REGEXP)
49
+ "data:image/jpeg;base64,#{original}"
50
+ else
51
+ original
52
+ end
53
+ end
54
+
55
+ def build_filename(original)
56
+ original.is_a?(CarrierWave::Uploader::Base) ? original.path&.split('/')&.last : nil
57
+ end
58
+
59
+ def build_div_content(original, thumb, filename)
60
+ begin
61
+ concat image_tag src_thumb(original, thumb) || src_original(original)
62
+ rescue NoMethodError => e
63
+ Tramway::Error.raise_error(
64
+ :tramway, :core, :concerns, :attributes_decorator_helper, :you_should_mount_photo_uploader,
65
+ message: e.message, attribute_name: attribute_name
66
+ )
67
+ end
68
+ concat link_to(fa_icon(:download), src_original(original), class: 'btn btn-success', download: filename) if filename
69
+ end
49
70
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway::Core::Delegating::ClassHelper
4
+ def delegate_attributes(*attributes)
5
+ attributes.each do |attr|
6
+ delegate attr, to: :object
7
+ end
8
+ end
9
+ end