tiny_admin 0.4.0 → 0.6.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: 6eb4a218cc1a4ff6acc940c402d74202582d2092b39f920e6551e5b395da752e
4
- data.tar.gz: fb137625f9f5faa5b33dd5c7633a68eec7a4a2d2bb31db6f70758d6ed9ec3750
3
+ metadata.gz: 0dfe9b2fef29073201e228daf3a7d89003902f8d98108e58cb8245fdb9a533e3
4
+ data.tar.gz: 6634891a9bbc2585549a759cd061a25234b834ebdcc0b77fbbb9bd2297b0a626
5
5
  SHA512:
6
- metadata.gz: fd7800ab65bd0e92ce5646210f08823d031fdd217f5230bc99207a0e00375b068ad2337805ddce196ca317fe6f0df60ad3e209fc885e31c765c68c125f83d900
7
- data.tar.gz: f1df2bd938781a04c4af36c9653a7efffcc075cf72accd8f9fbaedbcc74286d860a1953bab159ec833c6a2cf5851a00633f555966b915fedec625af9ef2f85fa
6
+ metadata.gz: 78b49e3f8b7efe941e24734692db4e4baaa2159f58a10f49ce0563f8c4cfa5950f3e155184016faff12c415ff1a6632d4ee06cd61659a466d170d4b7954becf7
7
+ data.tar.gz: 16c4fe68dca49b84c4c8ca834c9e3c2ce2304f5a78dc378cfdde837462a96a4e00030e5098083d025ad9ab28060ea2aa581725434ffeec1582715e5fa725ff28
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Tiny Admin
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/tiny_admin.svg)](https://badge.fury.io/rb/tiny_admin) [![Linters](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml) [![Specs Rails 7.0](https://github.com/blocknotes/tiny_admin/actions/workflows/specs_rails_70.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/specs_rails_70.yml)
3
+ [![Gem Version](https://badge.fury.io/rb/tiny_admin.svg)](https://badge.fury.io/rb/tiny_admin) [![Linters](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml) [![Specs](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml)
4
4
 
5
5
  A compact and composable dashboard component for Ruby.
6
6
 
@@ -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.4'`
19
+ - Add to your Gemfile: `gem 'tiny_admin', '~> 0.6'`
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,38 @@ 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
+ - **Content**: define how to present page with inline content;
55
+ - **PageNotFound**: define how to present pages not found;
56
+ - **RecordNotFound**: define how to present record not found page;
57
+ - **SimpleAuthLogin**: define how to present the login form for SimpleAuth plugin;
58
+ - **Index**: define how to present a collection of items;
59
+ - **Show**: define how to present the details of an item.
55
60
 
56
61
  ### View components
57
62
 
58
63
  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.
64
+
65
+ - **FiltersForm**: define how to present the filters form in the resource collection pages;
66
+ - **Flash**: define how to present the flash messages;
67
+ - **Head**: define how to present the Head tag;
68
+ - **Navbar**: define how to present the navbar (the default one uses the Bootstrap structure);
69
+ - **Pagination**: define how to present the pagination of a collection.
64
70
 
65
71
  ## Configuration
66
72
 
@@ -70,9 +76,10 @@ See [extra](extra) folder for some usage examples.
70
76
  The following options are supported:
71
77
 
72
78
  `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;
79
+
80
+ - `title` (String): root section's title;
81
+ - `page` (String): a view object to render;
82
+ - `redirect` (String): alternative to _page_ option - redirects to a specific slug;
76
83
 
77
84
  Example:
78
85
 
@@ -88,9 +95,21 @@ root:
88
95
 
89
96
  `record_not_found` (String): a view object to render when a missing record is requested.
90
97
 
98
+ `style_links` (Array of hashes): list of styles files to include, properties:
99
+
100
+ - `href` (String): URL for the style file;
101
+ - `rel` (String): type of style file.
102
+
103
+ `scripts` (Array of hashes): list of scripts to include, properties:
104
+
105
+ - `src` (String): source URL for the script.
106
+
107
+ `extra_styles` (String): inline CSS styles.
108
+
91
109
  `authentication` (Hash): define the authentication method, properties:
92
- - `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
93
- - `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
110
+
111
+ - `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
112
+ - `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
94
113
 
95
114
  Example:
96
115
 
@@ -101,15 +120,32 @@ authentication:
101
120
  ```
102
121
 
103
122
  `sections` (Array of hashes): define the admin sections, properties:
104
- - `slug` (String): section reference identifier;
105
- - `name` (String): section's title;
106
- - `type` (String): the type of section: `url`, `page` or `resource`;
107
- - other properties depends on the section's type.
123
+
124
+ - `slug` (String): section reference identifier;
125
+ - `name` (String): section's title;
126
+ - `type` (String): the type of section: `content`, `page`, `resource` or `url`;
127
+ - other properties depends on the section's type.
128
+
129
+ For _content_ sections:
130
+
131
+ - `content` (String): the HTML content to present.
132
+
133
+ Example:
134
+
135
+ ```yml
136
+ slug: test-content
137
+ name: Test content
138
+ type: content
139
+ content: >
140
+ <h1>Test content!</h1>
141
+ <p>Some test content</p>
142
+ ```
108
143
 
109
144
  For _url_ sections:
110
- - `url` (String): the URL to load when clicking on the section's menu item;
111
- - `options` (Hash): properties:
112
- + `target` (String): link _target_ attributes (ex. `_blank`).
145
+
146
+ - `url` (String): the URL to load when clicking on the section's menu item;
147
+ - `options` (Hash): properties:
148
+ + `target` (String): link _target_ attributes (ex. `_blank`).
113
149
 
114
150
  Example:
115
151
 
@@ -123,7 +159,8 @@ options:
123
159
  ```
124
160
 
125
161
  For _page_ sections:
126
- - `page` (String): a view object to render.
162
+
163
+ - `page` (String): a view object to render.
127
164
 
128
165
  Example:
129
166
 
@@ -135,14 +172,15 @@ page: Admin::Stats
135
172
  ```
136
173
 
137
174
  For _resource_ sections:
138
- - `model` (String): the class to use to fetch the data on an item of a collection;
139
- - `repository` (String): the class to get the properties related to the model;
140
- - `index` (Hash): collection's action options;
141
- - `show` (Hash): detail's action options;
142
- - `collection_actions` (Array of hashes): custom collection's actions;
143
- - `member_actions` (Array of hashes): custom details's actions;
144
- - `only` (Array of strings): list of supported actions (ex. `index`);
145
- - `options` (Array of strings): resource options (ex. `hidden`).
175
+
176
+ - `model` (String): the class to use to fetch the data on an item of a collection;
177
+ - `repository` (String): the class to get the properties related to the model;
178
+ - `index` (Hash): collection's action options (see below);
179
+ - `show` (Hash): detail's action options (see below);
180
+ - `collection_actions` (Array of hashes): custom collection's actions;
181
+ - `member_actions` (Array of hashes): custom details's actions;
182
+ - `only` (Array of strings): list of supported actions (ex. `index`);
183
+ - `options` (Array of strings): resource options (ex. `hidden`).
146
184
 
147
185
  Example:
148
186
 
@@ -153,14 +191,72 @@ type: resource
153
191
  model: Post
154
192
  ```
155
193
 
156
- `style_links` (Array of hashes): list of styles files to include, properties:
157
- - `href` (String): URL for the style file;
158
- - `rel` (String): type of style file.
194
+ #### Resource index options
159
195
 
160
- `scripts` (Array of hashes): list of scripts to include, properties:
161
- - `src` (String): source URL for the script.
196
+ The Index hash supports the following options:
162
197
 
163
- `extra_styles` (String): inline CSS styles.
198
+ - `attributes` (Array): fields to expose in the resource list page;
199
+ - `filters` (Array): filter the current listing;
200
+ - `links` (Array): custom member actions to expose for each list's entry (defined in _member_actions_);
201
+ - `pagination` (Integer): max pages size;
202
+ - `sort` (Array): sort options to pass to the listing query.
203
+
204
+ Example:
205
+
206
+ ```yml
207
+ index:
208
+ sort:
209
+ - id DESC
210
+ pagination: 10
211
+ attributes:
212
+ - id
213
+ - author: call, name
214
+ - position: round, 1
215
+ - field: author_id
216
+ header: The author
217
+ link_to: authors
218
+ call: author, name
219
+ filters:
220
+ - title
221
+ - field: state
222
+ type: select
223
+ values:
224
+ - published
225
+ - draft
226
+ - archived
227
+ links:
228
+ - show
229
+ - author_posts
230
+ - csv_export
231
+ ```
232
+
233
+ #### Resource show options
234
+
235
+ The Show hash supports the following options:
236
+
237
+ - `attributes` (Array): fields to expose in the resource details page.
238
+
239
+ Example:
240
+
241
+ ```yml
242
+ show:
243
+ attributes:
244
+ # Expose the id column
245
+ - id
246
+ # Expose the title column, calling `downcase` support method
247
+ - title: downcase
248
+ # Expose the category column, calling `upcase` support method
249
+ - category: upcase
250
+ # Expose the position column, calling `format` support method with argument %f
251
+ - position: format, %f
252
+ # Expose the position created_at, calling `strftime` support method with argument %Y%m%d %H:%M
253
+ - created_at: strftime, %Y%m%d %H:%M
254
+ # Expose the author_id column, with a custom header label, linked to authors section and calling author.name to get the value
255
+ - field: author_id
256
+ header: The author
257
+ link_to: authors
258
+ call: author, name
259
+ ```
164
260
 
165
261
  ### Sample
166
262
 
@@ -6,6 +6,7 @@ module TinyAdmin
6
6
  attr_reader :current_page,
7
7
  :fields_options,
8
8
  :filters_list,
9
+ :links,
9
10
  :pagination,
10
11
  :pages,
11
12
  :params,
@@ -13,18 +14,19 @@ module TinyAdmin
13
14
  :repository,
14
15
  :sort
15
16
 
16
- def call(app:, context:, options:, actions:)
17
+ def call(app:, context:, options:)
17
18
  evaluate_options(options)
18
19
  fields = repository.fields(options: fields_options)
19
20
  filters = prepare_filters(fields, filters_list)
20
21
  records, total_count = repository.list(page: current_page, limit: pagination, filters: filters, sort: sort)
21
22
 
22
23
  prepare_page(Views::Actions::Index) do |page|
23
- setup_pagination(page, settings.components[:pagination], total_count: total_count)
24
+ setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: total_count)
24
25
  page.update_attributes(
25
- actions: actions,
26
+ actions: context.actions,
26
27
  fields: fields,
27
28
  filters: filters,
29
+ links: links,
28
30
  prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
29
31
  records: records,
30
32
  title: repository.index_title
@@ -40,7 +42,8 @@ module TinyAdmin
40
42
  @repository = context.repository
41
43
  @filters_list = options[:filters]
42
44
  @pagination = options[:pagination] || 10
43
- @sort = options[:sort] || ['id']
45
+ @sort = options[:sort]
46
+ @links = options[:links]
44
47
 
45
48
  @current_page = (params['p'] || 1).to_i
46
49
  @query_string = params_to_s(params.except('p'))
@@ -60,7 +63,7 @@ module TinyAdmin
60
63
  return if pages <= 1 || !pagination_component_class
61
64
 
62
65
  page.pagination_component = pagination_component_class.new
63
- 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)
64
67
  end
65
68
  end
66
69
  end
@@ -3,18 +3,17 @@
3
3
  module TinyAdmin
4
4
  module Actions
5
5
  class Show < BasicAction
6
- attr_reader :repository
7
-
8
- def call(app:, context:, options:, actions:)
9
- @repository = context.repository
6
+ def call(app:, context:, options:)
10
7
  fields_options = attribute_options(options[:attributes])
8
+ repository = context.repository
11
9
  record = repository.find(context.reference)
10
+ prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
12
11
 
13
12
  prepare_page(Views::Actions::Show) do |page|
14
13
  page.update_attributes(
15
- actions: actions,
14
+ actions: context.actions,
16
15
  fields: repository.fields(options: fields_options),
17
- prepare_record: ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) },
16
+ prepare_record: prepare_record,
18
17
  record: record,
19
18
  title: repository.show_title(record)
20
19
  )
@@ -5,7 +5,7 @@ module TinyAdmin
5
5
  route do |r|
6
6
  r.get 'unauthenticated' do
7
7
  if current_user
8
- r.redirect settings.root_path
8
+ r.redirect TinyAdmin.settings.root_path
9
9
  else
10
10
  render_login
11
11
  end
@@ -17,14 +17,14 @@ module TinyAdmin
17
17
 
18
18
  r.get 'logout' do
19
19
  logout_user
20
- r.redirect settings.root_path
20
+ r.redirect TinyAdmin.settings.root_path
21
21
  end
22
22
  end
23
23
 
24
24
  private
25
25
 
26
26
  def render_login(notices: nil, warnings: nil, errors: nil)
27
- page = prepare_page(settings.authentication[:login], options: %i[no_menu compact_layout])
27
+ page = prepare_page(TinyAdmin.settings.authentication[:login], options: %i[no_menu compact_layout])
28
28
  page.messages = {
29
29
  notices: notices || flash['notices'],
30
30
  warnings: warnings || flash['warnings'],
@@ -6,7 +6,7 @@ module TinyAdmin
6
6
 
7
7
  class << self
8
8
  def authentication_plugin
9
- plugin = TinyAdmin::Settings.instance.authentication&.dig(:plugin)
9
+ plugin = TinyAdmin.settings.authentication&.dig(:plugin)
10
10
  plugin_class = plugin.is_a?(String) ? Object.const_get(plugin) : plugin
11
11
  plugin_class || TinyAdmin::Plugins::NoAuth
12
12
  end
@@ -17,8 +17,8 @@ module TinyAdmin
17
17
  plugin :render, engine: 'html'
18
18
  plugin :sessions, secret: SecureRandom.hex(64)
19
19
 
20
- plugin authentication_plugin, TinyAdmin::Settings.instance.authentication
20
+ plugin authentication_plugin, TinyAdmin.settings.authentication
21
21
 
22
- not_found { prepare_page(settings.page_not_found).call }
22
+ not_found { prepare_page(TinyAdmin.settings.page_not_found).call }
23
23
  end
24
24
  end
@@ -4,6 +4,15 @@ module TinyAdmin
4
4
  class Context
5
5
  include Singleton
6
6
 
7
- attr_accessor :reference, :repository, :request, :router, :settings, :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
@@ -7,19 +7,20 @@ module TinyAdmin
7
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
20
  def create_field(name:, title: nil, type: nil, options: {})
16
21
  field_name = name.to_s
17
- new(
18
- name: field_name,
19
- title: title || field_name.respond_to?(:humanize) ? field_name.humanize : field_name.tr('_', ' ').capitalize,
20
- type: type || :string,
21
- options: options
22
- )
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 || {})
23
24
  end
24
25
  end
25
26
  end
@@ -20,12 +20,12 @@ module TinyAdmin
20
20
  def fields(options: nil)
21
21
  if options
22
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)
23
+ options.to_h do |name, field_options|
24
+ [name, TinyAdmin::Field.create_field(name: name, type: types[name], options: field_options)]
25
25
  end
26
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)
27
+ model.columns.to_h do |column|
28
+ [column.name, TinyAdmin::Field.create_field(name: column.name, type: column.type)]
29
29
  end
30
30
  end
31
31
  end
@@ -42,8 +42,12 @@ module TinyAdmin
42
42
  raise BaseRepository::RecordNotFound, e.message
43
43
  end
44
44
 
45
- def list(page: 1, limit: 10, sort: ['id'], filters: nil)
46
- query = model.all.order(sort)
45
+ def collection
46
+ model.all
47
+ end
48
+
49
+ def list(page: 1, limit: 10, sort: nil, filters: nil)
50
+ query = sort ? collection.order(sort) : collection
47
51
  query = apply_filters(query, filters) if filters
48
52
  page_offset = page.positive? ? (page - 1) * limit : 0
49
53
  records = query.offset(page_offset).limit(limit).to_a
@@ -18,7 +18,7 @@ module TinyAdmin
18
18
  converter = Object.const_get(field[:converter])
19
19
  converter.send(method, value, options: options || [])
20
20
  else
21
- Settings.instance.helper_class.send(method, value, options: options || [])
21
+ TinyAdmin.settings.helper_class.send(method, value, options: options || [])
22
22
  end
23
23
  else
24
24
  value&.to_s
@@ -2,10 +2,11 @@
2
2
 
3
3
  module TinyAdmin
4
4
  class Router < BasicApp
5
+ TinyAdmin.settings.load_settings
6
+
5
7
  route do |r|
6
- context.settings = TinyAdmin::Settings.instance
7
- context.settings.load_settings
8
8
  context.router = r
9
+ context.settings = TinyAdmin.settings
9
10
 
10
11
  r.on 'auth' do
11
12
  context.slug = nil
@@ -26,14 +27,14 @@ module TinyAdmin
26
27
 
27
28
  r.post '' do
28
29
  context.slug = nil
29
- r.redirect settings.root_path
30
+ r.redirect TinyAdmin.settings.root_path
30
31
  end
31
32
 
32
- context.settings.pages.each do |slug, data|
33
- setup_page_route(r, slug, data)
33
+ context.pages.each do |slug, page_data|
34
+ setup_page_route(r, slug, page_data)
34
35
  end
35
36
 
36
- context.settings.resources.each do |slug, options|
37
+ context.resources.each do |slug, options|
37
38
  setup_resource_routes(r, slug, options: options || {})
38
39
  end
39
40
 
@@ -51,19 +52,21 @@ module TinyAdmin
51
52
 
52
53
  def root_route(router)
53
54
  context.slug = nil
54
- if settings.root[:redirect]
55
- router.redirect route_for(settings.root[:redirect])
55
+ if TinyAdmin.settings.root[:redirect]
56
+ router.redirect route_for(TinyAdmin.settings.root[:redirect])
56
57
  else
57
- page = settings.root[:page]
58
+ page = TinyAdmin.settings.root[:page]
58
59
  page_class = page.is_a?(String) ? Object.const_get(page) : page
59
60
  render_page prepare_page(page_class)
60
61
  end
61
62
  end
62
63
 
63
- def setup_page_route(router, slug, page_class)
64
+ def setup_page_route(router, slug, page_data)
64
65
  router.get slug do
65
66
  context.slug = slug
66
- render_page prepare_page(page_class)
67
+ page = prepare_page(page_data[:class])
68
+ page.update_attributes(content: page_data[:content]) if page_data[:content]
69
+ render_page page
67
70
  end
68
71
  end
69
72
 
@@ -88,19 +91,19 @@ module TinyAdmin
88
91
  )
89
92
 
90
93
  # Index
91
- actions = options[:only]
92
- if !actions || actions.include?(:index) || actions.include?('index')
94
+ if options[:only].include?(:index) || options[:only].include?('index')
93
95
  router.is do
96
+ context.actions = custom_actions
94
97
  context.request = request
95
98
  index_action = TinyAdmin::Actions::Index.new
96
- render_page index_action.call(app: self, context: context, options: action_options, actions: custom_actions)
99
+ render_page index_action.call(app: self, context: context, options: action_options)
97
100
  end
98
101
  end
99
102
  end
100
103
 
101
104
  def setup_member_routes(router, options:)
102
105
  context.repository = options[:repository].new(options[:model])
103
- action_options = (options[:show] || {}).merge(record_not_found_page: settings.record_not_found)
106
+ action_options = (options[:show] || {}).merge(record_not_found_page: TinyAdmin.settings.record_not_found)
104
107
 
105
108
  router.on String do |reference|
106
109
  context.reference = reference
@@ -114,12 +117,12 @@ module TinyAdmin
114
117
  )
115
118
 
116
119
  # Show
117
- actions = options[:only]
118
- if !actions || actions.include?(:show) || actions.include?('show')
120
+ if options[:only].include?(:show) || options[:only].include?('show')
119
121
  router.is do
122
+ context.actions = custom_actions
120
123
  context.request = request
121
124
  show_action = TinyAdmin::Actions::Show.new
122
- render_page show_action.call(app: self, context: context, options: action_options, actions: custom_actions)
125
+ render_page show_action.call(app: self, context: context, options: action_options)
123
126
  end
124
127
  end
125
128
  end
@@ -132,6 +135,7 @@ module TinyAdmin
132
135
  action_class = action.is_a?(String) ? Object.const_get(action) : action
133
136
 
134
137
  router.get action_slug.to_s do
138
+ context.actions = {}
135
139
  context.request = request
136
140
  custom_action = action_class.new
137
141
  render_page custom_action.call(app: self, context: context, options: options)
@@ -12,6 +12,7 @@ module TinyAdmin
12
12
  %i[components head] => Views::Components::Head,
13
13
  %i[components navbar] => Views::Components::Navbar,
14
14
  %i[components pagination] => Views::Components::Pagination,
15
+ %i[content_page] => Views::Pages::Content,
15
16
  %i[helper_class] => Support,
16
17
  %i[page_not_found] => Views::Pages::PageNotFound,
17
18
  %i[record_not_found] => Views::Pages::RecordNotFound,
@@ -21,28 +22,40 @@ module TinyAdmin
21
22
  %i[root title] => 'TinyAdmin'
22
23
  }.freeze
23
24
 
24
- attr_accessor :authentication,
25
- :components,
26
- :extra_styles,
27
- :helper_class,
28
- :navbar,
29
- :page_not_found,
30
- :record_not_found,
31
- :repository,
32
- :root,
33
- :root_path,
34
- :sections,
35
- :scripts,
36
- :style_links
37
-
38
- attr_reader :pages, :resources
39
-
40
- def [](key)
41
- send(key)
25
+ OPTIONS = %i[
26
+ authentication
27
+ components
28
+ content_page
29
+ extra_styles
30
+ helper_class
31
+ page_not_found
32
+ record_not_found
33
+ repository
34
+ root
35
+ root_path
36
+ sections
37
+ scripts
38
+ style_links
39
+ ].freeze
40
+
41
+ OPTIONS.each do |option|
42
+ define_method(option) do
43
+ self[option]
44
+ end
45
+
46
+ define_method("#{option}=") do |value|
47
+ self[option] = value
48
+ end
49
+ end
50
+
51
+ def [](*path)
52
+ key, option = fetch_setting(path)
53
+ option[key]
42
54
  end
43
55
 
44
- def []=(key, value)
45
- send("#{key}=", value)
56
+ def []=(*path, value)
57
+ key, option = fetch_setting(path)
58
+ option[key] = value
46
59
  convert_value(key, value)
47
60
  end
48
61
 
@@ -57,43 +70,29 @@ module TinyAdmin
57
70
  end
58
71
  end
59
72
 
60
- @pages ||= {}
61
- @resources ||= {}
62
- @sections ||= []
63
- @root_path = '/' if @root_path == ''
64
- if @authentication[:plugin] == Plugins::SimpleAuth
65
- @authentication[:logout] ||= ['logout', "#{root_path}/auth/logout"]
73
+ context.pages ||= {}
74
+ context.resources ||= {}
75
+ self.sections ||= []
76
+ self.root_path = '/' if root_path == ''
77
+
78
+ if authentication[:plugin] <= Plugins::SimpleAuth
79
+ authentication[:logout] ||= { name: 'logout', path: "#{root_path}/auth/logout" }
66
80
  end
67
- @navbar = prepare_navbar(sections, logout: authentication[:logout])
81
+ context.navbar = prepare_navbar(sections, logout: authentication[:logout])
68
82
  end
69
83
 
70
- def prepare_navbar(sections, logout:)
71
- items = sections.each_with_object({}) do |section, list|
72
- slug = section[:slug]
73
- case section[:type]&.to_sym
74
- when :url
75
- list[slug] = [section[:name], section[:url], section[:options]]
76
- when :page
77
- page = section[:page]
78
- pages[slug] = page.is_a?(String) ? Object.const_get(page) : page
79
- list[slug] = [section[:name], route_for(slug)]
80
- when :resource
81
- repository = section[:repository] || settings.repository
82
- resources[slug] = {
83
- model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
84
- repository: repository.is_a?(String) ? Object.const_get(repository) : repository
85
- }
86
- resources[slug].merge! section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
87
- hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
88
- list[slug] = [section[:name], route_for(slug)] unless hidden
89
- end
90
- end
91
- items['auth/logout'] = logout if logout
92
- items
84
+ def reset!
85
+ @options = {}
93
86
  end
94
87
 
95
88
  private
96
89
 
90
+ def fetch_setting(path)
91
+ @options ||= {}
92
+ *parts, last = path.map(&:to_sym)
93
+ [last, parts.inject(@options) { |result, part| result[part] ||= {} }]
94
+ end
95
+
97
96
  def convert_value(key, value)
98
97
  if value.is_a?(Hash)
99
98
  value.each_key do |key2|
@@ -106,5 +105,59 @@ module TinyAdmin
106
105
  self[key] = Object.const_get(self[key])
107
106
  end
108
107
  end
108
+
109
+ def prepare_navbar(sections, logout:)
110
+ items = sections.each_with_object({}) do |section, list|
111
+ unless section.is_a?(Hash)
112
+ section_class = section.is_a?(String) ? Object.const_get(section) : section
113
+ next unless section_class.respond_to?(:to_h)
114
+
115
+ section = section_class.to_h
116
+ end
117
+
118
+ slug = section[:slug].to_s
119
+ case section[:type]&.to_sym
120
+ when :content
121
+ list[slug] = add_content_section(slug, section)
122
+ when :page
123
+ list[slug] = add_page_section(slug, section)
124
+ when :resource
125
+ list[slug] = add_resource_section(slug, section)
126
+ when :url
127
+ list[slug] = add_url_section(slug, section)
128
+ end
129
+ end
130
+ items['auth/logout'] = logout if logout
131
+ items
132
+ end
133
+
134
+ def add_content_section(slug, section)
135
+ context.pages[slug] = { class: content_page, content: section[:content] }
136
+ { name: section[:name], path: route_for(slug), class: content_page }
137
+ end
138
+
139
+ def add_page_section(slug, section)
140
+ page = section[:page]
141
+ page_class = page.is_a?(String) ? Object.const_get(page) : page
142
+ context.pages[slug] = { class: page_class }
143
+ { name: section[:name], path: route_for(slug), class: page_class }
144
+ end
145
+
146
+ def add_resource_section(slug, section)
147
+ repo = section[:repository] || repository
148
+ context.resources[slug] = {
149
+ model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
150
+ repository: repo.is_a?(String) ? Object.const_get(repo) : repo
151
+ }
152
+ resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
153
+ resource_options[:only] ||= %i[index show]
154
+ context.resources[slug].merge!(resource_options)
155
+ hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
156
+ { name: section[:name], path: route_for(slug) } unless hidden
157
+ end
158
+
159
+ def add_url_section(_slug, section)
160
+ section.slice(:name, :options).tap { _1[:path] = section[:url] }
161
+ end
109
162
  end
110
163
  end
@@ -4,7 +4,7 @@ module TinyAdmin
4
4
  class Support
5
5
  class << self
6
6
  def call(value, options: [])
7
- value && options&.any? ? options.inject(value) { |result, message| result&.send(message) } : value
7
+ options.inject(value) { |result, message| result&.send(message) } if value && options&.any?
8
8
  end
9
9
 
10
10
  def downcase(value, options: [])
@@ -12,7 +12,7 @@ module TinyAdmin
12
12
  end
13
13
 
14
14
  def format(value, options: [])
15
- value && options&.any? ? Kernel.format(options.first, value) : value
15
+ Kernel.format(options.first, value) if value && options&.any?
16
16
  end
17
17
 
18
18
  def round(value, options: [])
@@ -20,11 +20,11 @@ module TinyAdmin
20
20
  end
21
21
 
22
22
  def strftime(value, options: [])
23
- value ? value.strftime(options&.first || '%Y-%m-%d %H:%M') : ''
23
+ value&.strftime(options&.first || '%Y-%m-%d %H:%M')
24
24
  end
25
25
 
26
26
  def to_date(value, options: [])
27
- value ? value.to_date.to_s : value
27
+ value.to_date.to_s if value
28
28
  end
29
29
 
30
30
  def upcase(value, options: [])
@@ -5,7 +5,8 @@ module TinyAdmin
5
5
  def params_to_s(params)
6
6
  list = params.each_with_object([]) do |(param, value), result|
7
7
  if value.is_a?(Hash)
8
- result.concat(value.map { |k, v| "#{param}[#{k}]=#{v}" })
8
+ values = value.map { |key, val| "#{param}[#{key}]=#{val}" }
9
+ result.concat(values)
9
10
  else
10
11
  result.push(["#{param}=#{value}"])
11
12
  end
@@ -16,32 +17,34 @@ module TinyAdmin
16
17
  def prepare_page(page_class, options: nil)
17
18
  page_class.new.tap do |page|
18
19
  page.options = options
19
- page.head_component = settings.components[:head]&.new
20
- page.flash_component = settings.components[:flash]&.new
21
- page.navbar_component = settings.components[:navbar]&.new(
20
+ page.head_component = TinyAdmin.settings.components[:head]&.new
21
+ page.flash_component = TinyAdmin.settings.components[:flash]&.new
22
+ page.navbar_component = TinyAdmin.settings.components[:navbar]&.new
23
+ page.navbar_component&.update_attributes(
22
24
  current_slug: context&.slug,
23
- root_path: settings.root_path,
24
- root_title: settings.root[:title],
25
- items: options&.include?(:no_menu) ? [] : settings.navbar
25
+ root_path: TinyAdmin.settings.root_path,
26
+ root_title: TinyAdmin.settings.root[:title],
27
+ items: options&.include?(:no_menu) ? [] : context&.navbar
26
28
  )
27
-
28
29
  yield(page) if block_given?
29
30
  end
30
31
  end
31
32
 
32
33
  def route_for(section, reference: nil, action: nil, query: nil)
33
- root_path = settings.root_path == '/' ? nil : settings.root_path
34
+ root_path = TinyAdmin.settings.root_path == '/' ? nil : TinyAdmin.settings.root_path
34
35
  route = [root_path, section, reference, action].compact.join("/")
35
36
  route << "?#{query}" if query
36
37
  route[0] == '/' ? route : route.prepend('/')
37
38
  end
38
39
 
39
- def context
40
- TinyAdmin::Context.instance
40
+ def to_label(string)
41
+ return '' unless string
42
+
43
+ string.respond_to?(:humanize) ? string.humanize : string.tr('_', ' ').capitalize
41
44
  end
42
45
 
43
- def settings
44
- TinyAdmin::Settings.instance
46
+ def context
47
+ TinyAdmin::Context.instance
45
48
  end
46
49
  end
47
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinyAdmin
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -4,7 +4,7 @@ module TinyAdmin
4
4
  module Views
5
5
  module Actions
6
6
  class Index < DefaultLayout
7
- attr_accessor :actions, :fields, :filters, :pagination_component, :prepare_record, :records
7
+ attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records
8
8
 
9
9
  def template
10
10
  super do
@@ -30,8 +30,9 @@ module TinyAdmin
30
30
 
31
31
  if filters&.any?
32
32
  div(class: 'col-3') {
33
- filters_form_attrs = { section_path: route_for(context.slug), filters: filters }
34
- render TinyAdmin::Views::Components::FiltersForm.new(**filters_form_attrs)
33
+ filters_form = TinyAdmin::Views::Components::FiltersForm.new
34
+ filters_form.update_attributes(section_path: route_for(context.slug), filters: filters)
35
+ render filters_form
35
36
  }
36
37
  end
37
38
  }
@@ -65,16 +66,33 @@ module TinyAdmin
65
66
  field = fields[key]
66
67
  td(class: "field-value-#{field.name} field-value-type-#{field.type}") {
67
68
  if field.options && field.options[:link_to]
68
- messages = (field.options[:call] || '').split(',').map(&:strip)
69
- label = messages.any? ? messages.inject(record) { |result, msg| result&.send(msg) } : value
70
- a(href: route_for(field.options[:link_to], reference: value)) { label }
69
+ a(href: route_for(field.options[:link_to], reference: value)) {
70
+ field.apply_call_option(record) || value
71
+ }
71
72
  else
72
73
  value
73
74
  end
74
75
  }
75
76
  end
76
- td(class: 'actions') {
77
- a(href: route_for(context.slug, reference: record.id)) { 'show' }
77
+
78
+ td(class: 'actions p-1') {
79
+ div(class: 'btn-group btn-group-sm') {
80
+ link_class = 'btn btn-outline-secondary'
81
+ if links
82
+ links.each do |link|
83
+ whitespace
84
+ if link == 'show'
85
+ a(href: route_for(context.slug, reference: record.id), class: link_class) { 'show' }
86
+ else
87
+ a(href: route_for(context.slug, reference: record.id, action: link), class: link_class) {
88
+ to_label(link)
89
+ }
90
+ end
91
+ end
92
+ else
93
+ a(href: route_for(context.slug, reference: record.id), class: link_class) { 'show' }
94
+ end
95
+ }
78
96
  }
79
97
  }
80
98
  end
@@ -86,8 +104,9 @@ module TinyAdmin
86
104
  (actions || {}).each do |action, action_class|
87
105
  li(class: 'nav-item mx-1') {
88
106
  href = route_for(context.slug, action: action)
89
- title = action_class.respond_to?(:title) ? action_class.title : action
90
- a(href: href, class: 'nav-link btn btn-outline-secondary') { title }
107
+ a(href: href, class: 'nav-link btn btn-outline-secondary') {
108
+ action_class.respond_to?(:title) ? action_class.title : action
109
+ }
91
110
  }
92
111
  end
93
112
  }
@@ -25,10 +25,10 @@ module TinyAdmin
25
25
  div(class: 'field-header col-2') { field.options[:header] || field.title }
26
26
  end
27
27
  div(class: 'field-value col-10') {
28
- if field.options && field.options[:link_to]
29
- messages = (field.options[:call] || '').split(',').map(&:strip)
30
- label = messages.any? ? messages.inject(record) { |result, msg| result&.send(msg) } : value
31
- a(href: route_for(field.options[:link_to], reference: value)) { label }
28
+ if field.options[:link_to]
29
+ a(href: route_for(field.options[:link_to], reference: value)) {
30
+ field.apply_call_option(record) || value
31
+ }
32
32
  else
33
33
  value
34
34
  end
@@ -46,8 +46,9 @@ module TinyAdmin
46
46
  (actions || {}).each do |action, action_class|
47
47
  li(class: 'nav-item mx-1') {
48
48
  href = route_for(context.slug, reference: context.reference, action: action)
49
- title = action_class.respond_to?(:title) ? action_class.title : action
50
- a(href: href, class: 'nav-link btn btn-outline-secondary') { title }
49
+ a(href: href, class: 'nav-link btn btn-outline-secondary') {
50
+ action_class.respond_to?(:title) ? action_class.title : action
51
+ }
51
52
  }
52
53
  end
53
54
  }
@@ -5,6 +5,8 @@ module TinyAdmin
5
5
  class BasicLayout < Phlex::HTML
6
6
  include Utils
7
7
 
8
+ attr_accessor :content
9
+
8
10
  def update_attributes(attributes)
9
11
  attributes.each do |key, value|
10
12
  send("#{key}=", value)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinyAdmin
4
+ module Views
5
+ module Components
6
+ class BasicComponent < Phlex::HTML
7
+ def update_attributes(attributes)
8
+ attributes.each do |key, value|
9
+ send("#{key}=", value)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,13 +3,8 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  module Components
6
- class FiltersForm < Phlex::HTML
7
- attr_reader :filters, :section_path
8
-
9
- def initialize(section_path:, filters:)
10
- @section_path = section_path
11
- @filters = filters
12
- end
6
+ class FiltersForm < BasicComponent
7
+ attr_accessor :filters, :section_path
13
8
 
14
9
  def template
15
10
  form(class: 'form_filters', method: 'get') {
@@ -3,24 +3,21 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  module Components
6
- class Flash < Phlex::HTML
7
- attr_reader :errors, :notices, :warnings
6
+ class Flash < BasicComponent
7
+ attr_accessor :messages
8
8
 
9
9
  def template
10
+ @messages ||= {}
11
+ notices = messages[:notices]
12
+ warnings = messages[:warnings]
13
+ errors = messages[:errors]
14
+
10
15
  div(class: 'flash') {
11
16
  div(class: 'notices alert alert-success', role: 'alert') { notices.join(', ') } if notices&.any?
12
17
  div(class: 'notices alert alert-warning', role: 'alert') { warnings.join(', ') } if warnings&.any?
13
18
  div(class: 'notices alert alert-danger', role: 'alert') { errors.join(', ') } if errors&.any?
14
19
  }
15
20
  end
16
-
17
- def update(messages:)
18
- return unless messages
19
-
20
- @notices = messages[:notices]
21
- @warnings = messages[:warnings]
22
- @errors = messages[:errors]
23
- end
24
21
  end
25
22
  end
26
23
  end
@@ -3,8 +3,8 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  module Components
6
- class Head < Phlex::HTML
7
- attr_reader :extra_styles, :page_title, :style_links
6
+ class Head < BasicComponent
7
+ attr_accessor :extra_styles, :page_title, :style_links
8
8
 
9
9
  def template
10
10
  head {
@@ -19,12 +19,6 @@ module TinyAdmin
19
19
  style { extra_styles } if extra_styles
20
20
  }
21
21
  end
22
-
23
- def update(page_title, style_links: [], extra_styles: nil)
24
- @page_title = page_title
25
- @style_links = style_links
26
- @extra_styles = extra_styles
27
- end
28
22
  end
29
23
  end
30
24
  end
@@ -3,33 +3,34 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  module Components
6
- class Navbar < Phlex::HTML
7
- attr_reader :current_slug, :items, :root_path, :root_title
8
-
9
- def initialize(root_path:, root_title:, items: [], current_slug: nil)
10
- @root_path = root_path
11
- @root_title = root_title
12
- @items = items || []
13
- @current_slug = current_slug
14
- end
6
+ class Navbar < BasicComponent
7
+ attr_accessor :current_slug, :items, :root_path, :root_title
15
8
 
16
9
  def template
17
10
  nav(class: 'navbar navbar-expand-lg') {
18
11
  div(class: 'container') {
19
12
  a(class: 'navbar-brand', href: root_path) { root_title }
20
- button(class: 'navbar-toggler', type: 'button', 'data-bs-toggle' => 'collapse', 'data-bs-target' => '#navbarNav', 'aria-controls' => 'navbarNav', 'aria-expanded' => 'false', 'aria-label' => 'Toggle navigation') {
13
+ button(
14
+ class: 'navbar-toggler',
15
+ type: 'button',
16
+ 'data-bs-toggle' => 'collapse',
17
+ 'data-bs-target' => '#navbarNav',
18
+ 'aria-controls' => 'navbarNav',
19
+ 'aria-expanded' => 'false',
20
+ 'aria-label' => 'Toggle navigation'
21
+ ) {
21
22
  span(class: 'navbar-toggler-icon')
22
23
  }
23
24
  div(class: 'collapse navbar-collapse', id: 'navbarNav') {
24
25
  ul(class: 'navbar-nav') {
25
- items.each do |slug, (name, path, options)|
26
+ items.each do |slug, item|
26
27
  classes = %w[nav-link]
27
28
  classes << 'active' if slug == current_slug
28
- link_attributes = { class: classes.join(' '), href: path, 'aria-current' => 'page' }
29
- link_attributes.merge!(options) if options
29
+ link_attributes = { class: classes.join(' '), href: item[:path], 'aria-current' => 'page' }
30
+ link_attributes.merge!(item[:options]) if item[:options]
30
31
 
31
32
  li(class: 'nav-item') {
32
- a(**link_attributes) { name }
33
+ a(**link_attributes) { item[:name] }
33
34
  }
34
35
  end
35
36
  }
@@ -3,8 +3,8 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  module Components
6
- class Pagination < Phlex::HTML
7
- attr_reader :current, :pages, :query_string
6
+ class Pagination < BasicComponent
7
+ attr_accessor :current, :pages, :query_string
8
8
 
9
9
  def template
10
10
  div(class: 'pagination-div') {
@@ -25,12 +25,6 @@ module TinyAdmin
25
25
  }
26
26
  end
27
27
 
28
- def update(current:, pages:, query_string:)
29
- @current = current
30
- @pages = pages
31
- @query_string = query_string
32
- end
33
-
34
28
  private
35
29
 
36
30
  def pages_range(range, with_dots: false)
@@ -6,8 +6,9 @@ module TinyAdmin
6
6
  attr_accessor :flash_component, :head_component, :messages, :navbar_component, :options, :title
7
7
 
8
8
  def template(&block)
9
- flash_component&.update(messages: messages)
10
- head_component&.update(title, style_links: style_links, extra_styles: settings.extra_styles)
9
+ extra_styles = TinyAdmin.settings.extra_styles
10
+ flash_component&.messages = messages
11
+ head_component&.update_attributes(page_title: title, style_links: style_links, extra_styles: extra_styles)
11
12
 
12
13
  doctype
13
14
  html {
@@ -48,7 +49,7 @@ module TinyAdmin
48
49
  end
49
50
 
50
51
  def style_links
51
- settings.style_links || [
52
+ TinyAdmin.settings.style_links || [
52
53
  # Bootstrap CDN
53
54
  {
54
55
  href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css',
@@ -60,7 +61,7 @@ module TinyAdmin
60
61
  end
61
62
 
62
63
  def render_scripts
63
- (settings.scripts || []).each do |script_attrs|
64
+ (TinyAdmin.settings.scripts || []).each do |script_attrs|
64
65
  script(**script_attrs)
65
66
  end
66
67
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinyAdmin
4
+ module Views
5
+ module Pages
6
+ class Content < DefaultLayout
7
+ def template
8
+ super do
9
+ div(class: 'content') {
10
+ unsafe_raw(content)
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/tiny_admin.rb CHANGED
@@ -12,15 +12,20 @@ loader.setup
12
12
 
13
13
  module TinyAdmin
14
14
  def configure(&block)
15
- block&.call(TinyAdmin::Settings.instance) || TinyAdmin::Settings.instance
15
+ block&.call(settings) || settings
16
16
  end
17
17
 
18
18
  def configure_from_file(file)
19
+ settings.reset!
19
20
  config = YAML.load_file(file, symbolize_names: true)
20
21
  config.each do |key, value|
21
- TinyAdmin::Settings.instance[key] = value
22
+ settings[key] = value
22
23
  end
23
24
  end
24
25
 
25
- module_function :configure, :configure_from_file
26
+ def settings
27
+ TinyAdmin::Settings.instance
28
+ end
29
+
30
+ module_function :configure, :configure_from_file, :settings
26
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-18 00:00:00.000000000 Z
11
+ date: 2023-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex
@@ -94,12 +94,14 @@ files:
94
94
  - lib/tiny_admin/views/actions/index.rb
95
95
  - lib/tiny_admin/views/actions/show.rb
96
96
  - lib/tiny_admin/views/basic_layout.rb
97
+ - lib/tiny_admin/views/components/basic_component.rb
97
98
  - lib/tiny_admin/views/components/filters_form.rb
98
99
  - lib/tiny_admin/views/components/flash.rb
99
100
  - lib/tiny_admin/views/components/head.rb
100
101
  - lib/tiny_admin/views/components/navbar.rb
101
102
  - lib/tiny_admin/views/components/pagination.rb
102
103
  - lib/tiny_admin/views/default_layout.rb
104
+ - lib/tiny_admin/views/pages/content.rb
103
105
  - lib/tiny_admin/views/pages/page_not_found.rb
104
106
  - lib/tiny_admin/views/pages/record_not_found.rb
105
107
  - lib/tiny_admin/views/pages/root.rb