tiny_admin 0.3.0 → 0.5.0

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: d203e48b821f195f95847a646e32787c9ffd953212326f64e65ca09d563ed55e
4
- data.tar.gz: a133ad428942d240ec8852e4885e12a51bc6a96f9a4bcf426470180671dddf6f
3
+ metadata.gz: c3ac2be067ca35c35f40c677c5f9461c5f83e366488a48e6ab50a888f0f22317
4
+ data.tar.gz: 2289dd33940174e8e614260f96273967ade51f75dc67a04d5e639415c984e65b
5
5
  SHA512:
6
- metadata.gz: 8955bf003179333debcf4c43dd7091b4d81fc9c1e90a6116119dc33f51b4ab7dca394d90f6f8aa6bb0385339dc5eb07b64bd546f0a92e02497d5c000cc835769
7
- data.tar.gz: 5cabb3dc010e73b416068cca3afda56dc07ac7b024c70d734b9409298d2082798a8e5e276d892be81e82dcbfa68bad20360f0d99e4c8e7199a7bdb5067f72124
6
+ metadata.gz: f4f80d86ac6b30bc29cf8e2f1efe480edefe84907bf5e9b7a3a8c3b3c3750ff59a1f9e609c87090105c0cd31fa22df7aa913858911a455d918633269c27317e7
7
+ data.tar.gz: 4fe8a75d9e3c0cc3bd756ab247fecb7575049dcec58559bdb7cdb1c58ecb4b6e7de3f8be96c396b0b304134cc75c6058b3cbeafce92cd027413971a99a812c02
data/README.md CHANGED
@@ -16,10 +16,10 @@ Please ⭐ if you like it.
16
16
 
17
17
  ## Install
18
18
 
19
- - Add to your Gemfile: `gem 'tiny_admin', '~> 0.3'`
19
+ - Add to your Gemfile: `gem 'tiny_admin', '~> 0.5'`
20
20
  - Mount the app in a route (check some examples with: Hanami, Rails, Roda and standalone in [extra](extra))
21
21
  + in Rails, update _config/routes.rb_: `mount TinyAdmin::Router => '/admin'`
22
- - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` (see [configuration](#configuration) below):
22
+ - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` with a YAML config file (see [configuration](#configuration) below):
23
23
 
24
24
  ```rb
25
25
  TinyAdmin.configure do |settings|
@@ -35,32 +35,37 @@ end
35
35
  ### Authentication
36
36
 
37
37
  Plugins available:
38
- - _SimpleAuth_: session authentication based on Warden (`warden` gem must be included in the Gemfile) using a password hash provided via config or via environment variable (`ADMIN_PASSWORD_HASH`);
39
- - _NoAuth_: no authentication.
38
+
39
+ - **SimpleAuth**: a session authentication based on Warden (`warden` gem must be included in the Gemfile) using a password hash provided via config or via environment variable (`ADMIN_PASSWORD_HASH`). _Disclaimer: this plugin is provided as example, if you need a secure authentication I suggest to create your own._
40
+
41
+ - **NoAuth**: no authentication.
40
42
 
41
43
  ### Repository
42
44
 
43
45
  Plugin available:
44
- - _ActiveRecordRepository_: isolates the query layer to expose the resources in the admin interface.
46
+
47
+ - **ActiveRecordRepository**: isolates the query layer to expose the resources in the admin interface.
45
48
 
46
49
  ### View pages
47
50
 
48
51
  Pages available:
49
- - _Root_: define how to present the content in the main page of the interface;
50
- - _PageNotFound_: define how to present pages not found;
51
- - _RecordNotFound_: define how to present record not found page;
52
- - _SimpleAuthLogin_: define how to present the login form for SimpleAuth plugin;
53
- - _Index_: define how to present a collection of items;
54
- - _Show_: define how to present the details of an item.
52
+
53
+ - **Root**: define how to present the content in the main page of the interface;
54
+ - **PageNotFound**: define how to present pages not found;
55
+ - **RecordNotFound**: define how to present record not found page;
56
+ - **SimpleAuthLogin**: define how to present the login form for SimpleAuth plugin;
57
+ - **Index**: define how to present a collection of items;
58
+ - **Show**: define how to present the details of an item.
55
59
 
56
60
  ### View components
57
61
 
58
62
  Components available:
59
- - _FiltersForm_: define how to present the filters form in the resource collection pages;
60
- - _Flash_: define how to present the flash messages;
61
- - _Head_: define how to present the Head tag;
62
- - _Navbar_: define how to present the navbar (the default one uses the Bootstrap structure);
63
- - _Pagination_: define how to present the pagination of a collection.
63
+
64
+ - **FiltersForm**: define how to present the filters form in the resource collection pages;
65
+ - **Flash**: define how to present the flash messages;
66
+ - **Head**: define how to present the Head tag;
67
+ - **Navbar**: define how to present the navbar (the default one uses the Bootstrap structure);
68
+ - **Pagination**: define how to present the pagination of a collection.
64
69
 
65
70
  ## Configuration
66
71
 
@@ -70,9 +75,10 @@ See [extra](extra) folder for some usage examples.
70
75
  The following options are supported:
71
76
 
72
77
  `root` (Hash): define the root section of the admin, properties:
73
- - `title` (String): root section's title;
74
- - `page` (String): a view object to render;
75
- - `redirect` (String): alternative to _page_ option - redirects to a specific slug;
78
+
79
+ - `title` (String): root section's title;
80
+ - `page` (String): a view object to render;
81
+ - `redirect` (String): alternative to _page_ option - redirects to a specific slug;
76
82
 
77
83
  Example:
78
84
 
@@ -82,9 +88,27 @@ root:
82
88
  redirect: posts
83
89
  ```
84
90
 
91
+ `helper_class` (String): class or module with helper methods, used for attributes' formatters.
92
+
93
+ `page_not_found` (String): a view object to render when a missing page is requested.
94
+
95
+ `record_not_found` (String): a view object to render when a missing record is requested.
96
+
97
+ `style_links` (Array of hashes): list of styles files to include, properties:
98
+
99
+ - `href` (String): URL for the style file;
100
+ - `rel` (String): type of style file.
101
+
102
+ `scripts` (Array of hashes): list of scripts to include, properties:
103
+
104
+ - `src` (String): source URL for the script.
105
+
106
+ `extra_styles` (String): inline CSS styles.
107
+
85
108
  `authentication` (Hash): define the authentication method, properties:
86
- - `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
87
- - `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
109
+
110
+ - `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
111
+ - `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
88
112
 
89
113
  Example:
90
114
 
@@ -95,15 +119,17 @@ authentication:
95
119
  ```
96
120
 
97
121
  `sections` (Array of hashes): define the admin sections, properties:
98
- - `slug` (String): section reference identifier;
99
- - `name` (String): section's title;
100
- - `type` (String): the type of section: `url`, `page` or `resource`;
101
- - other properties depends on the section's type.
122
+
123
+ - `slug` (String): section reference identifier;
124
+ - `name` (String): section's title;
125
+ - `type` (String): the type of section: `url`, `page` or `resource`;
126
+ - other properties depends on the section's type.
102
127
 
103
128
  For _url_ sections:
104
- - `url` (String): the URL to load when clicking on the section's menu item;
105
- - `options` (Hash): properties:
106
- + `target` (String): link _target_ attributes (ex. `_blank`).
129
+
130
+ - `url` (String): the URL to load when clicking on the section's menu item;
131
+ - `options` (Hash): properties:
132
+ + `target` (String): link _target_ attributes (ex. `_blank`).
107
133
 
108
134
  Example:
109
135
 
@@ -117,7 +143,8 @@ options:
117
143
  ```
118
144
 
119
145
  For _page_ sections:
120
- - `page` (String): a view object to render.
146
+
147
+ - `page` (String): a view object to render.
121
148
 
122
149
  Example:
123
150
 
@@ -129,14 +156,15 @@ page: Admin::Stats
129
156
  ```
130
157
 
131
158
  For _resource_ sections:
132
- - `model` (String): the class to use to fetch the data on an item of a collection;
133
- - `repository` (String): the class to get the properties related to the model;
134
- - `index` (Hash): collection's action options;
135
- - `show` (Hash): detail's action options;
136
- - `collection_actions` (Array of hashes): custom collection's actions;
137
- - `member_actions` (Array of hashes): custom details's actions;
138
- - `only` (Array of strings): list of supported actions (ex. `index`);
139
- - `options` (Array of strings): resource options (ex. `hidden`).
159
+
160
+ - `model` (String): the class to use to fetch the data on an item of a collection;
161
+ - `repository` (String): the class to get the properties related to the model;
162
+ - `index` (Hash): collection's action options (see below);
163
+ - `show` (Hash): detail's action options (see below);
164
+ - `collection_actions` (Array of hashes): custom collection's actions;
165
+ - `member_actions` (Array of hashes): custom details's actions;
166
+ - `only` (Array of strings): list of supported actions (ex. `index`);
167
+ - `options` (Array of strings): resource options (ex. `hidden`).
140
168
 
141
169
  Example:
142
170
 
@@ -147,14 +175,72 @@ type: resource
147
175
  model: Post
148
176
  ```
149
177
 
150
- `style_links` (Array of hashes): list of styles files to include, properties:
151
- - `href` (String): URL for the style file;
152
- - `rel` (String): type of style file.
178
+ #### Resource index options
153
179
 
154
- `scripts` (Array of hashes): list of scripts to include, properties:
155
- - `src` (String): source URL for the script.
180
+ The Index hash supports the following options:
156
181
 
157
- `extra_styles` (String): inline CSS styles.
182
+ - `attributes` (Array): fields to expose in the resource list page;
183
+ - `filters` (Array): filter the current listing;
184
+ - `links` (Array): custom member actions to expose for each list's entry (defined in _member_actions_);
185
+ - `pagination` (Integer): max pages size;
186
+ - `sort` (Array): sort options to pass to the listing query.
187
+
188
+ Example:
189
+
190
+ ```yml
191
+ index:
192
+ sort:
193
+ - id DESC
194
+ pagination: 10
195
+ attributes:
196
+ - id
197
+ - author: call, name
198
+ - position: round, 1
199
+ - field: author_id
200
+ header: The author
201
+ link_to: authors
202
+ call: author, name
203
+ filters:
204
+ - title
205
+ - field: state
206
+ type: select
207
+ values:
208
+ - published
209
+ - draft
210
+ - archived
211
+ links:
212
+ - show
213
+ - author_posts
214
+ - csv_export
215
+ ```
216
+
217
+ #### Resource show options
218
+
219
+ The Show hash supports the following options:
220
+
221
+ - `attributes` (Array): fields to expose in the resource details page.
222
+
223
+ Example:
224
+
225
+ ```yml
226
+ show:
227
+ attributes:
228
+ # Expose the id column
229
+ - id
230
+ # Expose the title column, calling `downcase` support method
231
+ - title: downcase
232
+ # Expose the category column, calling `upcase` support method
233
+ - category: upcase
234
+ # Expose the position column, calling `format` support method with argument %f
235
+ - position: format, %f
236
+ # Expose the position created_at, calling `strftime` support method with argument %Y%m%d %H:%M
237
+ - created_at: strftime, %Y%m%d %H:%M
238
+ # Expose the author_id column, with a custom header label, linked to authors section and calling author.name to get the value
239
+ - field: author_id
240
+ header: The author
241
+ link_to: authors
242
+ call: author, name
243
+ ```
158
244
 
159
245
  ### Sample
160
246
 
@@ -175,65 +261,64 @@ end
175
261
  ---
176
262
  authentication:
177
263
  plugin: TinyAdmin::Plugins::SimpleAuth
178
- # password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395df...' <= SHA512
179
- page_not_found: Admin::PageNotFound
180
- record_not_found: Admin::RecordNotFound
264
+ # password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
181
265
  root:
182
- title: 'Tiny Admin'
183
- page: Admin::PageRoot
184
- # redirect: posts
266
+ title: Test Admin
267
+ # page: RootPage
268
+ helper_class: AdminHelper
269
+ page_not_found: PageNotFound
270
+ record_not_found: RecordNotFound
185
271
  sections:
186
272
  - slug: google
187
273
  name: Google.it
188
274
  type: url
189
275
  url: https://www.google.it
190
276
  options:
191
- target: '_blank'
192
- - slug: stats
193
- name: Stats
277
+ target: _blank
278
+ - slug: sample
279
+ name: Sample page
194
280
  type: page
195
- page: Admin::Stats
281
+ page: SamplePage
196
282
  - slug: authors
197
283
  name: Authors
198
284
  type: resource
199
285
  model: Author
200
- repository: Admin::AuthorsRepo
201
286
  collection_actions:
202
- - latests: Admin::LatestAuthorsAction
287
+ - sample_col: SampleCollectionAction
203
288
  member_actions:
204
- - csv_export: Admin::CsvExportAuthorAction
205
- # only:
206
- # - index
207
- # options:
208
- # - hidden
289
+ - sample_mem: SampleMemberAction
209
290
  - slug: posts
210
291
  name: Posts
211
292
  type: resource
212
293
  model: Post
213
294
  index:
214
- sort:
215
- - author_id DESC
216
- pagination: 15
295
+ pagination: 5
217
296
  attributes:
218
297
  - id
219
298
  - title
220
299
  - field: author_id
221
300
  link_to: authors
222
- - state
301
+ - category: upcase
302
+ - state: downcase
223
303
  - published
224
- - dt
304
+ - position: round, 1
305
+ - dt: to_date
225
306
  - field: created_at
226
- converter: Admin::Utils
307
+ converter: AdminUtils
227
308
  method: datetime_formatter
309
+ - updated_at: strftime, %Y%m%d %H:%M
228
310
  filters:
229
311
  - title
230
- - field: state
312
+ - author_id
313
+ - field: category
231
314
  type: select
232
315
  values:
233
- - available
234
- - unavailable
235
- - arriving
316
+ - news
317
+ - sport
318
+ - tech
236
319
  - published
320
+ - dt
321
+ - created_at
237
322
  show:
238
323
  attributes:
239
324
  - id
@@ -243,7 +328,8 @@ sections:
243
328
  link_to: authors
244
329
  - category
245
330
  - published
246
- - state
331
+ - position: format, %f
332
+ - dt
247
333
  - created_at
248
334
  style_links:
249
335
  - href: /bootstrap.min.css
@@ -5,12 +5,21 @@ module TinyAdmin
5
5
  class BasicAction
6
6
  include Utils
7
7
 
8
- attr_reader :params, :path, :repository
9
-
10
- def initialize(repository, path:, params:)
11
- @repository = repository
12
- @path = path
13
- @params = params
8
+ def attribute_options(options)
9
+ options&.each_with_object({}) do |field, result|
10
+ field_data =
11
+ if field.is_a?(Hash)
12
+ if field.one?
13
+ field, method = field.first
14
+ { field.to_s => { field: field.to_s, method: method } }
15
+ else
16
+ { field[:field] => field }
17
+ end
18
+ else
19
+ { field => { field: field } }
20
+ end
21
+ result.merge!(field_data)
22
+ end
14
23
  end
15
24
  end
16
25
  end
@@ -3,9 +3,18 @@
3
3
  module TinyAdmin
4
4
  module Actions
5
5
  class Index < BasicAction
6
- attr_reader :current_page, :fields_options, :filters_list, :pagination, :pages, :query_string, :sort
6
+ attr_reader :current_page,
7
+ :fields_options,
8
+ :filters_list,
9
+ :links,
10
+ :pagination,
11
+ :pages,
12
+ :params,
13
+ :query_string,
14
+ :repository,
15
+ :sort
7
16
 
8
- def call(app:, context:, options:, actions:)
17
+ def call(app:, context:, options:)
9
18
  evaluate_options(options)
10
19
  fields = repository.fields(options: fields_options)
11
20
  filters = prepare_filters(fields, filters_list)
@@ -14,9 +23,10 @@ module TinyAdmin
14
23
  prepare_page(Views::Actions::Index) do |page|
15
24
  setup_pagination(page, settings.components[:pagination], total_count: total_count)
16
25
  page.update_attributes(
17
- actions: actions,
26
+ actions: context.actions,
18
27
  fields: fields,
19
28
  filters: filters,
29
+ links: links,
20
30
  prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
21
31
  records: records,
22
32
  title: repository.index_title
@@ -27,12 +37,13 @@ module TinyAdmin
27
37
  private
28
38
 
29
39
  def evaluate_options(options)
30
- @fields_options = options[:attributes]&.each_with_object({}) do |field, result|
31
- result.merge!(field.is_a?(Hash) ? { field[:field] => field } : { field => { field: field } })
32
- end
40
+ @fields_options = attribute_options(options[:attributes])
41
+ @params = context.request.params
42
+ @repository = context.repository
33
43
  @filters_list = options[:filters]
34
44
  @pagination = options[:pagination] || 10
35
- @sort = options[:sort] || ['id']
45
+ @sort = options[:sort]
46
+ @links = options[:links]
36
47
 
37
48
  @current_page = (params['p'] || 1).to_i
38
49
  @query_string = params_to_s(params.except('p'))
@@ -42,8 +53,8 @@ module TinyAdmin
42
53
  filters = (filters_list || []).map { _1.is_a?(Hash) ? _1 : { field: _1 } }
43
54
  filters = filters.each_with_object({}) { |filter, result| result[filter[:field]] = filter }
44
55
  values = (params['q'] || {})
45
- fields.each_with_object({}) do |field, result|
46
- result[field] = { value: values[field.name], filter: filters[field.name] } if filters.key?(field.name)
56
+ fields.each_with_object({}) do |(name, field), result|
57
+ result[field] = { value: values[name], filter: filters[name] } if filters.key?(name)
47
58
  end
48
59
  end
49
60
 
@@ -52,7 +63,7 @@ module TinyAdmin
52
63
  return if pages <= 1 || !pagination_component_class
53
64
 
54
65
  page.pagination_component = pagination_component_class.new
55
- page.pagination_component.update(current: current_page, pages: pages, query_string: query_string)
66
+ page.pagination_component.update_attributes(current: current_page, pages: pages, query_string: query_string)
56
67
  end
57
68
  end
58
69
  end
@@ -3,17 +3,20 @@
3
3
  module TinyAdmin
4
4
  module Actions
5
5
  class Show < BasicAction
6
- def call(app:, context:, options:, actions:)
7
- fields_options = (options[:attributes] || []).each_with_object({}) do |field, result|
8
- result.merge!(field.is_a?(Hash) ? { field[:field] => field } : { field => { field: field } })
9
- end
6
+ def call(app:, context:, options:)
7
+ fields_options = attribute_options(options[:attributes])
8
+ repository = context.repository
10
9
  record = repository.find(context.reference)
11
10
  prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
12
- fields = repository.fields(options: fields_options)
13
11
 
14
12
  prepare_page(Views::Actions::Show) do |page|
15
- page.setup_record(record: record, fields: fields, prepare_record: prepare_record)
16
- page.update_attributes(actions: actions, title: repository.show_title(record))
13
+ page.update_attributes(
14
+ actions: context.actions,
15
+ fields: repository.fields(options: fields_options),
16
+ prepare_record: prepare_record,
17
+ record: record,
18
+ title: repository.show_title(record)
19
+ )
17
20
  end
18
21
  rescue Plugins::BaseRepository::RecordNotFound => _e
19
22
  prepare_page(options[:record_not_found_page] || Views::Pages::RecordNotFound)
@@ -4,6 +4,15 @@ module TinyAdmin
4
4
  class Context
5
5
  include Singleton
6
6
 
7
- attr_accessor :reference, :slug
7
+ attr_accessor :actions,
8
+ :navbar,
9
+ :pages,
10
+ :reference,
11
+ :repository,
12
+ :request,
13
+ :resources,
14
+ :router,
15
+ :settings,
16
+ :slug
8
17
  end
9
18
  end
@@ -4,16 +4,23 @@ module TinyAdmin
4
4
  class Field
5
5
  attr_reader :name, :options, :title, :type
6
6
 
7
- def initialize(type:, name:, title:, options: {})
7
+ def initialize(name:, title:, type:, options: {})
8
8
  @type = type
9
9
  @name = name
10
- @title = title || name
10
+ @title = title
11
11
  @options = options
12
12
  end
13
13
 
14
+ def apply_call_option(target)
15
+ messages = (options[:call] || '').split(',').map(&:strip)
16
+ messages.inject(target) { |result, msg| result&.send(msg) } if messages.any?
17
+ end
18
+
14
19
  class << self
15
- def create_field(name:, title:, type: nil, options: {})
16
- new(type: type, name: name, title: title, options: options)
20
+ def create_field(name:, title: nil, type: nil, options: {})
21
+ field_name = name.to_s
22
+ field_title = field_name.respond_to?(:humanize) ? field_name.humanize : field_name.tr('_', ' ').capitalize
23
+ new(name: field_name, title: title || field_title, type: type || :string, options: options || {})
17
24
  end
18
25
  end
19
26
  end
@@ -4,17 +4,11 @@ module TinyAdmin
4
4
  module Plugins
5
5
  class ActiveRecordRepository < BaseRepository
6
6
  def index_record_attrs(record, fields: nil)
7
- return record.attributes.transform_values(&:to_s) if !fields || fields.empty?
7
+ return record.attributes.transform_values(&:to_s) unless fields
8
8
 
9
- record.attributes.slice(*fields.keys).each_with_object({}) do |(key, value), result|
10
- field_data = fields[key]
11
- result[key] =
12
- if field_data[:converter] && field_data[:method]
13
- converter = Object.const_get(field_data[:converter])
14
- converter.send(field_data[:method], value)
15
- else
16
- value&.to_s
17
- end
9
+ fields.to_h do |name, field|
10
+ value = record.send(name)
11
+ [name, translate_value(value, field)]
18
12
  end
19
13
  end
20
14
 
@@ -24,25 +18,19 @@ module TinyAdmin
24
18
  end
25
19
 
26
20
  def fields(options: nil)
27
- opts = options || {}
28
- columns = model.columns
29
- if !opts.empty?
30
- extra_fields = opts.keys - model.column_names
31
- raise "Some requested fields are not available: #{extra_fields.join(', ')}" if extra_fields.any?
32
-
33
- columns = opts.keys.map { |field| columns.find { _1.name == field } }
34
- end
35
- columns.map do |column|
36
- name = column.name
37
- type = opts.dig(column.name, :type) || column.type
38
- TinyAdmin::Field.create_field(name: name, title: name.humanize, type: type, options: opts[name])
21
+ if options
22
+ types = model.columns.to_h { [_1.name, _1.type] }
23
+ options.each_with_object({}) do |(name, field_options), result|
24
+ result[name] = TinyAdmin::Field.create_field(name: name, type: types[name], options: field_options)
25
+ end
26
+ else
27
+ model.columns.each_with_object({}) do |column, result|
28
+ result[column.name] = TinyAdmin::Field.create_field(name: column.name, type: column.type)
29
+ end
39
30
  end
40
31
  end
41
32
 
42
- def show_record_attrs(record, fields: nil)
43
- attrs = !fields || fields.empty? ? record.attributes : record.attributes.slice(*fields.keys)
44
- attrs.transform_values(&:to_s)
45
- end
33
+ alias show_record_attrs index_record_attrs
46
34
 
47
35
  def show_title(record)
48
36
  "#{model} ##{record.id}"
@@ -54,10 +42,10 @@ module TinyAdmin
54
42
  raise BaseRepository::RecordNotFound, e.message
55
43
  end
56
44
 
57
- def list(page: 1, limit: 10, filters: nil, sort: ['id'])
58
- page_offset = page.positive? ? (page - 1) * limit : 0
59
- query = model.all.order(sort)
45
+ def list(page: 1, limit: 10, sort: nil, filters: nil)
46
+ query = sort ? model.all.order(sort) : model.all
60
47
  query = apply_filters(query, filters) if filters
48
+ page_offset = page.positive? ? (page - 1) * limit : 0
61
49
  records = query.offset(page_offset).limit(limit).to_a
62
50
  [records, query.count]
63
51
  end
@@ -10,6 +10,20 @@ module TinyAdmin
10
10
  def initialize(model)
11
11
  @model = model
12
12
  end
13
+
14
+ def translate_value(value, field)
15
+ if field[:method]
16
+ method, *options = field[:method].split(',').map(&:strip)
17
+ if field[:converter]
18
+ converter = Object.const_get(field[:converter])
19
+ converter.send(method, value, options: options || [])
20
+ else
21
+ Settings.instance.helper_class.send(method, value, options: options || [])
22
+ end
23
+ else
24
+ value&.to_s
25
+ end
26
+ end
13
27
  end
14
28
  end
15
29
  end