aerogel-admin 1.4.4
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +13 -0
- data/Rakefile +1 -0
- data/aerogel-admin.gemspec +31 -0
- data/app/helpers/admin.rb +54 -0
- data/app/helpers/decorators.rb +29 -0
- data/app/helpers/icons.rb +11 -0
- data/app/helpers/table_builder.rb +8 -0
- data/app/helpers/tabs_builder.rb +7 -0
- data/app/routes/admin.rb +37 -0
- data/app/routes/users.rb +71 -0
- data/app/routes/users_access.rb +54 -0
- data/app/routes/users_roles.rb +53 -0
- data/assets/fonts/aerogel-icons.css +28 -0
- data/assets/fonts/aerogel-icons.eot +0 -0
- data/assets/fonts/aerogel-icons.svg +11 -0
- data/assets/fonts/aerogel-icons.ttf +0 -0
- data/assets/fonts/aerogel-icons.woff +0 -0
- data/assets/javascripts/.gitkeep +0 -0
- data/assets/javascripts/controllers/admin-modal.js.coffee +7 -0
- data/assets/javascripts/controllers/admin-modal/admin-modal-form-buttons.js.coffee +56 -0
- data/assets/javascripts/controllers/admin.js.coffee +13 -0
- data/assets/javascripts/controllers/admin/selectize-inputs.js.coffee +7 -0
- data/assets/javascripts/controllers/admin/top-menu-shadow.js.coffee +9 -0
- data/assets/javascripts/utils/ajax-spinner.js.coffee +26 -0
- data/assets/javascripts/utils/ajax-watcher.js.coffee +18 -0
- data/assets/javascripts/utils/bootstrap-modal-reload.js.coffee +8 -0
- data/assets/javascripts/utils/form-data-async.js.coffee +27 -0
- data/assets/javascripts/utils/i18n.js.coffee +27 -0
- data/assets/javascripts/utils/on-future-elements.js.coffee +15 -0
- data/assets/stylesheets/admin/bootstrap-settings.css.scss +33 -0
- data/assets/stylesheets/admin/global.css.scss +61 -0
- data/assets/stylesheets/admin/styles/ajax-indicator.css.scss +26 -0
- data/assets/stylesheets/admin/styles/bootstrap-modal.css.scss +24 -0
- data/assets/stylesheets/admin/styles/language-selector.css.scss +5 -0
- data/assets/stylesheets/admin/styles/page-header.css.scss +7 -0
- data/assets/stylesheets/admin/styles/sticky-footer-navbar.css.scss +34 -0
- data/assets/stylesheets/admin/styles/table.css.scss +3 -0
- data/assets/stylesheets/admin/styles/top-menu.css.scss +3 -0
- data/assets/stylesheets/admin/utils/center-absolutely.css.scss +5 -0
- data/assets/stylesheets/controllers/admin.css.scss +14 -0
- data/assets/vendor/bootstrap-datetimepicker.css.scss +1 -0
- data/assets/vendor/bootstrap-datetimepicker.js.coffee +1 -0
- data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css +5 -0
- data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js +1 -0
- data/assets/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.ru.js +163 -0
- data/assets/vendor/moment.js.coffee +1 -0
- data/assets/vendor/momentjs/moment-with-langs.min.js +9 -0
- data/assets/vendor/selectize.css.scss +2 -0
- data/assets/vendor/selectize.js.coffee +1 -0
- data/assets/vendor/selectize/selectize.bootstrap3.css +385 -0
- data/assets/vendor/selectize/selectize.css +311 -0
- data/assets/vendor/selectize/selectize.default.css +381 -0
- data/assets/vendor/selectize/selectize.js +3345 -0
- data/assets/vendor/smart-list-table.css.scss +42 -0
- data/assets/vendor/smart-list-table.js.coffee +1 -0
- data/assets/vendor/smart-list-table/smart-list-table-row.js.coffee +63 -0
- data/assets/vendor/smart-list-table/smart-list-table.css.scss +54 -0
- data/assets/vendor/smart-list-table/smart-list-table.js.coffee +133 -0
- data/assets/vendor/smart-tree-table.css.scss +42 -0
- data/assets/vendor/smart-tree-table.js.coffee +1 -0
- data/assets/vendor/smart-tree-table/smart-tree-table-drag-n-drop.js.coffee +190 -0
- data/assets/vendor/smart-tree-table/smart-tree-table-row.js.coffee +78 -0
- data/assets/vendor/smart-tree-table/smart-tree-table.css.scss +54 -0
- data/assets/vendor/smart-tree-table/smart-tree-table.js.coffee +267 -0
- data/assets/vendor/spin.js +353 -0
- data/config/README.md +3 -0
- data/config/development/.keep +0 -0
- data/config/production/.keep +0 -0
- data/db/model/README.md +1 -0
- data/db/model/admin/user_new_form.rb +40 -0
- data/db/model/user.rb +26 -0
- data/db/seed/01_admin_roles.seed +8 -0
- data/db/seed/02_admin_access.seed +24 -0
- data/db/seed/development/.keep +0 -0
- data/db/seed/development/20_users.seed +45 -0
- data/db/seed/development/admin_users.seed +38 -0
- data/db/seed/production/.keep +0 -0
- data/lib/aerogel/admin.rb +25 -0
- data/lib/aerogel/admin/core.rb +14 -0
- data/lib/aerogel/admin/menu.rb +38 -0
- data/lib/aerogel/admin/table_builder.rb +100 -0
- data/lib/aerogel/admin/tabs_builder.rb +69 -0
- data/lib/aerogel/admin/version.rb +5 -0
- data/locales/actions.en.yml +27 -0
- data/locales/actions.ru.yml +28 -0
- data/locales/admin.en.yml +14 -0
- data/locales/admin.ru.yml +14 -0
- data/locales/models.en.yml +8 -0
- data/locales/models.ru.yml +8 -0
- data/locales/views.en.yml +46 -0
- data/locales/views.ru.yml +46 -0
- data/public/README.md +1 -0
- data/rake/README.md +3 -0
- data/views/admin/index.html.erb +3 -0
- data/views/admin/table_builder/standard/_table_column.html.erb +3 -0
- data/views/admin/table_builder/standard/_table_row.html.erb +7 -0
- data/views/admin/table_builder/standard/table.html.erb +10 -0
- data/views/admin/tabs_builder/standard/_tab.html.erb +3 -0
- data/views/admin/tabs_builder/standard/tabs.html.erb +3 -0
- data/views/admin/users/_tabs.html.erb +8 -0
- data/views/admin/users/access/delete.html.erb +12 -0
- data/views/admin/users/access/edit.html.erb +9 -0
- data/views/admin/users/access/index.html.erb +23 -0
- data/views/admin/users/access/new.html.erb +9 -0
- data/views/admin/users/delete.html.erb +12 -0
- data/views/admin/users/edit.html.erb +46 -0
- data/views/admin/users/index.html.erb +31 -0
- data/views/admin/users/new.html.erb +11 -0
- data/views/admin/users/roles/delete.html.erb +12 -0
- data/views/admin/users/roles/edit.html.erb +9 -0
- data/views/admin/users/roles/index.html.erb +21 -0
- data/views/admin/users/roles/new.html.erb +7 -0
- data/views/form_builder/standard/field_multiselect.erb +14 -0
- data/views/form_builder/standard/field_select.erb +17 -0
- data/views/layouts/admin.html.erb +63 -0
- data/views/layouts/admin/_ajax_indicator.html.erb +4 -0
- data/views/layouts/admin/_alerts.html.erb +22 -0
- data/views/layouts/admin/_menu.html.erb +50 -0
- data/views/layouts/admin/_menu_item.html.erb +5 -0
- data/views/layouts/admin/_modals.html.erb +8 -0
- data/views/layouts/admin/modal.html.erb +38 -0
- metadata +280 -0
data/config/README.md
ADDED
|
File without changes
|
|
File without changes
|
data/db/model/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
A place for your models.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Admin
|
|
2
|
+
class UserNewForm
|
|
3
|
+
include Model::NonPersistent
|
|
4
|
+
|
|
5
|
+
field :full_name, type: String
|
|
6
|
+
field :email, type: String
|
|
7
|
+
field :roles, type: Array, default: [:user]
|
|
8
|
+
|
|
9
|
+
validates_presence_of :full_name, :email, :roles
|
|
10
|
+
validates_format_of :email, with: /@/, message: :invalid_format
|
|
11
|
+
|
|
12
|
+
validate do |record|
|
|
13
|
+
# validate email
|
|
14
|
+
if User.elem_match( :authentications => { :provider => :password, :uid => record.email } ).count > 0
|
|
15
|
+
record.errors.add :email, :taken
|
|
16
|
+
elsif User.elem_match( :emails => { :email => record.email } ).count > 0
|
|
17
|
+
record.errors.add :email, :taken
|
|
18
|
+
end
|
|
19
|
+
# validate roles
|
|
20
|
+
if record.roles_changed?
|
|
21
|
+
unless Role.slugs.contains? record.roles
|
|
22
|
+
record.errors.add :roles, :invalid_roles
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# accessors:
|
|
29
|
+
def roles=( value )
|
|
30
|
+
if value.is_a? Array
|
|
31
|
+
self[:roles] = value.map(&:to_sym)
|
|
32
|
+
elsif value.is_a? String
|
|
33
|
+
self[:roles] = value.split(",").map{|v| v.strip.to_sym}
|
|
34
|
+
else
|
|
35
|
+
raise ArgumentError.new "Invalid value of class #{value.class} passed to roles= setter"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end # class UserNewForm
|
|
40
|
+
end # module Admin
|
data/db/model/user.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class User
|
|
2
|
+
|
|
3
|
+
# Creates a User from Admin::UserNewForm.
|
|
4
|
+
#
|
|
5
|
+
def self.create_from_admin_user_new_form( object )
|
|
6
|
+
raise "Cannot create User from #{object.class}" unless object.is_a? Admin::UserNewForm
|
|
7
|
+
|
|
8
|
+
tmp_password = self.generate_confirmation_token # generates random password
|
|
9
|
+
self.new(
|
|
10
|
+
full_name: object.full_name,
|
|
11
|
+
roles: object.roles,
|
|
12
|
+
emails: [{
|
|
13
|
+
email: object.email,
|
|
14
|
+
confirmed: false
|
|
15
|
+
}],
|
|
16
|
+
authentications: [{
|
|
17
|
+
provider: :password,
|
|
18
|
+
uid: object.email,
|
|
19
|
+
email_id: object.email,
|
|
20
|
+
password: tmp_password,
|
|
21
|
+
password_confirmation: tmp_password
|
|
22
|
+
}]
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end # class User
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# A .seed file is a .conf file which stores
|
|
2
|
+
# database seed data and rules.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
# Model class to use
|
|
6
|
+
#
|
|
7
|
+
# ! required
|
|
8
|
+
model Access
|
|
9
|
+
|
|
10
|
+
# Attribute name or a list of attribute names to be used as key(s)
|
|
11
|
+
# when finding objects
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# find_by :name
|
|
15
|
+
# or
|
|
16
|
+
# find_by [:first_name, :last_name]
|
|
17
|
+
#
|
|
18
|
+
# ! required
|
|
19
|
+
find_by [:path, :role]
|
|
20
|
+
|
|
21
|
+
seeds [
|
|
22
|
+
{ path: '/admin/**', access: :RW, role: :admin }
|
|
23
|
+
]
|
|
24
|
+
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
model User
|
|
2
|
+
|
|
3
|
+
find_by :full_name
|
|
4
|
+
|
|
5
|
+
# Values of attributes listed here will be set even
|
|
6
|
+
# if they exist already in the matching database object.
|
|
7
|
+
#
|
|
8
|
+
# Other attributes in the seed data will be treated as a default value,
|
|
9
|
+
# i.e. they will only be set if the matching database object does not exist
|
|
10
|
+
# or its corresponding attribute is not set.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# force :value
|
|
14
|
+
# or:
|
|
15
|
+
# force [:value, :description]
|
|
16
|
+
#
|
|
17
|
+
# default is: no fields are forced
|
|
18
|
+
|
|
19
|
+
#force :value
|
|
20
|
+
|
|
21
|
+
def create_test_user( name )
|
|
22
|
+
email = "#{name}@#{name}"
|
|
23
|
+
{
|
|
24
|
+
full_name: name,
|
|
25
|
+
roles: [:user],
|
|
26
|
+
emails:[{
|
|
27
|
+
email: email,
|
|
28
|
+
confirmed: true
|
|
29
|
+
}],
|
|
30
|
+
authentications:[{
|
|
31
|
+
provider: :password,
|
|
32
|
+
uid: email,
|
|
33
|
+
email_id: email,
|
|
34
|
+
password: 'test',
|
|
35
|
+
password_confirmation: 'test'
|
|
36
|
+
}]
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
user_seeds = []
|
|
41
|
+
20.times do |i|
|
|
42
|
+
user_seeds << create_test_user( "user#{i}" )
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
seeds user_seeds
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
model User
|
|
2
|
+
|
|
3
|
+
find_by :full_name
|
|
4
|
+
|
|
5
|
+
# Values of attributes listed here will be set even
|
|
6
|
+
# if they exist already in the matching database object.
|
|
7
|
+
#
|
|
8
|
+
# Other attributes in the seed data will be treated as a default value,
|
|
9
|
+
# i.e. they will only be set if the matching database object does not exist
|
|
10
|
+
# or its corresponding attribute is not set.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# force :value
|
|
14
|
+
# or:
|
|
15
|
+
# force [:value, :description]
|
|
16
|
+
#
|
|
17
|
+
# default is: no fields are forced
|
|
18
|
+
|
|
19
|
+
#force :value
|
|
20
|
+
|
|
21
|
+
seeds [
|
|
22
|
+
{
|
|
23
|
+
full_name: 'admin',
|
|
24
|
+
roles: [:user, :admin],
|
|
25
|
+
emails:[{
|
|
26
|
+
email: 'admin@admin',
|
|
27
|
+
confirmed: true
|
|
28
|
+
}],
|
|
29
|
+
authentications:[{
|
|
30
|
+
provider: :password,
|
|
31
|
+
uid: 'admin@admin',
|
|
32
|
+
email_id: 'admin@admin',
|
|
33
|
+
password: 'admin',
|
|
34
|
+
password_confirmation: 'admin'
|
|
35
|
+
}]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'aerogel/core'
|
|
2
|
+
require 'aerogel/forms'
|
|
3
|
+
require 'aerogel/users'
|
|
4
|
+
require 'aerogel/media'
|
|
5
|
+
require 'aerogel/bootstrap'
|
|
6
|
+
require 'aerogel/font_awesome'
|
|
7
|
+
|
|
8
|
+
require "aerogel/admin/version"
|
|
9
|
+
require "aerogel/admin/core"
|
|
10
|
+
require "aerogel/admin/menu"
|
|
11
|
+
require "aerogel/admin/table_builder"
|
|
12
|
+
require "aerogel/admin/tabs_builder"
|
|
13
|
+
|
|
14
|
+
module Aerogel
|
|
15
|
+
|
|
16
|
+
# Finally, register module's root folder
|
|
17
|
+
register_path File.join( File.dirname(__FILE__), '..', '..' )
|
|
18
|
+
|
|
19
|
+
# configure module
|
|
20
|
+
on_load do |app|
|
|
21
|
+
app.register Aerogel::Admin
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Aerogel::Admin
|
|
2
|
+
|
|
3
|
+
def self.registered(app)
|
|
4
|
+
setup_reloader(app) if Aerogel.config.aerogel.reloader?
|
|
5
|
+
|
|
6
|
+
# module initialization
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.setup_reloader(app)
|
|
10
|
+
app.use Aerogel::Reloader, :routes, before: true do
|
|
11
|
+
Aerogel::Admin::Menu.reset!
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end # module Aerogel::Admin
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Aerogel::Admin
|
|
2
|
+
class Menu
|
|
3
|
+
attr_accessor :items
|
|
4
|
+
def initialize()
|
|
5
|
+
@items = []
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.instance
|
|
9
|
+
@instance ||= self.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.reset!
|
|
13
|
+
instance.items.clear
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Item
|
|
17
|
+
attr_accessor :url, :icon, :label, :priority
|
|
18
|
+
def initialize( url, opts = {} )
|
|
19
|
+
self.url = url
|
|
20
|
+
self.icon = opts[:icon]
|
|
21
|
+
self.label = opts[:label] || url
|
|
22
|
+
self.priority = opts[:priority] || 50
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.create( url, opts = {} )
|
|
26
|
+
Menu.instance.items << self.new( url, opts )
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def human_label
|
|
30
|
+
if label.is_a? Symbol
|
|
31
|
+
I18n.t label
|
|
32
|
+
else
|
|
33
|
+
label.humanize
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end # class Item
|
|
37
|
+
end # class Menu
|
|
38
|
+
end # module Aerogel::Admin
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module Aerogel::Admin
|
|
2
|
+
|
|
3
|
+
# TableBuilder constructs and displays a table populated with data obtained
|
|
4
|
+
# from passed object.
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# table User.all do
|
|
8
|
+
# column :full_name
|
|
9
|
+
# column :roles
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
class TableBuilder < Aerogel::Render::BlockHelper
|
|
13
|
+
|
|
14
|
+
attr_accessor :object, :options, :columns, :style
|
|
15
|
+
|
|
16
|
+
DEFAULT_OPTIONS = {
|
|
17
|
+
class: 'table-striped',
|
|
18
|
+
style: 'standard',
|
|
19
|
+
html_params: {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def initialize( object, options = {}, &block )
|
|
23
|
+
super( &block )
|
|
24
|
+
self.object = object
|
|
25
|
+
self.options = DEFAULT_OPTIONS.deep_merge( options )
|
|
26
|
+
self.style = self.options[:style]
|
|
27
|
+
self.columns = []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def column( *args, &block )
|
|
31
|
+
self.columns << Column.new( self, *args, &block )
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def template( name )
|
|
36
|
+
"admin/table_builder/#{style}/#{name}".to_sym
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def wrap( content )
|
|
40
|
+
erb template("table.html"), locals: { table: self }, layout: false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Renders html params for <table ...>
|
|
44
|
+
#
|
|
45
|
+
def html_params
|
|
46
|
+
attrs = @options[:html_params].dup
|
|
47
|
+
attrs.merge!({
|
|
48
|
+
# :action => @options[:action]
|
|
49
|
+
})
|
|
50
|
+
attrs.map{|n, v| v.nil? ? "#{n}" : "#{n}=\"#{v}\""}.join(" ")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
class Column
|
|
57
|
+
|
|
58
|
+
attr_accessor :table, :field, :label, :options, :block
|
|
59
|
+
|
|
60
|
+
KNOWN_OPTIONS = [ :label ]
|
|
61
|
+
|
|
62
|
+
def initialize( table, field, options = {}, &block )
|
|
63
|
+
self.table = table
|
|
64
|
+
self.field = field
|
|
65
|
+
self.options = options
|
|
66
|
+
self.block = block
|
|
67
|
+
self.label = self.options[:label] || self.field.to_sym
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def human_label
|
|
71
|
+
if label.is_a? Symbol
|
|
72
|
+
if table.object.respond_to? :human_attribute_name
|
|
73
|
+
table.object.human_attribute_name label, default: label
|
|
74
|
+
elsif table.object.respond_to?( :first ) && table.object.first.class.respond_to?( :human_attribute_name )
|
|
75
|
+
table.object.first.class.human_attribute_name label, default: label
|
|
76
|
+
else
|
|
77
|
+
I18n.t label
|
|
78
|
+
end
|
|
79
|
+
elsif label.is_a? String
|
|
80
|
+
label
|
|
81
|
+
else
|
|
82
|
+
label.to_s.humanize
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Renders html params for column cells
|
|
87
|
+
#
|
|
88
|
+
def html_params
|
|
89
|
+
attrs = @options.except( *KNOWN_OPTIONS )
|
|
90
|
+
attrs = attrs.deep_merge( @options[:html_params] ) if @options.key? :html_params
|
|
91
|
+
attrs.to_html_params
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end # class Column
|
|
95
|
+
|
|
96
|
+
end # class TableBuilder
|
|
97
|
+
|
|
98
|
+
end # module Aerogel::Admin
|
|
99
|
+
|
|
100
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Aerogel::Admin
|
|
2
|
+
|
|
3
|
+
# TabsBuilder constructs and displays a tab navigation.
|
|
4
|
+
# from passed object.
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# tabs do
|
|
8
|
+
# tab "/url/to/page1", label: "Page 1"
|
|
9
|
+
# tab "/url/to/page2", label: "Page 2"
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
class TabsBuilder < Aerogel::Render::BlockHelper
|
|
13
|
+
|
|
14
|
+
attr_accessor :options, :tabs, :style
|
|
15
|
+
|
|
16
|
+
DEFAULT_OPTIONS = {
|
|
17
|
+
style: 'standard'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def initialize( options = {}, &block )
|
|
21
|
+
super( &block )
|
|
22
|
+
self.options = DEFAULT_OPTIONS.deep_merge( options )
|
|
23
|
+
self.style = self.options[:style]
|
|
24
|
+
self.tabs = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def tab( *args, &block )
|
|
28
|
+
self.tabs << Tab.new( *args, &block )
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def template( name )
|
|
33
|
+
"admin/tabs_builder/#{style}/#{name}".to_sym
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def wrap( content )
|
|
37
|
+
erb template("tabs.html"), locals: { tabs: self }, layout: false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
class Tab
|
|
43
|
+
|
|
44
|
+
attr_accessor :url, :label, :options, :block
|
|
45
|
+
|
|
46
|
+
def initialize( url, options = {}, &block )
|
|
47
|
+
self.url = url
|
|
48
|
+
self.options = options
|
|
49
|
+
self.label = self.options[:label] || self.url
|
|
50
|
+
self.block = block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def human_label
|
|
54
|
+
if label.is_a? String
|
|
55
|
+
label
|
|
56
|
+
elsif label.is_a? Symbol
|
|
57
|
+
I18n.t label
|
|
58
|
+
else
|
|
59
|
+
label.humanize
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end # class Tab
|
|
64
|
+
|
|
65
|
+
end # class TabsBuilder
|
|
66
|
+
|
|
67
|
+
end # module Aerogel::Admin
|
|
68
|
+
|
|
69
|
+
|