tiny_admin 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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