cm-admin 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +21 -20
  5. data/README.md +24 -1
  6. data/app/assets/stylesheets/cm_admin/base/auth.scss +0 -12
  7. data/app/assets/stylesheets/cm_admin/base/filters.scss +8 -7
  8. data/app/assets/stylesheets/cm_admin/base/form.scss +7 -72
  9. data/app/assets/stylesheets/cm_admin/base/navbar.scss +1 -3
  10. data/app/assets/stylesheets/cm_admin/base/quicksearch.scss +7 -0
  11. data/app/assets/stylesheets/cm_admin/base/scaffold.scss +38 -3
  12. data/app/assets/stylesheets/cm_admin/base/show.scss +24 -2
  13. data/app/assets/stylesheets/cm_admin/base/table.scss +270 -253
  14. data/app/assets/stylesheets/cm_admin/components/_dropdown-popup.scss +22 -10
  15. data/app/assets/stylesheets/cm_admin/components/_range.scss +13 -2
  16. data/app/assets/stylesheets/cm_admin/components/_status-tag.scss +2 -1
  17. data/app/assets/stylesheets/cm_admin/helpers/_variable.scss +1 -0
  18. data/app/controllers/cm_admin/application_controller.rb +5 -0
  19. data/app/controllers/cm_admin/exports_controller.rb +1 -1
  20. data/app/controllers/cm_admin/static_controller.rb +12 -0
  21. data/app/helpers/cm_admin/custom_helper.rb +4 -0
  22. data/app/javascript/packs/cm_admin/application.js +14 -1
  23. data/app/javascript/packs/cm_admin/filters.js +331 -16
  24. data/app/javascript/packs/cm_admin/quick_search.js +67 -0
  25. data/app/javascript/packs/cm_admin/scaffolds.js +30 -1
  26. data/app/javascript/stylesheets/cm_admin/application.scss +3 -0
  27. data/app/views/cm_admin/main/_associated_table.html.slim +60 -0
  28. data/app/views/cm_admin/main/_cm_pagy_nav.html.slim +6 -6
  29. data/app/views/cm_admin/main/_filters.html.slim +1 -10
  30. data/app/views/cm_admin/main/_nested_fields.html.slim +7 -0
  31. data/app/views/cm_admin/main/_nested_table_form.html.slim +6 -0
  32. data/app/views/cm_admin/main/_table.html.slim +5 -4
  33. data/app/views/cm_admin/main/_tabs.html.slim +5 -0
  34. data/app/views/cm_admin/main/_top_navbar.html.slim +4 -4
  35. data/app/views/cm_admin/main/associated_index.html.slim +6 -0
  36. data/app/views/cm_admin/main/associated_show.html.slim +6 -0
  37. data/app/views/cm_admin/main/index.html.slim +11 -8
  38. data/app/views/cm_admin/main/show.html.slim +12 -13
  39. data/app/views/cm_admin/static/error_401.html.slim +4 -0
  40. data/app/views/layouts/_left_sidebar_nav.html.slim +7 -3
  41. data/app/views/layouts/_quick_links.html.slim +25 -0
  42. data/app/views/layouts/cm_admin.html.slim +24 -1
  43. data/app/views/layouts/static.html.slim +18 -0
  44. data/bin/console +0 -1
  45. data/cm_admin.gemspec +1 -0
  46. data/config/routes.rb +2 -1
  47. data/config/webpack/environment.js +3 -2
  48. data/lib/.DS_Store +0 -0
  49. data/lib/cm_admin/model.rb +57 -167
  50. data/lib/cm_admin/models/action.rb +15 -1
  51. data/lib/cm_admin/models/cm_show_section.rb +20 -0
  52. data/lib/cm_admin/models/column.rb +49 -4
  53. data/lib/cm_admin/models/controller_method.rb +75 -0
  54. data/lib/cm_admin/models/dsl_method.rb +122 -0
  55. data/lib/cm_admin/models/export.rb +16 -5
  56. data/lib/cm_admin/models/field.rb +2 -1
  57. data/lib/cm_admin/models/filter.rb +67 -2
  58. data/lib/cm_admin/models/form_field.rb +21 -0
  59. data/lib/cm_admin/models/tab.rb +13 -0
  60. data/lib/cm_admin/version.rb +1 -1
  61. data/lib/cm_admin/view_helpers/column_field_helper.rb +29 -0
  62. data/lib/cm_admin/view_helpers/field_display_helper.rb +65 -0
  63. data/lib/cm_admin/view_helpers/filter_helper.rb +190 -0
  64. data/lib/cm_admin/view_helpers/form_field_helper.rb +21 -4
  65. data/lib/cm_admin/view_helpers/form_helper.rb +22 -12
  66. data/lib/cm_admin/view_helpers/manage_column_popup_helper.rb +75 -0
  67. data/lib/cm_admin/view_helpers/navigation_helper.rb +22 -6
  68. data/lib/cm_admin/view_helpers/page_info_helper.rb +36 -0
  69. data/lib/cm_admin/view_helpers.rb +3 -1
  70. data/lib/cm_admin.rb +3 -1
  71. data/lib/generators/cm_admin/install_generator.rb +20 -8
  72. data/package.json +6 -1
  73. data/tmp/cache/webpacker/last-compilation-digest-development +1 -0
  74. data/yarn.lock +35 -5
  75. metadata +41 -7
  76. data/app/controllers/cm_admin/main_controller.rb +0 -8
  77. data/lib/c.png +0 -0
  78. data/lib/cm_admin/view_helpers/field_column_helper.rb +0 -23
  79. data/lib/generators/cm_admin/templates/cm_admin_initializer.rb +0 -4
  80. data/yarn-error.log +0 -44
@@ -0,0 +1,75 @@
1
+ module CmAdmin
2
+ module Models
3
+ module ControllerMethod
4
+ extend ActiveSupport::Concern
5
+
6
+ def show(params)
7
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
8
+ @ar_object = @ar_model.find(params[:id])
9
+ end
10
+
11
+ def index(params)
12
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
13
+ # Based on the params the filter and pagination object to be set
14
+ @ar_object = filter_by(params, nil, filter_params(params))
15
+ end
16
+
17
+ def new(params)
18
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
19
+ @ar_object = @ar_model.new
20
+ end
21
+
22
+ def edit(params)
23
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
24
+ @ar_object = @ar_model.find(params[:id])
25
+ end
26
+
27
+ def update(params)
28
+ @ar_object = @ar_model.find(params[:id])
29
+ @ar_object.assign_attributes(resource_params(params))
30
+ @ar_object
31
+ end
32
+
33
+ def create(params)
34
+ @ar_object = @ar_model.new(resource_params(params))
35
+ end
36
+
37
+ def filter_by(params, records, filter_params={}, sort_params={})
38
+ filtered_result = OpenStruct.new
39
+ sort_column = "created_at"
40
+ sort_direction = %w[asc desc].include?(sort_params[:sort_direction]) ? sort_params[:sort_direction] : "asc"
41
+ sort_params = {sort_column: sort_column, sort_direction: sort_direction}
42
+ records = self.name.constantize.where(nil) if records.nil?
43
+ final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, @filters)
44
+ pagy, records = pagy(final_data)
45
+ filtered_result.data = records
46
+ filtered_result.pagy = pagy
47
+ # filtered_result.facets = paginate(page, raw_data.size)
48
+ # filtered_result.sort = sort_params
49
+ # filtered_result.facets.sort = sort_params
50
+ return filtered_result
51
+ end
52
+
53
+ def resource_params(params)
54
+ permittable_fields = @permitted_fields || @ar_model.columns.map(&:name).reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }.map(&:to_sym)
55
+ permittable_fields += @ar_model.reflect_on_all_attachments.map {|x|
56
+ if x.class.name.include?('HasOne')
57
+ x.name
58
+ elsif x.class.name.include?('HasMany')
59
+ Hash[x.name.to_s, []]
60
+ end
61
+ }.compact
62
+ nested_tables = self.available_fields[:new].except(:fields).keys
63
+ nested_tables += self.available_fields[:edit].except(:fields).keys
64
+ nested_fields = nested_tables.map {|table|
65
+ Hash[
66
+ table.to_s + '_attributes',
67
+ table.to_s.singularize.titleize.constantize.columns.map(&:name).reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }.map(&:to_sym) + [:id, :_destroy]
68
+ ]
69
+ }
70
+ permittable_fields += nested_fields
71
+ params.require(self.name.underscore.to_sym).permit(*permittable_fields)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,122 @@
1
+ module CmAdmin
2
+ module Models
3
+ module DslMethod
4
+ extend ActiveSupport::Concern
5
+
6
+ def cm_index(page_title: nil ,page_description: nil, &block)
7
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'index')
8
+ @current_action.page_title = page_title
9
+ @current_action.page_description = page_description
10
+ yield
11
+ # action.instance_eval(&block)
12
+ end
13
+
14
+ def cm_show(page_title: nil,page_description: nil,&block)
15
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
16
+ @current_action.page_title = page_title
17
+ @current_action.page_description = page_description
18
+ yield
19
+ end
20
+
21
+ def cm_edit(page_title: nil,page_description: nil, &block)
22
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'edit')
23
+ @current_action.page_title = page_title
24
+ @current_action.page_description = page_description
25
+ yield
26
+ end
27
+
28
+ def cm_new(page_title: nil,page_description: nil,&block)
29
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'new')
30
+ @current_action.page_title = page_title
31
+ @current_action.page_description = page_description
32
+ yield
33
+ end
34
+
35
+ def page_title(title)
36
+ if @current_action
37
+ @current_action.page_title = title
38
+ end
39
+ end
40
+
41
+ def page_description(description)
42
+ if @current_action
43
+ @current_action.page_description = description
44
+ end
45
+ end
46
+
47
+ def tab(tab_name, custom_action, associated_model: nil, layout_type: nil, layout: nil, partial: nil, &block)
48
+ if custom_action.to_s == ''
49
+ @current_action = CmAdmin::Models::Action.find_by(self, name: 'show')
50
+ @available_tabs << CmAdmin::Models::Tab.new(tab_name, '', &block)
51
+ else
52
+ action = CmAdmin::Models::Action.new(name: custom_action.to_s, verb: :get, path: ':id/'+custom_action, layout_type: layout_type, layout: layout, partial: partial, child_records: associated_model)
53
+ @available_actions << action
54
+ @current_action = action
55
+ @available_tabs << CmAdmin::Models::Tab.new(tab_name, custom_action, &block)
56
+ end
57
+ yield if block
58
+ end
59
+
60
+ def cm_show_section(section_name, &block)
61
+ @available_fields[@current_action.name.to_sym] ||= []
62
+ @available_fields[@current_action.name.to_sym] << CmAdmin::Models::CmShowSection.new(section_name, &block)
63
+ end
64
+
65
+ def form_field(field_name, options={}, arg=nil)
66
+ unless @current_action.is_nested_field
67
+ @available_fields[@current_action.name.to_sym][:fields] << CmAdmin::Models::FormField.new(field_name, options[:input_type], options)
68
+ else
69
+ @available_fields[@current_action.name.to_sym][@current_action.nested_table_name] ||= []
70
+ @available_fields[@current_action.name.to_sym][@current_action.nested_table_name] << CmAdmin::Models::FormField.new(field_name, options[:input_type], options)
71
+ end
72
+ end
73
+
74
+ def nested_form_field(field_name, &block)
75
+ @current_action.is_nested_field = true
76
+ @current_action.nested_table_name = field_name
77
+ yield
78
+ end
79
+
80
+ def column(field_name, options={})
81
+ @available_fields[@current_action.name.to_sym] ||= []
82
+ if @available_fields[@current_action.name.to_sym].select{|x| x.lockable}.size > 0 && options[:lockable]
83
+ raise "Only one column can be locked in a table."
84
+ end
85
+ unless @available_fields[@current_action.name.to_sym].map{|x| x.field_name.to_sym}.include?(field_name)
86
+ @available_fields[@current_action.name.to_sym] << CmAdmin::Models::Column.new(field_name, options)
87
+ end
88
+ end
89
+
90
+ def all_db_columns(options={})
91
+ field_names = self.instance_variable_get(:@ar_model)&.columns&.map{|x| x.name.to_sym}
92
+ if options.include?(:exclude) && field_names
93
+ excluded_fields = (Array.new << options[:exclude]).flatten.map(&:to_sym)
94
+ field_names -= excluded_fields
95
+ end
96
+ field_names.each do |field_name|
97
+ column field_name
98
+ end
99
+ end
100
+
101
+ # Custom actions
102
+ # eg
103
+ # class User < ApplicationRecord
104
+ # cm_admin do
105
+ # custom_action name: 'submit', verb: 'post', path: ':id/submit' do
106
+ # def user_submit
107
+ # Code for action here...
108
+ # end
109
+ # end
110
+ # end
111
+ # end
112
+ def custom_action(name: nil, verb: nil, layout: nil, partial: nil, path: nil, &block)
113
+ @available_actions << CmAdmin::Models::Action.new(name: name, verb: verb, layout: layout, partial: partial, path: path)
114
+ self.class.class_eval(&block)
115
+ end
116
+
117
+ def filter(db_column_name, filter_type, options={})
118
+ @filters << CmAdmin::Models::Filter.new(db_column_name: db_column_name, filter_type: filter_type, options: options)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -2,19 +2,30 @@ module CmAdmin
2
2
  module Models
3
3
  class Export
4
4
  class << self
5
- def generate_excel(klass_name, columns = [])
5
+ def generate_excel(klass_name, columns = [], helpers)
6
6
  klass = klass_name.constantize
7
- records = get_records(klass, columns)
7
+ model = CmAdmin::Model.find_by({name: klass_name})
8
+ records = get_records(klass, model, columns, helpers)
8
9
  file_path = "#{Rails.root}/tmp/#{klass}_data_#{DateTime.now.strftime("%Y-%m-%d_%H-%M-%S")}.xlsx"
9
10
  create_workbook(records, columns, file_path)
10
11
  return file_path
11
12
  end
12
13
 
13
- def get_records(klass, columns)
14
+ def get_records(klass, model, columns, helpers)
14
15
  records = klass
16
+ custom_fields = model.available_fields[:index].map{|field| field if field.field_type == :custom}.compact
17
+ normal_fields = model.available_fields[:index].map{|field| field unless field.field_type == :custom}.compact
15
18
  deserialized_columns = CmAdmin::Utils.deserialize_csv_columns(columns, :as_json_params)
16
19
  # This includes isn't recursve, a full solution should be recursive
17
- records.includes(deserialized_columns[:include].keys).find_each.as_json(deserialized_columns)
20
+ records_arr = []
21
+ records.includes(deserialized_columns[:include].keys).find_each do |record|
22
+ record_hash = record.as_json({only: normal_fields.map(&:field_name)})
23
+ custom_fields.each do |field|
24
+ record_hash[field.field_name.to_sym] = helpers.send(field.helper_method, record, field.field_name)
25
+ end
26
+ records_arr << record_hash
27
+ end
28
+ records_arr
18
29
  end
19
30
 
20
31
  def create_workbook(records, class_name, file_path)
@@ -34,7 +45,7 @@ module CmAdmin
34
45
  end
35
46
 
36
47
  def exportable_columns(klass)
37
- klass.available_fields[:index].map{|x| x.exportable ? x.db_column_name : ""}.reject { |c| c.empty? }
48
+ klass.available_fields[:index].map{|x| x.exportable ? x.field_name : ""}.reject { |c| c.empty? }
38
49
  end
39
50
 
40
51
  end
@@ -1,7 +1,8 @@
1
1
  module CmAdmin
2
2
  module Models
3
3
  class Field
4
- attr_accessor :field_name, :label, :header
4
+
5
+ attr_accessor :field_name, :label, :header, :field_type, :format, :precision, :helper_method, :preview, :custom_link, :precision
5
6
 
6
7
  def initialize(field_name, attributes = {})
7
8
  @field_name = field_name
@@ -1,9 +1,9 @@
1
1
  module CmAdmin
2
2
  module Models
3
3
  class Filter
4
- attr_accessor :db_column_name, :filter_type, :placeholder, :collection, :multiselect, :checked
4
+ attr_accessor :db_column_name, :filter_type, :placeholder, :collection
5
5
 
6
- VALID_FILTER_TYPES = Set[:checkbox, :date, :dropdown, :range, :search].freeze
6
+ VALID_FILTER_TYPES = Set[:date, :multi_select, :range, :search, :single_select].freeze
7
7
 
8
8
  def initialize(db_column_name:, filter_type:, options: {})
9
9
  raise TypeError, "Can't have array of multiple columns for #{filter_type} filter" if db_column_name.is_a?(Array) && db_column_name.size > 1 && !filter_type.to_sym.eql?(:search)
@@ -25,6 +25,71 @@ module CmAdmin
25
25
  end
26
26
  [db_column_name, filter_type]
27
27
  end
28
+
29
+ # Methods to filter the records based on the filter type.
30
+ class << self
31
+ def filtered_data(filter_params, records, filters)
32
+ if filter_params
33
+ filter_params.each do |scope_type, scope_value|
34
+ scope_name = if scope_type.eql?('date') || scope_type.eql?('range')
35
+ 'date_and_range'
36
+ elsif scope_type.eql?('single_select') || scope_type.eql?('multi_select')
37
+ 'dropdown'
38
+ else
39
+ scope_type
40
+ end
41
+ records = self.send("cm_#{scope_name}_filter", scope_value, records, filters) if scope_value.present?
42
+ end
43
+ end
44
+ records
45
+ end
46
+
47
+ def cm_search_filter(scope_value, records, filters)
48
+ return nil if scope_value.blank?
49
+ table_name = records.table_name
50
+
51
+ filters.select{|x| x if x.filter_type.eql?(:search)}.each do |filter|
52
+ terms = scope_value.downcase.split(/\s+/)
53
+ terms = terms.map { |e|
54
+ (e.gsub('*', '%').prepend('%') + '%').gsub(/%+/, '%')
55
+ }
56
+ sql = ""
57
+ filter.db_column_name.each.with_index do |column, i|
58
+ sql.concat("#{table_name}.#{column} ILIKE ?")
59
+ sql.concat(' OR ') unless filter.db_column_name.size.eql?(i+1)
60
+ end
61
+
62
+ records = records.where(
63
+ terms.map { |term|
64
+ sql
65
+ }.join(' AND '),
66
+ *terms.map { |e| [e] * filter.db_column_name.size }.flatten
67
+ )
68
+ end
69
+ records
70
+ end
71
+
72
+ def cm_date_and_range_filter(scope_value, records, filters)
73
+ return nil if scope_value.nil?
74
+ scope_value.each do |key, value|
75
+ if value.present?
76
+ value = value.split(' to ')
77
+ from = value[0].presence
78
+ to = value[1].presence
79
+ records = records.where(key => from..to)
80
+ end
81
+ end
82
+ records
83
+ end
84
+
85
+ def cm_dropdown_filter(scope_value, records, filters)
86
+ return nil if scope_value.nil?
87
+ scope_value.each do |key, value|
88
+ records = records.where(key => value) if value.present?
89
+ end
90
+ records
91
+ end
92
+ end
28
93
  end
29
94
  end
30
95
  end
@@ -0,0 +1,21 @@
1
+ module CmAdmin
2
+ module Models
3
+ class FormField
4
+ attr_accessor :field_name, :label, :header, :input_type, :collection, :custom_value, :disabled
5
+ VALID_INPUT_TYPES = [:integer, :decimal, :string, :single_select, :multi_select, :date, :date_time, :text, :single_file_upload, :multi_file_upload, :hidden].freeze
6
+
7
+ def initialize(field_name, input_type, attributes = {})
8
+ raise ArgumentError, "Kindly select a valid filter type like #{VALID_INPUT_TYPES.sort.to_sentence(last_word_connector: ', or ')} instead of #{input_type} for column #{field_name}" unless VALID_INPUT_TYPES.include?(input_type.to_sym)
9
+ @field_name = field_name
10
+ set_default_values
11
+ attributes.each do |key, value|
12
+ self.send("#{key.to_s}=", value)
13
+ end
14
+ end
15
+
16
+ def set_default_values
17
+ self.disabled = false
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module CmAdmin
2
+ module Models
3
+ class Tab
4
+
5
+ attr_accessor :nav_item_name, :custom_action
6
+
7
+ def initialize(nav_item_name, custom_action)
8
+ @nav_item_name = nav_item_name
9
+ @custom_action = custom_action
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module CmAdmin
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,29 @@
1
+ module CmAdmin
2
+ module ViewHelpers
3
+ module ColumnFieldHelper
4
+
5
+ #adds prefix and suffix to a value
6
+ def add_prefix_and_suffix_helper(value, prefix, suffix)
7
+ "#{prefix} #{value} #{suffix}"
8
+ end
9
+
10
+ #formats the column value a field
11
+ def column_for_field_helper(ar_object, column)
12
+ value = ar_object.send(column.db_column_name)
13
+ formatted_value = CmAdmin::Models::Column.format_data_type(column, value)
14
+ formatted_value = add_prefix_and_suffix_helper(formatted_value, column.prefix, column.suffix)
15
+ formatted_value = link_url_value_helper(column, value, formatted_value)
16
+ return formatted_value
17
+ end
18
+
19
+ #column's value is either linked with 'url' attribute's value or its own value
20
+ def link_url_value_helper(column, value, formatted_value)
21
+ return formatted_value unless column.column_type.to_s == 'link'
22
+ link_url_value = column.url.present? ? column.url : value
23
+ final_value = "<a href=#{link_url_value}>#{formatted_value}</a>".html_safe
24
+ return final_value
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,65 @@
1
+ module CmAdmin
2
+ module ViewHelpers
3
+ module FieldDisplayHelper
4
+
5
+ def show_field(ar_object, field)
6
+ content_tag(:div, class: "info-split") do
7
+ concat show_field_label(ar_object, field)
8
+ concat show_field_value(ar_object, field)
9
+ end
10
+ end
11
+
12
+ def show_field_label(ar_object, field)
13
+ content_tag(:div, class: "info-split__lhs") do
14
+ p = field.label.present? ? field.label.to_s : field.field_name.to_s.titleize
15
+ end
16
+ end
17
+
18
+ def show_field_value(ar_object, field)
19
+ content_tag(:div, class: "info-split__rhs") do
20
+ case field.field_type || :string
21
+ when :integer
22
+ ar_object.send(field.field_name).to_s
23
+ when :decimal
24
+ ar_object.send(field.field_name).to_f.round(field.precision).to_s if ar_object.send(field.field_name)
25
+ when :string
26
+ ar_object.send(field.field_name).to_s
27
+ when :datetime
28
+ ar_object.send(field.field_name).strftime(field.format || "%d/%m/%Y").to_s if ar_object.send(field.field_name)
29
+ when :text
30
+ ar_object.send(field.field_name)
31
+ when :custom
32
+ send(field.helper_method, ar_object, field.field_name)
33
+ when :link
34
+ if field.custom_link
35
+ link = field.custom_link
36
+ else
37
+ link = ar_object.send(field.field_name)
38
+ end
39
+ content_tag :a, href: link do
40
+ ar_object.send(field.field_name).to_s
41
+ end
42
+ when :attachment
43
+ concat show_attachment_value(ar_object, field)
44
+ end
45
+ end
46
+ end
47
+
48
+ def show_attachment_value(ar_object, field)
49
+ if ar_object.send(field.field_name).attached?
50
+ if ar_object.send(field.field_name).class.name.include?('One')
51
+ content_tag :a, href: rails_blob_path(ar_object.send(field.field_name), disposition: "attachment") do
52
+ ar_object.send(field.field_name).filename.to_s
53
+ end
54
+ elsif ar_object.send(field.field_name).class.name.include?('Many')
55
+ ar_object.send(field.field_name).map do |asset|
56
+ content_tag :a, href: rails_blob_path(asset, disposition: "attachment") do
57
+ asset.filename.to_s
58
+ end
59
+ end.join("\n").html_safe
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end