madmin 1.2.1 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f3bd8e986e4859620b06401c531dc24ca84bb02e4e2284a34918b5a088c126c
4
- data.tar.gz: 9bf59b4f93849feda832005caa748e02b4227ea2552cd6e94264d3b7d170f5a9
3
+ metadata.gz: ea857d0e707bca79172d1bcbecb72cfb939724279ab3f2c08546ff279fd38d03
4
+ data.tar.gz: c886c8ad0c5d0a11c81f1aecf45463e29205e9bf29dfe00fae4fc94466988ee0
5
5
  SHA512:
6
- metadata.gz: 3181e8f05aad7af36b905a4a04ffb2ddd63419b347e699b0b9ea6d8d2fb9a880f12ebbd7b12d05770e02dc7508e4789a1cb8250b11b6eed26878b6ef71d7fc1b
7
- data.tar.gz: '0019a725b02cde9e007190917381da718a139662d1d9094a0e49ccc69b799437865a4170658b99492947ce1d6d2ae04f71dde97886212a8dd728c3a9aa2275f3'
6
+ metadata.gz: 65e7522be423638030455299a70bfdd21cc706e75a5b6624f7a41f869250517474b00dd514551914e86bb3b7e7dac31aa812826461a634649caba8456b73f5c8
7
+ data.tar.gz: faea74829b0c7e307960dcbe006986f51c064e147c00b17f1234bfc6b72246511c1d206d4891fd773f3f97ea05b96a87dbcdd3e9b709aac1141407a580529b26
@@ -0,0 +1,22 @@
1
+ module Madmin
2
+ class ApplicationController < Madmin::BaseController
3
+ before_action :authenticate_admin_user
4
+
5
+ def authenticate_admin_user
6
+ # TODO: Add your authentication logic here
7
+
8
+ # For example, we could redirect if the user isn't an admin
9
+ # redirect_to "/", alert: "Not authorized." unless user_signed_in? && current_user.admin?
10
+ end
11
+
12
+ # Authenticate with Clearance
13
+ # include Clearance::Controller
14
+ # before_action :require_login
15
+
16
+ # Authenticate with Devise
17
+ # before_action :authenticate_user!
18
+
19
+ # Authenticate with Basic Auth
20
+ # http_basic_authenticate_with(name: Rails.application.credentials.admin_username, password: Rails.application.credentials.admin_password)
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  module Madmin
2
- class ResourceController < ApplicationController
2
+ class ResourceController < Madmin::ApplicationController
3
3
  include SortHelper
4
4
 
5
5
  before_action :set_record, except: [:index, :new, :create]
@@ -19,7 +19,7 @@
19
19
  <div id="sidebar" class="md:w-64 p-4 flex-shrink-0 border-r">
20
20
  <%= render "navigation" -%>
21
21
  </div>
22
- <main class="flex-grow p-4" role="main">
22
+ <main class="flex-grow p-4 overflow-x-scroll" role="main">
23
23
  <%#= render "flashes" -%>
24
24
  <%= yield %>
25
25
  </main>
@@ -3,7 +3,7 @@
3
3
  <%= stylesheet_link_tag "https://unpkg.com/tom-select/dist/css/tom-select.min.css", "data-turbo-track": "reload" %>
4
4
 
5
5
  <script type="module">
6
- import { Application, Controller } from 'https://cdn.skypack.dev/stimulus'
6
+ import { Application, Controller } from 'https://cdn.skypack.dev/@hotwired/stimulus'
7
7
  const application = Application.start()
8
8
 
9
9
  import { Dropdown } from "https://cdn.skypack.dev/tailwindcss-stimulus-components"
@@ -27,33 +27,36 @@
27
27
  <%= link_to scope.to_s.humanize, resource.index_path(scope: scope), class: class_names("p-2 rounded", {"bg-gray-100" => params[:scope] == scope.to_s}) %>
28
28
  <% end %>
29
29
  </div>
30
-
31
- <table class="min-w-full divide-y divide-gray-200">
32
- <thead>
33
- <tr class="border-b border-gray-200">
34
- <% resource.attributes.values.each do |attribute| %>
35
- <% next if attribute.field.nil? %>
36
- <% next unless attribute.field.visible?(action_name) %>
37
-
38
- <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase whitespace-nowrap"><%= sortable attribute.name, attribute.name.to_s.titleize %></th>
39
- <% end %>
40
- <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
41
- </tr>
42
- </thead>
43
-
44
- <tbody class="text-sm divide-y">
45
- <% @records.each do |record| %>
46
- <tr>
30
+ <div class="min-w-full max-w-xl overflow-x-auto pb-4">
31
+ <table class="min-w-full divide-y divide-gray-200">
32
+ <thead>
33
+ <tr class="border-b border-gray-200">
47
34
  <% resource.attributes.values.each do |attribute| %>
48
35
  <% next if attribute.field.nil? %>
49
36
  <% next unless attribute.field.visible?(action_name) %>
50
- <td class="px-4 py-2"><%= render partial: attribute.field.to_partial_path("index"), locals: { field: attribute.field, record: record } %></td>
51
- <% end %>
52
37
 
53
- <td class="px-4 py-2 text-center"><%= link_to "View", resource.show_path(record), class: "text-indigo-500" %></td>
38
+ <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase whitespace-nowrap"><%= sortable attribute.name, attribute.name.to_s.titleize %></th>
39
+ <% end %>
40
+ <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
54
41
  </tr>
55
- <% end %>
56
- </tbody>
57
- </table>
58
-
42
+ </thead>
43
+
44
+ <tbody class="text-sm divide-y">
45
+ <% @records.each do |record| %>
46
+ <tr>
47
+ <% resource.attributes.values.each do |attribute| %>
48
+ <% next if attribute.field.nil? %>
49
+ <% next unless attribute.field.visible?(action_name) %>
50
+ <td class="px-4 py-2"><%= render partial: attribute.field.to_partial_path("index"), locals: { field: attribute.field, record: record, resource: resource } %></td>
51
+ <% end %>
52
+
53
+ <td class="px-4 py-2 text-center">
54
+ <%= link_to "View", resource.show_path(record), class: "text-indigo-500" %>
55
+ <%= link_to "Edit", resource.edit_path(record), class: "text-indigo-500" %>
56
+ </td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+ </div>
59
62
  <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
 
26
26
  <div class="md:col-span-3">
27
- <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record } %>
27
+ <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record, resource: resource } %>
28
28
  </div>
29
29
  </div>
30
30
  <% end %>
@@ -0,0 +1,4 @@
1
+ <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
+ <%= render "madmin/shared/label", form: form, field: field %>
3
+ </div>
4
+ <%= form.file_field field.attribute_name, class: "form-input" %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -0,0 +1,6 @@
1
+ <% value = field.value(record) %>
2
+ <% if value.respond_to?(:url) %>
3
+ <%= link_to value.url, value.url, target: :_blank, class: "text-indigo-500 underline" %>
4
+ <% else %>
5
+ <%= value %>
6
+ <% end %>
@@ -1 +1,5 @@
1
- <%= field.value(record) %>
1
+ <% if field.attribute_name == :id %>
2
+ <%= link_to field.value(record), resource.show_path(record), class: "text-indigo-500 underline" %>
3
+ <% else %>
4
+ <%= field.value(record) %>
5
+ <% end %>
@@ -1,5 +1,5 @@
1
- <%= content_tag :div, class: "nested-fields bg-gray-100 rounded-t-xl p-5", data: { new_record: f.object.new_record? } do %>
2
- <% field.nested_attributes.each do |nested_attribute| %>
1
+ <%= content_tag :div, class: "nested-fields border border-gray-200 rounded-lg p-5", data: { new_record: f.object.new_record? } do %>
2
+ <% field.nested_attributes.each do |name, nested_attribute| %>
3
3
  <% next if nested_attribute[:field].nil? %>
4
4
  <% next unless nested_attribute[:field].visible?(action_name) %>
5
5
  <% next unless nested_attribute[:field].visible?(:form) %>
@@ -14,5 +14,4 @@
14
14
  <small><%= link_to "Remove", "#", data: { action: "click->nested-form#remove_association" } %></small>
15
15
 
16
16
  <%= f.hidden_field :_destroy %>
17
-
18
17
  <% end %>
@@ -1 +1 @@
1
- <%= field.value(record) %>
1
+ <%= truncate field.value(record) %>
@@ -28,69 +28,19 @@ module Madmin
28
28
 
29
29
  private
30
30
 
31
- def associations
32
- model.reflections.reject { |name, association|
33
- # Hide these special associations
34
- name.starts_with?("rich_text") ||
35
- name.ends_with?("_attachment") ||
36
- name.ends_with?("_attachments") ||
37
- name.ends_with?("_blob") ||
38
- name.ends_with?("_blobs")
39
- }.keys
31
+ def model
32
+ @model ||= class_name.constantize
40
33
  end
41
34
 
42
- def attributes
43
- model.attribute_names + virtual_attributes - redundant_attributes
35
+ def resource_builder
36
+ @resource_builder ||= ResourceBuilder.new(model)
44
37
  end
45
38
 
46
- def virtual_attributes
47
- virtual = []
48
-
49
- # has_secure_password columns
50
- password_attributes = model.attribute_types.keys.select { |k| k.ends_with?("_digest") }.map { |k| k.delete_suffix("_digest") }
51
- virtual += password_attributes.map { |attr| [attr, "#{attr}_confirmation"] }.flatten
52
-
53
- # Add virtual attributes for ActionText and ActiveStorage
54
- model.reflections.each do |name, association|
55
- if name.starts_with?("rich_text")
56
- virtual << name.split("rich_text_").last
57
- elsif name.ends_with?("_attachment")
58
- virtual << name.split("_attachment").first
59
- elsif name.ends_with?("_attachments")
60
- virtual << name.split("_attachments").first
61
- end
62
- end
63
-
64
- virtual
39
+ def model_attributes
40
+ resource_builder.attributes
65
41
  end
66
42
 
67
- def redundant_attributes
68
- redundant = []
69
-
70
- # has_secure_password columns
71
- redundant += model.attribute_types.keys.select { |k| k.ends_with?("_digest") }
72
-
73
- model.reflections.each do |name, association|
74
- if association.has_one?
75
- next
76
- elsif association.collection?
77
- next
78
- elsif association.polymorphic?
79
- redundant << "#{name}_id"
80
- redundant << "#{name}_type"
81
- elsif name.starts_with?("rich_text")
82
- redundant << name
83
- else # belongs to
84
- redundant << "#{name}_id"
85
- end
86
- end
87
-
88
- redundant
89
- end
90
-
91
- def model
92
- @model ||= class_name.constantize
93
- end
43
+ delegate :associations, :virtual_attributes, :store_accessors, to: :resource_builder
94
44
 
95
45
  def formatted_options_for_attribute(name)
96
46
  options = options_for_attribute(name)
@@ -114,7 +64,7 @@ module Madmin
114
64
  {form: false}
115
65
 
116
66
  # Attributes without a database column
117
- elsif !model.column_names.include?(name)
67
+ elsif !model.column_names.include?(name) && !store_accessors.map(&:to_s).include?(name)
118
68
  {index: false}
119
69
  end
120
70
  end
@@ -1,6 +1,6 @@
1
1
  class <%= class_name %>Resource < Madmin::Resource
2
2
  # Attributes
3
- <% attributes.each do |attribute_name| -%>
3
+ <% model_attributes.each do |attribute_name| -%>
4
4
  attribute :<%= attribute_name %><%= formatted_options_for_attribute(attribute_name) %>
5
5
  <% end -%>
6
6
 
data/lib/madmin/engine.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Madmin
2
2
  class Engine < ::Rails::Engine
3
- initializer "madmin.autoload", before: :set_autoload_paths do |app|
4
- app.config.paths.add "app/madmin/resources", eager_load: true
5
- app.config.paths.add "app/madmin/fields", eager_load: true
3
+ config.before_configuration do |app|
4
+ app.config.autoload_paths << File.expand_path("app/madmin/resources", Rails.root)
5
+ app.config.autoload_paths << File.expand_path("app/madmin/fields", Rails.root)
6
6
  end
7
7
 
8
8
  config.to_prepare do
data/lib/madmin/field.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  module Madmin
2
2
  class Field
3
- attr_reader :attribute_name, :model, :options
3
+ attr_reader :attribute_name, :model, :options, :resource
4
4
 
5
5
  def self.field_type
6
6
  to_s.split("::").last.underscore
7
7
  end
8
8
 
9
- def initialize(attribute_name:, model:, **options)
9
+ def initialize(attribute_name:, model:, resource:, **options)
10
10
  @attribute_name = attribute_name
11
11
  @model = model
12
+ @resource = resource
12
13
  @options = options
13
14
  end
14
15
 
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ module Fields
3
+ class File < Field
4
+ def searchable?
5
+ false
6
+ end
7
+ end
8
+ end
9
+ end
@@ -3,7 +3,7 @@ module Madmin
3
3
  class NestedHasMany < Field
4
4
  DEFAULT_ATTRIBUTES = %w[_destroy id].freeze
5
5
  def nested_attributes
6
- resource.attributes.reject { |i| skipped_fields.include?(i[:name]) }
6
+ resource.attributes.reject { |name, attribute| skipped_fields.include?(name) }
7
7
  end
8
8
 
9
9
  def resource
@@ -11,7 +11,7 @@ module Madmin
11
11
  end
12
12
 
13
13
  def model
14
- model_name.constantize
14
+ @model ||= model_name.constantize
15
15
  end
16
16
 
17
17
  def model_find(id)
@@ -37,36 +37,51 @@ module Madmin
37
37
  attributes[name] = OpenStruct.new(
38
38
  name: name,
39
39
  type: type,
40
- field: field.new(**options.merge(attribute_name: name, model: model))
40
+ field: field.new(**options.merge(attribute_name: name, model: model, resource: self))
41
41
  )
42
+ rescue => e
43
+ builder = ResourceBuilder.new(model)
44
+ raise ArgumentError, <<~MESSAGE
45
+ Madmin couldn't find attribute or association '#{name}' on #{model} model.
46
+
47
+ We searched these attributes and associations:
48
+ #{(builder.attributes + builder.associations).join(", ")}
49
+
50
+ This attribute is defined in a Madmin resource at:
51
+ #{e.backtrace.find { |l| l =~ /_resource.rb/ }}
52
+
53
+ Either add the missing attribute or assocation, or remove this line from your Madmin resource.
54
+ MESSAGE
42
55
  end
43
56
 
44
57
  def friendly_name
45
58
  model_name.gsub("::", " / ")
46
59
  end
47
60
 
48
- def index_path(options = {})
49
- route_name = "madmin_#{model.model_name.plural}_path"
61
+ # Support for isolated namespaces
62
+ # Finds parent module class to include in polymorphic urls
63
+ def route_namespace
64
+ return @route_namespace if instance_variable_defined?(:@route_namespace)
65
+ namespace = model.module_parents.detect do |n|
66
+ n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
67
+ end
68
+ @route_namespace = (namespace ? namespace.name.singularize.underscore.to_sym : nil)
69
+ end
50
70
 
51
- url_helpers.send(route_name, options)
71
+ def index_path(options = {})
72
+ url_helpers.polymorphic_path([:madmin, route_namespace, model], options)
52
73
  end
53
74
 
54
75
  def new_path
55
- route_name = "new_madmin_#{model.model_name.singular}_path"
56
-
57
- url_helpers.send(route_name)
76
+ url_helpers.polymorphic_path([:madmin, route_namespace, model], action: :new)
58
77
  end
59
78
 
60
79
  def show_path(record)
61
- route_name = "madmin_#{model.model_name.singular}_path"
62
-
63
- url_helpers.send(route_name, record.to_param)
80
+ url_helpers.polymorphic_path([:madmin, route_namespace, record])
64
81
  end
65
82
 
66
83
  def edit_path(record)
67
- route_name = "edit_madmin_#{model.model_name.singular}_path"
68
-
69
- url_helpers.send(route_name, record.to_param)
84
+ url_helpers.polymorphic_path([:madmin, route_namespace, record], action: :edit)
70
85
  end
71
86
 
72
87
  def param_key
@@ -115,6 +130,7 @@ module Madmin
115
130
  time: Fields::Time,
116
131
  timestamp: Fields::Time,
117
132
  password: Fields::Password,
133
+ file: Fields::File,
118
134
 
119
135
  # Postgres specific types
120
136
  bit: Fields::String,
@@ -154,15 +170,6 @@ module Madmin
154
170
  rich_text: Fields::RichText,
155
171
  nested_has_many: Fields::NestedHasMany
156
172
  }.fetch(type)
157
- rescue
158
- raise ArgumentError, <<~MESSAGE
159
- Couldn't find attribute or association '#{name}' with type '#{type}' on #{model} model
160
-
161
- To fix this, either:
162
-
163
- 1. Remove 'attribute #{name}' from app/madmin/resources/#{model.to_s.underscore}_resource.rb
164
- 2. Or add the missing attribute or association to the #{model} model
165
- MESSAGE
166
173
  end
167
174
 
168
175
  def infer_type(name)
@@ -187,6 +194,10 @@ module Madmin
187
194
  # has_secure_password
188
195
  elsif model.attribute_types.include?("#{name_string}_digest") || name_string.ends_with?("_confirmation")
189
196
  :password
197
+
198
+ # ActiveRecord Store
199
+ elsif model_store_accessors.include?(name)
200
+ :string
190
201
  end
191
202
  end
192
203
 
@@ -205,6 +216,12 @@ module Madmin
205
216
  def url_helpers
206
217
  @url_helpers ||= Rails.application.routes.url_helpers
207
218
  end
219
+
220
+ def model_store_accessors
221
+ store_accessors = model.stored_attributes.values
222
+
223
+ store_accessors.flatten
224
+ end
208
225
  end
209
226
  end
210
227
  end
@@ -0,0 +1,80 @@
1
+ module Madmin
2
+ class ResourceBuilder
3
+ attr_reader :model
4
+
5
+ def initialize(model)
6
+ @model = model
7
+ end
8
+
9
+ def associations
10
+ model.reflections.reject { |name, association|
11
+ # Hide these special associations
12
+ name.starts_with?("rich_text") ||
13
+ name.ends_with?("_attachment") ||
14
+ name.ends_with?("_attachments") ||
15
+ name.ends_with?("_blob") ||
16
+ name.ends_with?("_blobs")
17
+ }.keys
18
+ end
19
+
20
+ def attributes
21
+ model.attribute_names + virtual_attributes - redundant_attributes
22
+ end
23
+
24
+ def store_accessors
25
+ model.stored_attributes.values.flatten
26
+ end
27
+
28
+ def virtual_attributes
29
+ virtual = []
30
+
31
+ # has_secure_password columns
32
+ password_attributes = model.attribute_types.keys.select { |k| k.ends_with?("_digest") }.map { |k| k.delete_suffix("_digest") }
33
+ virtual += password_attributes.map { |attr| [attr, "#{attr}_confirmation"] }.flatten
34
+
35
+ # ActiveRecord Store columns
36
+ virtual += store_accessors.map(&:to_s)
37
+
38
+ # Add virtual attributes for ActionText and ActiveStorage
39
+ model.reflections.each do |name, association|
40
+ if name.starts_with?("rich_text")
41
+ virtual << name.split("rich_text_").last
42
+ elsif name.ends_with?("_attachment")
43
+ virtual << name.split("_attachment").first
44
+ elsif name.ends_with?("_attachments")
45
+ virtual << name.split("_attachments").first
46
+ end
47
+ end
48
+
49
+ virtual
50
+ end
51
+
52
+ def redundant_attributes
53
+ redundant = []
54
+
55
+ # has_secure_password columns
56
+ redundant += model.attribute_types.keys.select { |k| k.ends_with?("_digest") }
57
+
58
+ # ActiveRecord Store columns
59
+ store_columns = model.stored_attributes.keys
60
+ redundant += store_columns.map(&:to_s)
61
+
62
+ model.reflections.each do |name, association|
63
+ if association.has_one?
64
+ next
65
+ elsif association.collection?
66
+ next
67
+ elsif association.polymorphic?
68
+ redundant << "#{name}_id"
69
+ redundant << "#{name}_type"
70
+ elsif name.starts_with?("rich_text")
71
+ redundant << name
72
+ else # belongs to
73
+ redundant << "#{name}_id"
74
+ end
75
+ end
76
+
77
+ redundant
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,3 @@
1
1
  module Madmin
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.5"
3
3
  end
data/lib/madmin.rb CHANGED
@@ -6,6 +6,7 @@ module Madmin
6
6
  autoload :Field, "madmin/field"
7
7
  autoload :GeneratorHelpers, "madmin/generator_helpers"
8
8
  autoload :Resource, "madmin/resource"
9
+ autoload :ResourceBuilder, "madmin/resource_builder"
9
10
  autoload :Search, "madmin/search"
10
11
 
11
12
  module Fields
@@ -17,6 +18,7 @@ module Madmin
17
18
  autoload :DateTime, "madmin/fields/date_time"
18
19
  autoload :Decimal, "madmin/fields/decimal"
19
20
  autoload :Enum, "madmin/fields/enum"
21
+ autoload :File, "madmin/fields/file"
20
22
  autoload :Float, "madmin/fields/float"
21
23
  autoload :HasMany, "madmin/fields/has_many"
22
24
  autoload :HasOne, "madmin/fields/has_one"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: madmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-09-09 00:00:00.000000000 Z
12
+ date: 2021-09-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -59,6 +59,7 @@ files:
59
59
  - app/assets/config/manifest.js
60
60
  - app/assets/stylesheets/actiontext.scss
61
61
  - app/assets/stylesheets/application.css
62
+ - app/controllers/madmin/application_controller.rb
62
63
  - app/controllers/madmin/base_controller.rb
63
64
  - app/controllers/madmin/dashboard_controller.rb
64
65
  - app/controllers/madmin/resource_controller.rb
@@ -98,6 +99,9 @@ files:
98
99
  - app/views/madmin/fields/enum/_form.html.erb
99
100
  - app/views/madmin/fields/enum/_index.html.erb
100
101
  - app/views/madmin/fields/enum/_show.html.erb
102
+ - app/views/madmin/fields/file/_form.html.erb
103
+ - app/views/madmin/fields/file/_index.html.erb
104
+ - app/views/madmin/fields/file/_show.html.erb
101
105
  - app/views/madmin/fields/float/_form.html.erb
102
106
  - app/views/madmin/fields/float/_index.html.erb
103
107
  - app/views/madmin/fields/float/_show.html.erb
@@ -167,6 +171,7 @@ files:
167
171
  - lib/madmin/fields/date_time.rb
168
172
  - lib/madmin/fields/decimal.rb
169
173
  - lib/madmin/fields/enum.rb
174
+ - lib/madmin/fields/file.rb
170
175
  - lib/madmin/fields/float.rb
171
176
  - lib/madmin/fields/has_many.rb
172
177
  - lib/madmin/fields/has_one.rb
@@ -182,6 +187,7 @@ files:
182
187
  - lib/madmin/generator_helpers.rb
183
188
  - lib/madmin/namespace.rb
184
189
  - lib/madmin/resource.rb
190
+ - lib/madmin/resource_builder.rb
185
191
  - lib/madmin/search.rb
186
192
  - lib/madmin/version.rb
187
193
  - lib/madmin/view_generator.rb