trestle 0.8.4 → 0.8.11
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 +5 -5
- data/.gitignore +4 -0
- data/.travis.yml +14 -1
- data/Gemfile +6 -0
- data/README.md +15 -9
- data/app/assets/javascripts/trestle/admin.js +11 -10
- data/app/assets/javascripts/trestle/components/_confirmation.js +40 -0
- data/app/assets/javascripts/trestle/components/_datepicker.js +37 -0
- data/app/assets/javascripts/trestle/components/_dialog.js +136 -0
- data/app/assets/javascripts/trestle/{_errors.js → components/_errors.js} +2 -2
- data/app/assets/javascripts/trestle/components/_form.js +75 -0
- data/app/assets/javascripts/trestle/{_gallery.js → components/_gallery.js} +3 -3
- data/app/assets/javascripts/trestle/{_select.js → components/_select.js} +2 -2
- data/app/assets/javascripts/trestle/components/_sidebar.js +77 -0
- data/app/assets/javascripts/trestle/{_table.js → components/_table.js} +1 -1
- data/app/assets/javascripts/trestle/components/_tabs.js +25 -0
- data/app/assets/javascripts/trestle/components/_tooltips.js +19 -0
- data/app/assets/javascripts/trestle/core/_contexts.js +13 -0
- data/app/assets/javascripts/trestle/core/_cookies.js +24 -0
- data/app/assets/javascripts/trestle/core/_events.js +39 -0
- data/app/assets/javascripts/trestle/core/_l10n.js +23 -0
- data/app/assets/javascripts/trestle/core/_visit.js +10 -0
- data/app/assets/stylesheets/trestle/components/_alerts.scss +28 -0
- data/app/assets/stylesheets/trestle/components/_avatar.scss +23 -1
- data/app/assets/stylesheets/trestle/components/_breadcrumbs.scss +12 -0
- data/app/assets/stylesheets/trestle/components/_buttons.scss +111 -1
- data/app/assets/stylesheets/trestle/components/_content.scss +53 -21
- data/app/assets/stylesheets/trestle/components/_datepicker.scss +28 -0
- data/app/assets/stylesheets/trestle/components/_fields.scss +17 -0
- data/app/assets/stylesheets/trestle/components/_input-group.scss +26 -1
- data/app/assets/stylesheets/trestle/components/_modal.scss +108 -0
- data/app/assets/stylesheets/trestle/components/_navigation.scss +104 -14
- data/app/assets/stylesheets/trestle/components/_pagination.scss +3 -3
- data/app/assets/stylesheets/trestle/components/_scopes.scss +11 -2
- data/app/assets/stylesheets/trestle/components/_sidebar.scss +3 -2
- data/app/assets/stylesheets/trestle/components/_table.scss +34 -10
- data/app/assets/stylesheets/trestle/components/_tags.scss +9 -0
- data/app/assets/stylesheets/trestle/components/_timestamp.scss +4 -1
- data/app/assets/stylesheets/trestle/components/_toolbars.scss +55 -0
- data/app/assets/stylesheets/trestle/components/_wells.scss +9 -1
- data/app/assets/stylesheets/trestle/core/_defaults.scss +21 -6
- data/app/assets/stylesheets/trestle/core/_layout.scss +8 -0
- data/app/assets/stylesheets/trestle/core/_mixins.scss +11 -0
- data/app/assets/stylesheets/trestle/core/_typography.scss +39 -0
- data/app/controllers/concerns/trestle/controller/breadcrumbs.rb +21 -0
- data/app/controllers/concerns/trestle/controller/callbacks.rb +21 -0
- data/app/controllers/concerns/trestle/controller/dialog.rb +16 -0
- data/app/controllers/concerns/trestle/controller/helpers.rb +18 -0
- data/app/controllers/concerns/trestle/controller/layout.rb +16 -0
- data/app/controllers/concerns/trestle/controller/location.rb +15 -0
- data/app/controllers/trestle/application_controller.rb +6 -30
- data/app/helpers/trestle/avatar_helper.rb +9 -2
- data/app/helpers/trestle/flash_helper.rb +15 -0
- data/app/helpers/trestle/form_helper.rb +8 -5
- data/app/helpers/trestle/format_helper.rb +7 -3
- data/app/helpers/trestle/headings_helper.rb +27 -0
- data/app/helpers/trestle/hook_helper.rb +13 -4
- data/app/helpers/trestle/i18n_helper.rb +14 -0
- data/app/helpers/trestle/panel_helper.rb +24 -0
- data/app/helpers/trestle/tab_helper.rb +2 -2
- data/app/helpers/trestle/table_helper.rb +41 -2
- data/app/helpers/trestle/timestamp_helper.rb +49 -7
- data/app/helpers/trestle/toolbars_helper.rb +34 -0
- data/app/helpers/trestle/url_helper.rb +72 -8
- data/app/views/layouts/trestle/admin.html.erb +3 -5
- data/app/views/trestle/application/_dialog.html.erb +34 -0
- data/app/views/trestle/application/_header.html.erb +22 -20
- data/app/views/trestle/application/_layout.html.erb +1 -1
- data/app/views/trestle/application/_tabs.html.erb +8 -2
- data/app/views/trestle/{application → flash}/_alert.html.erb +7 -2
- data/app/views/trestle/flash/_debug.html.erb +8 -0
- data/app/views/trestle/flash/_flash.html.erb +7 -0
- data/app/views/trestle/resource/_scopes.html.erb +1 -1
- data/app/views/trestle/resource/edit.html.erb +8 -10
- data/app/views/trestle/resource/index.html.erb +16 -4
- data/app/views/trestle/resource/new.html.erb +6 -6
- data/app/views/trestle/resource/show.html.erb +8 -10
- data/app/views/trestle/shared/_sidebar.html.erb +10 -7
- data/app/views/trestle/shared/_title.html.erb +14 -0
- data/app/views/trestle/table/_table.html.erb +4 -6
- data/bower.json +1 -1
- data/config/locales/cs.rb +18 -0
- data/config/locales/cs.yml +95 -0
- data/config/locales/en.yml +65 -36
- data/config/locales/es-MX.yml +94 -0
- data/config/locales/es.yml +94 -0
- data/config/locales/fr.rb +18 -0
- data/config/locales/fr.yml +84 -0
- data/config/locales/lv.rb +18 -0
- data/config/locales/lv.yml +94 -0
- data/config/locales/nl.rb +18 -0
- data/config/locales/nl.yml +82 -0
- data/config/locales/pl.rb +18 -0
- data/config/locales/pl.yml +85 -0
- data/config/locales/pt-BR.rb +18 -0
- data/config/locales/pt-BR.yml +84 -0
- data/config/locales/zh-CN.rb +18 -0
- data/config/locales/zh-CN.yml +94 -0
- data/gemfiles/rails-4.2.gemfile +16 -0
- data/gemfiles/rails-5.0.gemfile +16 -0
- data/gemfiles/rails-5.1.gemfile +16 -0
- data/gemfiles/rails-5.2.gemfile +16 -0
- data/gemfiles/rails-edge.gemfile +17 -0
- data/lib/generators/trestle/install/templates/trestle.rb.erb +18 -2
- data/lib/generators/trestle/resource/templates/admin.rb.erb +2 -2
- data/lib/trestle/adapters/active_record_adapter.rb +2 -6
- data/lib/trestle/adapters/adapter.rb +40 -15
- data/lib/trestle/adapters/sequel_adapter.rb +2 -6
- data/lib/trestle/admin/builder.rb +42 -12
- data/lib/trestle/admin/controller.rb +3 -1
- data/lib/trestle/admin.rb +84 -3
- data/lib/trestle/breadcrumb.rb +16 -1
- data/lib/trestle/configurable.rb +6 -0
- data/lib/trestle/configuration.rb +28 -5
- data/lib/trestle/display.rb +1 -1
- data/lib/trestle/engine.rb +10 -4
- data/lib/trestle/form/automatic.rb +6 -3
- data/lib/trestle/form/builder.rb +5 -1
- data/lib/trestle/form/field.rb +1 -1
- data/lib/trestle/form/fields/date_picker.rb +2 -2
- data/lib/trestle/form/fields/form_control.rb +14 -4
- data/lib/trestle/form/fields/form_group.rb +14 -3
- data/lib/trestle/form/fields/select.rb +5 -1
- data/lib/trestle/form/fields/tag_select.rb +1 -1
- data/lib/trestle/form/renderer.rb +2 -2
- data/lib/trestle/form.rb +7 -3
- data/lib/trestle/hook.rb +27 -0
- data/lib/trestle/model_name.rb +64 -0
- data/lib/trestle/navigation/group.rb +16 -0
- data/lib/trestle/navigation/item.rb +11 -1
- data/lib/trestle/navigation.rb +24 -6
- data/lib/trestle/options.rb +7 -5
- data/lib/trestle/reloader.rb +1 -1
- data/lib/trestle/resource/adapter_methods.rb +62 -0
- data/lib/trestle/resource/builder.rb +43 -17
- data/lib/trestle/resource/collection.rb +68 -0
- data/lib/trestle/resource/controller.rb +70 -33
- data/lib/trestle/resource.rb +97 -105
- data/lib/trestle/scope.rb +13 -3
- data/lib/trestle/tab.rb +4 -0
- data/lib/trestle/table/actions_column.rb +41 -31
- data/lib/trestle/table/automatic.rb +1 -2
- data/lib/trestle/table/builder.rb +2 -2
- data/lib/trestle/table/column.rb +40 -9
- data/lib/trestle/table/row.rb +18 -2
- data/lib/trestle/table.rb +18 -2
- data/lib/trestle/toolbar/builder.rb +52 -0
- data/lib/trestle/toolbar/context.rb +39 -0
- data/lib/trestle/toolbar.rb +43 -0
- data/lib/trestle/version.rb +1 -1
- data/lib/trestle.rb +9 -4
- data/trestle.gemspec +5 -3
- data/vendor/assets/bower_components/trestle/select2/dist/js/select2.full.js +90 -69
- data/vendor/assets/javascripts/trestle/flatpickr.js.erb +2 -0
- data/vendor/assets/stylesheets/trestle/magnific-popup.scss +13 -1
- metadata +99 -22
- data/app/assets/javascripts/trestle/_confirmation.js +0 -23
- data/app/assets/javascripts/trestle/_datepicker.js +0 -22
- data/app/assets/javascripts/trestle/_form.js +0 -6
- data/app/assets/javascripts/trestle/_sidebar.js +0 -52
- data/app/assets/javascripts/trestle/_tabs.js +0 -13
- data/app/assets/javascripts/trestle/_tooltips.js +0 -3
- data/app/helpers/trestle/title_helper.rb +0 -26
- data/app/views/trestle/application/_flash.html.erb +0 -11
|
@@ -13,15 +13,23 @@ module Trestle
|
|
|
13
13
|
def input_group
|
|
14
14
|
if @prepend || @append
|
|
15
15
|
content_tag(:div, class: "input-group") do
|
|
16
|
-
concat
|
|
16
|
+
concat input_group_addon(@prepend) if @prepend
|
|
17
17
|
concat yield
|
|
18
|
-
concat
|
|
18
|
+
concat input_group_addon(@append) if @append
|
|
19
19
|
end
|
|
20
20
|
else
|
|
21
21
|
yield
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def input_group_addon(addon)
|
|
26
|
+
if addon[:wrap]
|
|
27
|
+
content_tag(:span, addon[:content], class: "input-group-addon")
|
|
28
|
+
else
|
|
29
|
+
addon[:content]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
def defaults
|
|
26
34
|
super.merge(class: ["form-control"])
|
|
27
35
|
end
|
|
@@ -29,8 +37,10 @@ module Trestle
|
|
|
29
37
|
def extract_options!
|
|
30
38
|
super
|
|
31
39
|
|
|
32
|
-
@prepend = options.delete(:prepend)
|
|
33
|
-
@
|
|
40
|
+
@prepend = { content: options.delete(:prepend), wrap: true } if options[:prepend]
|
|
41
|
+
@prepend = { content: options.delete(:prepend!), wrap: false } if options[:prepend!]
|
|
42
|
+
@append = { content: options.delete(:append), wrap: true } if options[:append]
|
|
43
|
+
@append = { content: options.delete(:append!), wrap: false } if options[:append!]
|
|
34
44
|
end
|
|
35
45
|
end
|
|
36
46
|
end
|
|
@@ -2,19 +2,30 @@ module Trestle
|
|
|
2
2
|
class Form
|
|
3
3
|
module Fields
|
|
4
4
|
class FormGroup < Field
|
|
5
|
+
WRAPPER_OPTIONS = [:help, :label, :hide_label]
|
|
6
|
+
|
|
5
7
|
def render
|
|
6
8
|
options[:class] << 'has-error' if errors.any?
|
|
7
9
|
|
|
8
|
-
content_tag(:div, options) do
|
|
10
|
+
content_tag(:div, options.except(*WRAPPER_OPTIONS)) do
|
|
9
11
|
concat label unless options[:label] == false
|
|
10
|
-
concat block
|
|
12
|
+
concat template.capture(&block) if block
|
|
11
13
|
concat help_message if options[:help]
|
|
12
14
|
concat error_message if errors.any?
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def help_message
|
|
17
|
-
|
|
19
|
+
classes = ["help-block"]
|
|
20
|
+
|
|
21
|
+
if options[:help].is_a?(Hash)
|
|
22
|
+
message = options[:help][:text]
|
|
23
|
+
classes << "floating" if options[:help][:float]
|
|
24
|
+
else
|
|
25
|
+
message = options[:help]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
content_tag(:p, message, class: classes)
|
|
18
29
|
end
|
|
19
30
|
|
|
20
31
|
def error_message
|
|
@@ -7,7 +7,7 @@ module Trestle
|
|
|
7
7
|
def initialize(builder, template, name, choices=nil, options={}, html_options={}, &block)
|
|
8
8
|
super(builder, template, name, options, &block)
|
|
9
9
|
|
|
10
|
-
@choices = Choices.new(choices ||
|
|
10
|
+
@choices = Choices.new(choices || default_choices)
|
|
11
11
|
@html_options = default_html_options.merge(html_options)
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -19,6 +19,10 @@ module Trestle
|
|
|
19
19
|
Trestle::Options.new(class: ["form-control"], data: { enable_select2: true })
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def default_choices
|
|
23
|
+
builder.object.send(name) if builder.object
|
|
24
|
+
end
|
|
25
|
+
|
|
22
26
|
# Allows an array of model instances (or a scope) to be
|
|
23
27
|
# passed to the select field as the list of choices.
|
|
24
28
|
class Choices
|
|
@@ -5,11 +5,11 @@ module Trestle
|
|
|
5
5
|
include ::ActionView::Helpers::CaptureHelper
|
|
6
6
|
|
|
7
7
|
# Whitelisted helpers will concatenate their result to the output buffer when called.
|
|
8
|
-
WHITELISTED_HELPERS = [:row, :col, :render, :tab, :
|
|
8
|
+
WHITELISTED_HELPERS = [:row, :col, :render, :tab, :table, :divider, :h1, :h2, :h3, :h4, :h5, :h6, :panel, :well]
|
|
9
9
|
|
|
10
10
|
# Raw block helpers will pass their block argument directly to the method
|
|
11
11
|
# without wrapping it in a new output buffer.
|
|
12
|
-
RAW_BLOCK_HELPERS = [:table]
|
|
12
|
+
RAW_BLOCK_HELPERS = [:table, :toolbar]
|
|
13
13
|
|
|
14
14
|
# The #select and #display methods are defined on Kernel. Undefine them so
|
|
15
15
|
# that they can be delegated to the form builder or template by method_missing.
|
data/lib/trestle/form.rb
CHANGED
|
@@ -8,10 +8,14 @@ module Trestle
|
|
|
8
8
|
autoload :Fields
|
|
9
9
|
autoload :Renderer
|
|
10
10
|
|
|
11
|
-
attr_reader :block
|
|
11
|
+
attr_reader :options, :block
|
|
12
12
|
|
|
13
|
-
def initialize(&block)
|
|
14
|
-
@block = block
|
|
13
|
+
def initialize(options={}, &block)
|
|
14
|
+
@options, @block = options, block
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def dialog?
|
|
18
|
+
options[:dialog] == true
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def render(template, instance)
|
data/lib/trestle/hook.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Trestle
|
|
2
|
+
class Hook
|
|
3
|
+
attr_reader :name, :options, :block
|
|
4
|
+
|
|
5
|
+
def initialize(name, options={}, &block)
|
|
6
|
+
@name, @options, @block = name, options, block
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def ==(other)
|
|
10
|
+
other.is_a?(self.class) && name == other.name && options == other.options && block == other.block
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def visible?(context)
|
|
14
|
+
if options[:if]
|
|
15
|
+
context.instance_exec(&options[:if])
|
|
16
|
+
elsif options[:unless]
|
|
17
|
+
!context.instance_exec(&options[:unless])
|
|
18
|
+
else
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def evaluate(context)
|
|
24
|
+
context.instance_exec(&block)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require "active_model/naming"
|
|
2
|
+
|
|
3
|
+
module Trestle
|
|
4
|
+
class ModelName
|
|
5
|
+
attr_reader :klass
|
|
6
|
+
|
|
7
|
+
delegate :downcase, :upcase, :titleize, :titlecase, to: :to_s
|
|
8
|
+
|
|
9
|
+
def initialize(klass)
|
|
10
|
+
@klass = klass
|
|
11
|
+
@name = klass.respond_to?(:model_name) ? klass.model_name : ActiveModel::Name.new(klass)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def ==(other)
|
|
15
|
+
other.is_a?(self.class) && klass == other.klass
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
singular
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def singular(options={})
|
|
23
|
+
human(default_singular, options)
|
|
24
|
+
end
|
|
25
|
+
alias_method :singularize, :singular
|
|
26
|
+
|
|
27
|
+
def plural(options={})
|
|
28
|
+
if i18n_supported? && i18n_pluralizations_available?
|
|
29
|
+
human(default_plural, { count: :many }.merge(options))
|
|
30
|
+
else
|
|
31
|
+
default_plural
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
alias_method :pluralize, :plural
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
# Default singular version if it cannot be determined from i18n
|
|
38
|
+
def default_singular
|
|
39
|
+
@name.name.demodulize.titleize
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Default plural version if it cannot be determined from i18n
|
|
43
|
+
def default_plural
|
|
44
|
+
singular.pluralize(I18n.locale)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Safely delegates to ActiveModel::Name#human, catching exceptions caused by missing pluralizations
|
|
48
|
+
def human(default, options={})
|
|
49
|
+
@name.human(options.merge(default: default))
|
|
50
|
+
rescue I18n::InvalidPluralizationData
|
|
51
|
+
default
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Checks if the model can be translated by ActiveModel
|
|
55
|
+
def i18n_supported?
|
|
56
|
+
klass.respond_to?(:lookup_ancestors) && klass.respond_to?(:i18n_scope)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Checks if multiple pluralization forms (e.g. zero/one/few/many/other) are available from i18n
|
|
60
|
+
def i18n_pluralizations_available?
|
|
61
|
+
@name.human(count: nil).is_a?(Hash)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -26,6 +26,10 @@ module Trestle
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def merge(other)
|
|
30
|
+
self.class.new(name, options.merge(other.options))
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
def priority
|
|
30
34
|
case options[:priority]
|
|
31
35
|
when :first
|
|
@@ -40,6 +44,10 @@ module Trestle
|
|
|
40
44
|
def label
|
|
41
45
|
I18n.t("admin.navigation.groups.#{name}", default: name.to_s.titlecase)
|
|
42
46
|
end
|
|
47
|
+
|
|
48
|
+
def id
|
|
49
|
+
name.to_s.parameterize
|
|
50
|
+
end
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
class NullGroup
|
|
@@ -59,9 +67,17 @@ module Trestle
|
|
|
59
67
|
NullGroup.hash
|
|
60
68
|
end
|
|
61
69
|
|
|
70
|
+
def id
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
62
74
|
def <=>(other)
|
|
63
75
|
-1
|
|
64
76
|
end
|
|
77
|
+
|
|
78
|
+
def merge(other)
|
|
79
|
+
self
|
|
80
|
+
end
|
|
65
81
|
end
|
|
66
82
|
end
|
|
67
83
|
end
|
|
@@ -40,7 +40,7 @@ module Trestle
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def label
|
|
43
|
-
I18n.t("admin.navigation.items.#{name}", default: name.to_s.titlecase)
|
|
43
|
+
options[:label] || I18n.t("admin.navigation.items.#{name}", default: name.to_s.titlecase)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def icon
|
|
@@ -55,6 +55,16 @@ module Trestle
|
|
|
55
55
|
Badge.new(options[:badge]) if badge?
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def visible?(context)
|
|
59
|
+
if options[:if]
|
|
60
|
+
context.instance_exec(&options[:if])
|
|
61
|
+
elsif options[:unless]
|
|
62
|
+
!context.instance_exec(&options[:unless])
|
|
63
|
+
else
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
58
68
|
class Badge
|
|
59
69
|
attr_reader :text
|
|
60
70
|
|
data/lib/trestle/navigation.rb
CHANGED
|
@@ -7,16 +7,17 @@ module Trestle
|
|
|
7
7
|
autoload :Group
|
|
8
8
|
autoload :NullGroup, "trestle/navigation/group"
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
@blocks = blocks
|
|
12
|
-
end
|
|
10
|
+
attr_reader :items
|
|
13
11
|
|
|
14
|
-
def items
|
|
15
|
-
@
|
|
12
|
+
def initialize(items)
|
|
13
|
+
@items = items
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def by_group
|
|
19
|
-
|
|
17
|
+
sorted_groups = stable_sort(items.group_by { |item| groups[item.group.id] })
|
|
18
|
+
sorted_items = sorted_groups.map { |group, items| [group, stable_sort(items)] }
|
|
19
|
+
|
|
20
|
+
Hash[sorted_items]
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def each(&block)
|
|
@@ -28,9 +29,26 @@ module Trestle
|
|
|
28
29
|
sorted.first.first if sorted.any?
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def visible(context)
|
|
33
|
+
self.class.new(items.select { |item| item.visible?(context) })
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.build(blocks)
|
|
37
|
+
new(blocks.map(&:items).flatten)
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
private
|
|
32
41
|
def stable_sort(items)
|
|
33
42
|
items.sort_by.with_index { |item, i| [item, i] }
|
|
34
43
|
end
|
|
44
|
+
|
|
45
|
+
def groups
|
|
46
|
+
@groups ||= items.inject({}) { |groups, item|
|
|
47
|
+
group = groups[item.group.id]
|
|
48
|
+
|
|
49
|
+
groups[item.group.id] = group ? group.merge(item.group) : item.group
|
|
50
|
+
groups
|
|
51
|
+
}
|
|
52
|
+
end
|
|
35
53
|
end
|
|
36
54
|
end
|
data/lib/trestle/options.rb
CHANGED
|
@@ -4,13 +4,15 @@ module Trestle
|
|
|
4
4
|
self[hash]
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
def merge(other)
|
|
8
|
-
dup.merge!(other)
|
|
7
|
+
def merge(other, &block)
|
|
8
|
+
dup.merge!(other, &block)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def merge!(other)
|
|
12
|
-
|
|
13
|
-
if v1.is_a?(
|
|
11
|
+
def merge!(other, &block)
|
|
12
|
+
super(other || {}) do |key, v1, v2|
|
|
13
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
|
14
|
+
v1.merge(v2, &block)
|
|
15
|
+
elsif v1.is_a?(Array)
|
|
14
16
|
v1 + Array(v2)
|
|
15
17
|
else
|
|
16
18
|
v2
|
data/lib/trestle/reloader.rb
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Trestle
|
|
2
|
+
class Resource
|
|
3
|
+
module AdapterMethods
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Adapter instance bound to the current resource's context.
|
|
7
|
+
def adapter
|
|
8
|
+
@adapter ||= adapter_class.new(self, @context)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
# Declares a method that is handled by the admin's adapter class.
|
|
13
|
+
def adapter_method(name)
|
|
14
|
+
delegate name, to: :adapter
|
|
15
|
+
|
|
16
|
+
singleton_class.class_eval do
|
|
17
|
+
delegate name, to: :adapter
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Defines an admin-specific adapter method.
|
|
22
|
+
#
|
|
23
|
+
# The given block is wrapped rather than passed to #define_method directly, so that
|
|
24
|
+
# adapter methods can be defined with incomplete block parameters. Unfortunately
|
|
25
|
+
# this means we lose the ability to call super from within a custom adapter method.
|
|
26
|
+
def define_adapter_method(name, &block)
|
|
27
|
+
return unless block_given?
|
|
28
|
+
|
|
29
|
+
adapter_methods.module_eval do
|
|
30
|
+
define_method(name) do |*args|
|
|
31
|
+
instance_exec(*args, &block)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the adapter class for this admin.
|
|
37
|
+
#
|
|
38
|
+
# Defaults to a subclass of `Trestle.config.default_adapter` with
|
|
39
|
+
# the admin-specific adapter methods module included.
|
|
40
|
+
def adapter_class
|
|
41
|
+
@adapter_class ||= Class.new(Trestle.config.default_adapter).include(adapter_methods)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Sets an explicit adapter class for this admin.
|
|
45
|
+
# A subclass is created with the admin-specific adapter methods module included.
|
|
46
|
+
def adapter_class=(klass)
|
|
47
|
+
@adapter_class = Class.new(klass).include(adapter_methods)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Module container for admin-specific adapter methods.
|
|
51
|
+
def adapter_methods
|
|
52
|
+
@adapter_methods ||= Module.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Unbound instance of adapter.
|
|
56
|
+
def adapter
|
|
57
|
+
@adapter ||= adapter_class.new(self)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -5,46 +5,53 @@ module Trestle
|
|
|
5
5
|
self.controller = Controller
|
|
6
6
|
|
|
7
7
|
def adapter(&block)
|
|
8
|
-
klass = admin.
|
|
9
|
-
klass.
|
|
8
|
+
klass = admin.adapter_class
|
|
9
|
+
klass.class_eval(&block) if block_given?
|
|
10
10
|
klass
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def adapter=(adapter)
|
|
14
|
-
admin.
|
|
14
|
+
admin.adapter_class = adapter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_action(*actions)
|
|
18
|
+
actions.each do |action|
|
|
19
|
+
controller.remove_possible_method(action.to_sym)
|
|
20
|
+
admin.actions.delete(action.to_sym)
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
def collection(&block)
|
|
18
|
-
admin.collection
|
|
25
|
+
admin.define_adapter_method(:collection, &block)
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
def find_instance(&block)
|
|
22
|
-
admin.find_instance
|
|
29
|
+
admin.define_adapter_method(:find_instance, &block)
|
|
23
30
|
end
|
|
24
31
|
alias instance find_instance
|
|
25
32
|
|
|
26
33
|
def build_instance(&block)
|
|
27
|
-
admin.build_instance
|
|
34
|
+
admin.define_adapter_method(:build_instance, &block)
|
|
28
35
|
end
|
|
29
36
|
|
|
30
37
|
def update_instance(&block)
|
|
31
|
-
admin.update_instance
|
|
38
|
+
admin.define_adapter_method(:update_instance, &block)
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
def save_instance(&block)
|
|
35
|
-
admin.save_instance
|
|
42
|
+
admin.define_adapter_method(:save_instance, &block)
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
def delete_instance(&block)
|
|
39
|
-
admin.delete_instance
|
|
46
|
+
admin.define_adapter_method(:delete_instance, &block)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
49
|
def to_param(&block)
|
|
43
|
-
admin.to_param
|
|
50
|
+
admin.define_adapter_method(:to_param, &block)
|
|
44
51
|
end
|
|
45
52
|
|
|
46
53
|
def params(&block)
|
|
47
|
-
admin.permitted_params
|
|
54
|
+
admin.define_adapter_method(:permitted_params, &block)
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
def decorator(decorator)
|
|
@@ -52,27 +59,28 @@ module Trestle
|
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
def decorate_collection(&block)
|
|
55
|
-
admin.decorate_collection
|
|
62
|
+
admin.define_adapter_method(:decorate_collection, &block)
|
|
56
63
|
end
|
|
57
64
|
|
|
58
65
|
def merge_scopes(&block)
|
|
59
|
-
admin.merge_scopes
|
|
66
|
+
admin.define_adapter_method(:merge_scopes, &block)
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
def sort(&block)
|
|
63
|
-
admin.sort
|
|
70
|
+
admin.define_adapter_method(:sort, &block)
|
|
64
71
|
end
|
|
65
72
|
|
|
66
73
|
def sort_column(column, &block)
|
|
67
74
|
admin.column_sorts[column.to_sym] = block
|
|
68
75
|
end
|
|
69
76
|
|
|
70
|
-
def paginate(&block)
|
|
71
|
-
admin.
|
|
77
|
+
def paginate(options={}, &block)
|
|
78
|
+
admin.pagination_options = admin.pagination_options.merge(options)
|
|
79
|
+
admin.define_adapter_method(:paginate, &block)
|
|
72
80
|
end
|
|
73
81
|
|
|
74
82
|
def count(&block)
|
|
75
|
-
admin.count
|
|
83
|
+
admin.define_adapter_method(:count, &block)
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
def scope(name, scope=nil, options={}, &block)
|
|
@@ -83,6 +91,24 @@ module Trestle
|
|
|
83
91
|
|
|
84
92
|
admin.scopes[name] = Scope.new(admin, name, options, &(scope || block))
|
|
85
93
|
end
|
|
94
|
+
|
|
95
|
+
def return_to(options={}, &block)
|
|
96
|
+
actions = options.key?(:on) ? Array(options[:on]) : [:create, :update, :destroy]
|
|
97
|
+
|
|
98
|
+
actions.each do |action|
|
|
99
|
+
admin.return_locations[action.to_sym] = block
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
protected
|
|
104
|
+
def normalize_table_options(name, options)
|
|
105
|
+
if name.is_a?(Hash)
|
|
106
|
+
# Default index table
|
|
107
|
+
name, options = :index, name.reverse_merge(sortable: true)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
[name, options.reverse_merge(admin: admin)]
|
|
111
|
+
end
|
|
86
112
|
end
|
|
87
113
|
end
|
|
88
114
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Trestle
|
|
2
|
+
class Resource
|
|
3
|
+
class Collection
|
|
4
|
+
delegate :collection, :paginate, :finalize_collection, :decorate_collection,
|
|
5
|
+
:scopes, :merge_scopes, :column_sorts, :sort, to: :@admin
|
|
6
|
+
|
|
7
|
+
def initialize(admin, options={})
|
|
8
|
+
@admin, @options = admin, options
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def prepare(params)
|
|
12
|
+
collection = collection(params)
|
|
13
|
+
collection = apply_scopes(collection, params) if scope?
|
|
14
|
+
collection = apply_sorting(collection, params) if sort?
|
|
15
|
+
collection = paginate(collection, params) if paginate?
|
|
16
|
+
collection = finalize_collection(collection) if finalize?
|
|
17
|
+
collection = decorate_collection(collection) if decorate?
|
|
18
|
+
collection
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def scope?
|
|
22
|
+
@options[:scope] != false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def sort?
|
|
26
|
+
@options[:sort] != false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def paginate?
|
|
30
|
+
@options[:paginate] != false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def finalize?
|
|
34
|
+
@options[:finalize] != false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def decorate?
|
|
38
|
+
@options[:decorate] != false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def apply_scopes(collection, params)
|
|
43
|
+
unscoped = collection(params)
|
|
44
|
+
|
|
45
|
+
active_scopes(params).reduce(collection) do |collection, scope|
|
|
46
|
+
merge_scopes(collection, scope.apply(unscoped))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def active_scopes(params)
|
|
51
|
+
scopes.values.select { |s| s.active?(params) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def apply_sorting(collection, params)
|
|
55
|
+
return collection unless params[:sort]
|
|
56
|
+
|
|
57
|
+
field = params[:sort].to_sym
|
|
58
|
+
order = params[:order].to_s.downcase == "desc" ? :desc : :asc
|
|
59
|
+
|
|
60
|
+
if column_sorts.has_key?(field)
|
|
61
|
+
@admin.instance_exec(collection, order, &column_sorts[field])
|
|
62
|
+
else
|
|
63
|
+
sort(collection, field, order)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|