Class: Madmin::Resource

Inherits:
Object
  • Object
show all
Defined in:
lib/madmin/resource.rb

Constant Summary collapse

Attribute =
Data.define(:name, :type, :field)

Class Method Summary collapse

Class Method Details

.attribute(name, type = nil, **options) {|config| ... } ⇒ Object

Yields:

  • (config)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/madmin/resource.rb', line 43

def attribute(name, type = nil, **options)
  type ||= infer_type(name)
  field = options.delete(:field) || field_for_type(type)

  if field.nil?
    Rails.logger.warn <<~MESSAGE
      WARNING: Madmin could not infer a field type for `#{name}` attribute. Defaulting to a String type.
      You can set the type by specifying the type on the attribute:

          attribute :#{name}, :boolean
    MESSAGE
    field = Fields::String
  end

  config = ActiveSupport::OrderedOptions.new.merge(options)
  yield config if block_given?

  # Form is an alias for new & edit
  if config.has_key?(:form)
    config.new = config[:form]
    config.edit = config[:form]
  end

  # New/create and edit/update need to match
  config.create = config[:create] if config.has_key?(:new)
  config.update = config[:update] if config.has_key?(:edit)

  attributes[name] = Attribute.new(
    name: name,
    type: type,
    field: field.new(attribute_name: name, model: model, resource: self, options: config)
  )
end

.becomes(record) ⇒ Object



107
108
109
# File 'lib/madmin/resource.rb', line 107

def becomes(record)
  record.instance_of?(model) ? record : record.becomes(model)
end

.display_name(record) ⇒ Object



119
120
121
# File 'lib/madmin/resource.rb', line 119

def display_name(record)
  "#{record.class} ##{record.id}"
end

.edit_path(record) ⇒ Object



103
104
105
# File 'lib/madmin/resource.rb', line 103

def edit_path(record)
  url_helpers.polymorphic_path([:madmin, route_namespace, becomes(record)], action: :edit)
end

.field_for_type(type) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/madmin/resource.rb', line 139

def field_for_type(type)
  {
    binary: Fields::String,
    blob: Fields::Text,
    boolean: Fields::Boolean,
    currency: Fields::Currency,
    date: Fields::Date,
    datetime: Fields::DateTime,
    decimal: Fields::Decimal,
    enum: Fields::Enum,
    float: Fields::Float,
    hstore: Fields::Json,
    integer: Fields::Integer,
    json: Fields::Json,
    jsonb: Fields::Json,
    primary_key: Fields::String,
    select: Fields::Select,
    string: Fields::String,
    text: Fields::Text,
    time: Fields::Time,
    timestamp: Fields::Time,
    timestamptz: Fields::Time,
    password: Fields::Password,
    file: Fields::File,

    # Postgres specific types
    bit: Fields::String,
    bit_varying: Fields::String,
    box: Fields::String,
    cidr: Fields::String,
    circle: Fields::String,
    citext: Fields::Text,
    daterange: Fields::String,
    inet: Fields::String,
    int4range: Fields::String,
    int8range: Fields::String,
    interval: Fields::String,
    line: Fields::String,
    lseg: Fields::String,
    ltree: Fields::String,
    macaddr: Fields::String,
    money: Fields::String,
    numrange: Fields::String,
    oid: Fields::String,
    path: Fields::String,
    point: Fields::String,
    polygon: Fields::String,
    tsrange: Fields::String,
    tstzrange: Fields::String,
    tsvector: Fields::String,
    uuid: Fields::String,
    xml: Fields::Text,

    # Associations
    attachment: Fields::Attachment,
    attachments: Fields::Attachments,
    belongs_to: Fields::BelongsTo,
    polymorphic: Fields::Polymorphic,
    has_many: Fields::HasMany,
    has_one: Fields::HasOne,
    rich_text: Fields::RichText,
    nested_has_many: Fields::NestedHasMany
  }[type]
end

.friendly_model?Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/madmin/resource.rb', line 123

def friendly_model?
  model.respond_to? :friendly
end

.friendly_nameObject



77
78
79
# File 'lib/madmin/resource.rb', line 77

def friendly_name
  model_name.gsub("::", " / ").split(/(?=[A-Z])/).join(" ")
end

.get_attribute(name) ⇒ Object



39
40
41
# File 'lib/madmin/resource.rb', line 39

def get_attribute(name)
  attributes[name]
end

.index_path(options = {}) ⇒ Object



91
92
93
# File 'lib/madmin/resource.rb', line 91

def index_path(options = {})
  url_helpers.polymorphic_path([:madmin, route_namespace, model], options)
end

.infer_type(name) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/madmin/resource.rb', line 204

def infer_type(name)
  name_string = name.to_s

  if model.attribute_types.include?(name_string)
    column_type = model.attribute_types[name_string]
    if column_type.is_a? ::ActiveRecord::Enum::EnumType
      :enum
    else
      column_type.type || :string
    end
  elsif (association = model.reflect_on_association(name))
    type_for_association(association)
  elsif model.reflect_on_association(:"rich_text_#{name_string}")
    :rich_text
  elsif model.reflect_on_association(:"#{name_string}_attachment")
    :attachment
  elsif model.reflect_on_association(:"#{name_string}_attachments")
    :attachments

  # has_secure_password
  elsif model.attribute_types.include?("#{name_string}_digest") || name_string.ends_with?("_confirmation")
    :password

    # ActiveRecord Store
  elsif model_store_accessors.include?(name)
    :string
  end
end

.inherited(base) ⇒ Object



11
12
13
14
15
16
# File 'lib/madmin/resource.rb', line 11

def inherited(base)
  base.attributes = attributes.dup
  base.member_actions = scopes.dup
  base.scopes = scopes.dup
  super
end

.member_action(&block) ⇒ Object



135
136
137
# File 'lib/madmin/resource.rb', line 135

def member_action(&block)
  member_actions << block
end


254
255
256
# File 'lib/madmin/resource.rb', line 254

def menu(options)
  @menu_options = options
end


258
259
260
261
262
# File 'lib/madmin/resource.rb', line 258

def menu_options
  return false if @menu_options == false
  @menu_options ||= {}
  @menu_options.with_defaults(label: friendly_name.pluralize, url: index_path)
end

.model(value = nil) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/madmin/resource.rb', line 18

def model(value = nil)
  if value
    @model = value
  else
    @model ||= model_name.constantize
  end
end

.model_find(id) ⇒ Object



26
27
28
29
# File 'lib/madmin/resource.rb', line 26

def model_find(id)
  record = friendly_model? ? model.friendly.find(id) : model.find(id)
  becomes(record)
end

.model_nameObject



31
32
33
# File 'lib/madmin/resource.rb', line 31

def model_name
  to_s.chomp("Resource").classify
end

.model_store_accessorsObject



249
250
251
252
# File 'lib/madmin/resource.rb', line 249

def model_store_accessors
  store_accessors = model.stored_attributes.values
  store_accessors.flatten
end

.new_pathObject



95
96
97
# File 'lib/madmin/resource.rb', line 95

def new_path
  url_helpers.polymorphic_path([:madmin, route_namespace, model], action: :new)
end

.param_keyObject



111
112
113
# File 'lib/madmin/resource.rb', line 111

def param_key
  model.model_name.param_key
end

.permitted_paramsObject



115
116
117
# File 'lib/madmin/resource.rb', line 115

def permitted_params
  attributes.values.filter { |a| a.field.visible?(:form) }.map { |a| a.field.to_param }
end

.route_namespaceObject

Support for isolated namespaces Finds parent module class to include in polymorphic urls



83
84
85
86
87
88
89
# File 'lib/madmin/resource.rb', line 83

def route_namespace
  return @route_namespace if instance_variable_defined?(:@route_namespace)
  namespace = model.module_parents.detect do |n|
    n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
  end
  @route_namespace = (namespace ? namespace.name.singularize.underscore.to_sym : nil)
end

.scope(name) ⇒ Object



35
36
37
# File 'lib/madmin/resource.rb', line 35

def scope(name)
  scopes << name
end

.searchable_attributesObject



131
132
133
# File 'lib/madmin/resource.rb', line 131

def searchable_attributes
  attributes.values.select { |a| a.field.searchable? }
end

.show_path(record) ⇒ Object



99
100
101
# File 'lib/madmin/resource.rb', line 99

def show_path(record)
  url_helpers.polymorphic_path([:madmin, route_namespace, becomes(record)])
end

.sortable_columnsObject



127
128
129
# File 'lib/madmin/resource.rb', line 127

def sortable_columns
  model.column_names
end

.type_for_association(association) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
# File 'lib/madmin/resource.rb', line 233

def type_for_association(association)
  if association.has_one?
    :has_one
  elsif association.collection?
    :has_many
  elsif association.polymorphic?
    :polymorphic
  else
    :belongs_to
  end
end

.url_helpersObject



245
246
247
# File 'lib/madmin/resource.rb', line 245

def url_helpers
  @url_helpers ||= Rails.application.routes.url_helpers
end