tiny_admin 0.5.0 → 0.6.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: c3ac2be067ca35c35f40c677c5f9461c5f83e366488a48e6ab50a888f0f22317
4
- data.tar.gz: 2289dd33940174e8e614260f96273967ade51f75dc67a04d5e639415c984e65b
3
+ metadata.gz: 0dfe9b2fef29073201e228daf3a7d89003902f8d98108e58cb8245fdb9a533e3
4
+ data.tar.gz: 6634891a9bbc2585549a759cd061a25234b834ebdcc0b77fbbb9bd2297b0a626
5
5
  SHA512:
6
- metadata.gz: f4f80d86ac6b30bc29cf8e2f1efe480edefe84907bf5e9b7a3a8c3b3c3750ff59a1f9e609c87090105c0cd31fa22df7aa913858911a455d918633269c27317e7
7
- data.tar.gz: 4fe8a75d9e3c0cc3bd756ab247fecb7575049dcec58559bdb7cdb1c58ecb4b6e7de3f8be96c396b0b304134cc75c6058b3cbeafce92cd027413971a99a812c02
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,7 +16,7 @@ Please ⭐ if you like it.
16
16
 
17
17
  ## Install
18
18
 
19
- - Add to your Gemfile: `gem 'tiny_admin', '~> 0.5'`
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
22
  - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` with a YAML config file (see [configuration](#configuration) below):
@@ -51,6 +51,7 @@ Plugin available:
51
51
  Pages available:
52
52
 
53
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;
54
55
  - **PageNotFound**: define how to present pages not found;
55
56
  - **RecordNotFound**: define how to present record not found page;
56
57
  - **SimpleAuthLogin**: define how to present the login form for SimpleAuth plugin;
@@ -122,9 +123,24 @@ authentication:
122
123
 
123
124
  - `slug` (String): section reference identifier;
124
125
  - `name` (String): section's title;
125
- - `type` (String): the type of section: `url`, `page` or `resource`;
126
+ - `type` (String): the type of section: `content`, `page`, `resource` or `url`;
126
127
  - other properties depends on the section's type.
127
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
+ ```
143
+
128
144
  For _url_ sections:
129
145
 
130
146
  - `url` (String): the URL to load when clicking on the section's menu item;
@@ -21,7 +21,7 @@ module TinyAdmin
21
21
  records, total_count = repository.list(page: current_page, limit: pagination, filters: filters, sort: sort)
22
22
 
23
23
  prepare_page(Views::Actions::Index) do |page|
24
- setup_pagination(page, settings.components[:pagination], total_count: total_count)
24
+ setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: total_count)
25
25
  page.update_attributes(
26
26
  actions: context.actions,
27
27
  fields: fields,
@@ -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
@@ -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 collection
46
+ model.all
47
+ end
48
+
45
49
  def list(page: 1, limit: 10, sort: nil, filters: nil)
46
- query = sort ? model.all.order(sort) : model.all
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,11 +27,11 @@ 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.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
37
  context.resources.each do |slug, options|
@@ -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
 
@@ -100,7 +103,7 @@ module TinyAdmin
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
@@ -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,25 +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
- :page_not_found,
29
- :record_not_found,
30
- :repository,
31
- :root,
32
- :root_path,
33
- :sections,
34
- :scripts,
35
- :style_links
36
-
37
- def [](key)
38
- 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]
39
54
  end
40
55
 
41
- def []=(key, value)
42
- send("#{key}=", value)
56
+ def []=(*path, value)
57
+ key, option = fetch_setting(path)
58
+ option[key] = value
43
59
  convert_value(key, value)
44
60
  end
45
61
 
@@ -56,17 +72,27 @@ module TinyAdmin
56
72
 
57
73
  context.pages ||= {}
58
74
  context.resources ||= {}
59
- @sections ||= []
60
- @root_path = '/' if @root_path == ''
75
+ self.sections ||= []
76
+ self.root_path = '/' if root_path == ''
61
77
 
62
- if @authentication[:plugin] <= Plugins::SimpleAuth
63
- @authentication[:logout] ||= { name: 'logout', path: "#{root_path}/auth/logout" }
78
+ if authentication[:plugin] <= Plugins::SimpleAuth
79
+ authentication[:logout] ||= { name: 'logout', path: "#{root_path}/auth/logout" }
64
80
  end
65
81
  context.navbar = prepare_navbar(sections, logout: authentication[:logout])
66
82
  end
67
83
 
84
+ def reset!
85
+ @options = {}
86
+ end
87
+
68
88
  private
69
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
+
70
96
  def convert_value(key, value)
71
97
  if value.is_a?(Hash)
72
98
  value.each_key do |key2|
@@ -83,7 +109,7 @@ module TinyAdmin
83
109
  def prepare_navbar(sections, logout:)
84
110
  items = sections.each_with_object({}) do |section, list|
85
111
  unless section.is_a?(Hash)
86
- section_class = Object.const_get(section)
112
+ section_class = section.is_a?(String) ? Object.const_get(section) : section
87
113
  next unless section_class.respond_to?(:to_h)
88
114
 
89
115
  section = section_class.to_h
@@ -91,33 +117,37 @@ module TinyAdmin
91
117
 
92
118
  slug = section[:slug].to_s
93
119
  case section[:type]&.to_sym
94
- when :url
95
- list[slug] = add_url_section(slug, section)
120
+ when :content
121
+ list[slug] = add_content_section(slug, section)
96
122
  when :page
97
123
  list[slug] = add_page_section(slug, section)
98
124
  when :resource
99
125
  list[slug] = add_resource_section(slug, section)
126
+ when :url
127
+ list[slug] = add_url_section(slug, section)
100
128
  end
101
129
  end
102
130
  items['auth/logout'] = logout if logout
103
131
  items
104
132
  end
105
133
 
106
- def add_url_section(_slug, section)
107
- section.slice(:name, :options).tap { _1[:path] = section[:url] }
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 }
108
137
  end
109
138
 
110
139
  def add_page_section(slug, section)
111
140
  page = section[:page]
112
- context.pages[slug] = page.is_a?(String) ? Object.const_get(page) : page
113
- { name: section[:name], path: route_for(slug), class: context.pages[slug] }
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 }
114
144
  end
115
145
 
116
146
  def add_resource_section(slug, section)
117
- repository = section[:repository] || settings.repository
147
+ repo = section[:repository] || repository
118
148
  context.resources[slug] = {
119
149
  model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
120
- repository: repository.is_a?(String) ? Object.const_get(repository) : repository
150
+ repository: repo.is_a?(String) ? Object.const_get(repo) : repo
121
151
  }
122
152
  resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
123
153
  resource_options[:only] ||= %i[index show]
@@ -125,5 +155,9 @@ module TinyAdmin
125
155
  hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
126
156
  { name: section[:name], path: route_for(slug) } unless hidden
127
157
  end
158
+
159
+ def add_url_section(_slug, section)
160
+ section.slice(:name, :options).tap { _1[:path] = section[:url] }
161
+ end
128
162
  end
129
163
  end
@@ -17,13 +17,13 @@ module TinyAdmin
17
17
  def prepare_page(page_class, options: nil)
18
18
  page_class.new.tap do |page|
19
19
  page.options = options
20
- page.head_component = settings.components[:head]&.new
21
- page.flash_component = settings.components[:flash]&.new
22
- 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
23
  page.navbar_component&.update_attributes(
24
24
  current_slug: context&.slug,
25
- root_path: settings.root_path,
26
- root_title: settings.root[:title],
25
+ root_path: TinyAdmin.settings.root_path,
26
+ root_title: TinyAdmin.settings.root[:title],
27
27
  items: options&.include?(:no_menu) ? [] : context&.navbar
28
28
  )
29
29
  yield(page) if block_given?
@@ -31,7 +31,7 @@ module TinyAdmin
31
31
  end
32
32
 
33
33
  def route_for(section, reference: nil, action: nil, query: nil)
34
- root_path = settings.root_path == '/' ? nil : settings.root_path
34
+ root_path = TinyAdmin.settings.root_path == '/' ? nil : TinyAdmin.settings.root_path
35
35
  route = [root_path, section, reference, action].compact.join("/")
36
36
  route << "?#{query}" if query
37
37
  route[0] == '/' ? route : route.prepend('/')
@@ -46,9 +46,5 @@ module TinyAdmin
46
46
  def context
47
47
  TinyAdmin::Context.instance
48
48
  end
49
-
50
- def settings
51
- TinyAdmin::Settings.instance
52
- end
53
49
  end
54
50
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinyAdmin
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -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)
@@ -6,7 +6,7 @@ module TinyAdmin
6
6
  attr_accessor :flash_component, :head_component, :messages, :navbar_component, :options, :title
7
7
 
8
8
  def template(&block)
9
- extra_styles = settings.extra_styles
9
+ extra_styles = TinyAdmin.settings.extra_styles
10
10
  flash_component&.messages = messages
11
11
  head_component&.update_attributes(page_title: title, style_links: style_links, extra_styles: extra_styles)
12
12
 
@@ -49,7 +49,7 @@ module TinyAdmin
49
49
  end
50
50
 
51
51
  def style_links
52
- settings.style_links || [
52
+ TinyAdmin.settings.style_links || [
53
53
  # Bootstrap CDN
54
54
  {
55
55
  href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css',
@@ -61,7 +61,7 @@ module TinyAdmin
61
61
  end
62
62
 
63
63
  def render_scripts
64
- (settings.scripts || []).each do |script_attrs|
64
+ (TinyAdmin.settings.scripts || []).each do |script_attrs|
65
65
  script(**script_attrs)
66
66
  end
67
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.5.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-24 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
@@ -101,6 +101,7 @@ files:
101
101
  - lib/tiny_admin/views/components/navbar.rb
102
102
  - lib/tiny_admin/views/components/pagination.rb
103
103
  - lib/tiny_admin/views/default_layout.rb
104
+ - lib/tiny_admin/views/pages/content.rb
104
105
  - lib/tiny_admin/views/pages/page_not_found.rb
105
106
  - lib/tiny_admin/views/pages/record_not_found.rb
106
107
  - lib/tiny_admin/views/pages/root.rb