trestle 0.9.1 → 0.9.2
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 +4 -4
- data/app/assets/bundle/trestle/bundle.css +0 -0
- data/app/assets/bundle/trestle/bundle.js +0 -0
- data/app/assets/javascripts/trestle/admin.js +1 -0
- data/app/assets/javascripts/trestle/i18n.js.erb +8 -0
- data/app/helpers/trestle/grid_helper.rb +44 -4
- data/app/helpers/trestle/i18n_helper.rb +8 -15
- data/app/helpers/trestle/table_helper.rb +7 -3
- data/app/helpers/trestle/title_helper.rb +11 -0
- data/app/helpers/trestle/toolbars_helper.rb +0 -11
- data/app/views/layouts/trestle/admin.html.erb +4 -4
- data/app/views/trestle/application/_dialog.html.erb +1 -1
- data/app/views/trestle/application/_header.html.erb +1 -3
- data/app/views/trestle/resource/edit.html.erb +2 -2
- data/app/views/trestle/resource/index.html.erb +1 -1
- data/app/views/trestle/resource/new.html.erb +1 -1
- data/app/views/trestle/resource/show.html.erb +2 -2
- data/app/views/trestle/shared/_sidebar.html.erb +1 -1
- data/config/locales/de.rb +18 -0
- data/config/locales/de.yml +101 -0
- data/frontend/js/components/tabs.js +2 -2
- data/lib/generators/trestle/resource/resource_generator.rb +15 -1
- data/lib/generators/trestle/resource/templates/admin.rb.erb +15 -8
- data/lib/trestle.rb +13 -0
- data/lib/trestle/adapters/active_record_adapter.rb +1 -1
- data/lib/trestle/admin.rb +5 -2
- data/lib/trestle/admin/builder.rb +5 -1
- data/lib/trestle/admin/controller.rb +8 -1
- data/lib/trestle/application_controller.rb +2 -0
- data/lib/trestle/configuration.rb +1 -1
- data/lib/trestle/controller/breadcrumbs.rb +1 -1
- data/lib/trestle/controller/title.rb +20 -0
- data/lib/trestle/controller/toolbars.rb +30 -0
- data/lib/trestle/form/automatic.rb +2 -2
- data/lib/trestle/form/field.rb +12 -2
- data/lib/trestle/form/fields/check_box_helpers.rb +1 -1
- data/lib/trestle/form/fields/collection_select.rb +1 -1
- data/lib/trestle/form/fields/date_select.rb +1 -1
- data/lib/trestle/form/fields/datetime_select.rb +1 -1
- data/lib/trestle/form/fields/select.rb +1 -1
- data/lib/trestle/form/fields/static_field.rb +5 -1
- data/lib/trestle/form/fields/tag_select.rb +1 -1
- data/lib/trestle/form/fields/time_select.rb +1 -1
- data/lib/trestle/form/fields/time_zone_select.rb +1 -1
- data/lib/trestle/navigation/item.rb +4 -0
- data/lib/trestle/resource.rb +1 -0
- data/lib/trestle/resource/controller.rb +9 -166
- data/lib/trestle/resource/controller/actions.rb +133 -0
- data/lib/trestle/resource/controller/data_methods.rb +52 -0
- data/lib/trestle/resource/controller/redirection.rb +23 -0
- data/lib/trestle/resource/controller/toolbar.rb +11 -0
- data/lib/trestle/resource/toolbar.rb +29 -0
- data/lib/trestle/table.rb +6 -0
- data/lib/trestle/version.rb +1 -1
- data/trestle.gemspec +1 -0
- data/yarn.lock +518 -496
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ec0dcceb14b2184fb07724245272ba57926c91058ed554cb19653149aa5a61d
|
4
|
+
data.tar.gz: ec4b694ff3649bb301dffad74fba26fce2dc222c52ffeb48ff205fcda95c1e98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e07e61553b61a4f0ed93efc32178290de7623a37fb58e8caf4ce170e8b09cf1d15b1765b7cd28544e99c841fa5de25bd78d02220308da2ce98cc02fb2af36938
|
7
|
+
data.tar.gz: 58eb9ad54d778ace1d86fc8e7f39b069604d15049af4f3bd05b6cef8c629b9eec0b11589e44e5f63a371632d1f45396873eb3ac9c395c4ae002108c47f77e2b5
|
Binary file
|
Binary file
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% flatpickr_locale_conversions = { ca: "cat", el: "gr", nb: "no", vi: "vn" } %>
|
2
|
+
|
3
|
+
<% I18n.available_locales.each do |locale| %>
|
4
|
+
<% Trestle.i18n_fallbacks(locale).each do |candidate| %>
|
5
|
+
<% candidate = flatpickr_locale_conversions[candidate] || candidate %>
|
6
|
+
<% require_asset("trestle/flatpickr/#{candidate}") rescue nil %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
@@ -1,9 +1,44 @@
|
|
1
1
|
module Trestle
|
2
2
|
module GridHelper
|
3
|
-
|
4
|
-
|
3
|
+
# Renders a row div, one of the building blocks of Bootstrap's grid system.
|
4
|
+
# https://getbootstrap.com/docs/4.4/layout/grid/
|
5
|
+
#
|
6
|
+
# attrs - Hash of attributes that will be passed to the tag (e.g. id, data, class).
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# <%= row do %>
|
11
|
+
# <%= col do %>Column content<% end %>
|
12
|
+
# <% end %>
|
13
|
+
#
|
14
|
+
# <%= row class: "row-cols-2", id: "my-row" do %> ...
|
15
|
+
#
|
16
|
+
# Returns a HTML-safe String.
|
17
|
+
def row(attrs={})
|
18
|
+
defaults = Trestle::Options.new(class: ["row"])
|
19
|
+
options = defaults.merge(attrs)
|
20
|
+
|
21
|
+
content_tag(:div, options) { yield }
|
5
22
|
end
|
6
23
|
|
24
|
+
# Renders a column div, one of the building blocks of Bootstrap's grid system.
|
25
|
+
# https://getbootstrap.com/docs/4.4/layout/grid/
|
26
|
+
#
|
27
|
+
# Column divs should always be rendered inside of a row div.
|
28
|
+
#
|
29
|
+
# Examples
|
30
|
+
#
|
31
|
+
# # Standard column - evenly fills available space
|
32
|
+
# <%= col do %>...<% end %>
|
33
|
+
#
|
34
|
+
# # Column spans 4 (out of 12) grid columns (i.e. 1/3 width) at all breakpoints
|
35
|
+
# <%= col 4 do %> ...
|
36
|
+
#
|
37
|
+
# # Column spans 6 grid columns at smallest breakpoint, 4 at md breakpoint
|
38
|
+
# # and above (portrait tablet) and 3 at xl breakpoint and above (desktop)
|
39
|
+
# <%= col 6, md: 4, xl: 3 do %> ...
|
40
|
+
#
|
41
|
+
# Returns a HTML-safe String.
|
7
42
|
def col(columns=nil, breakpoints={})
|
8
43
|
if columns.is_a?(Hash)
|
9
44
|
breakpoints = columns
|
@@ -17,8 +52,13 @@ module Trestle
|
|
17
52
|
content_tag(:div, class: classes) { yield }
|
18
53
|
end
|
19
54
|
|
20
|
-
|
21
|
-
|
55
|
+
# Renders an <HR> (horizontal rule) HTML tag.
|
56
|
+
#
|
57
|
+
# attrs - Hash of attributes that will be passed to the tag (e.g. id, data, class).
|
58
|
+
#
|
59
|
+
# Returns a HTML-safe String.
|
60
|
+
def divider(attrs={})
|
61
|
+
tag(:hr, attrs)
|
22
62
|
end
|
23
63
|
end
|
24
64
|
end
|
@@ -1,20 +1,13 @@
|
|
1
1
|
module Trestle
|
2
2
|
module I18nHelper
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def default_translation(key)
|
15
|
-
translate(key, locale: :en)
|
16
|
-
rescue I18n::InvalidLocale
|
17
|
-
key.split(".").last.humanize
|
3
|
+
def i18n_javascript_translations
|
4
|
+
Trestle.config.javascript_i18n_keys.map { |key|
|
5
|
+
begin
|
6
|
+
[key, t(key, raise: true)]
|
7
|
+
rescue I18n::MissingTranslationData
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
}.compact
|
18
11
|
end
|
19
12
|
end
|
20
13
|
end
|
@@ -32,10 +32,14 @@ module Trestle
|
|
32
32
|
end
|
33
33
|
|
34
34
|
table = Table::Builder.build(options, &block)
|
35
|
-
elsif name.is_a?(Trestle::Table)
|
36
|
-
table = name
|
37
35
|
else
|
38
|
-
|
36
|
+
if name.is_a?(Trestle::Table)
|
37
|
+
table = name
|
38
|
+
else
|
39
|
+
table = admin.tables.fetch(name) { raise ArgumentError, "Unable to find table named #{name.inspect}" }
|
40
|
+
end
|
41
|
+
|
42
|
+
table = table.with_options(options.reverse_merge(sortable: false))
|
39
43
|
end
|
40
44
|
|
41
45
|
collection ||= options[:collection] || table.options[:collection]
|
@@ -1,16 +1,5 @@
|
|
1
1
|
module Trestle
|
2
2
|
module ToolbarsHelper
|
3
|
-
def toolbar(name, options={}, &block)
|
4
|
-
toolbar = (toolbars[name.to_s] ||= Toolbar.new)
|
5
|
-
toolbar.clear! if options[:clear]
|
6
|
-
toolbar.prepend(&block) if block_given?
|
7
|
-
toolbar
|
8
|
-
end
|
9
|
-
|
10
|
-
def toolbars
|
11
|
-
@_toolbars ||= {}
|
12
|
-
end
|
13
|
-
|
14
3
|
def render_toolbar(toolbar, *args)
|
15
4
|
result = toolbar.groups(self, *args).map do |items|
|
16
5
|
if items.many?
|
@@ -12,7 +12,7 @@
|
|
12
12
|
|
13
13
|
<%= csrf_meta_tags %>
|
14
14
|
|
15
|
-
<title><%= [
|
15
|
+
<title><%= [title, Trestle.config.site_title].join(' :: ') %></title>
|
16
16
|
|
17
17
|
<%= favicon_link_tag Trestle.config.favicon if Trestle.config.favicon %>
|
18
18
|
|
@@ -26,11 +26,11 @@
|
|
26
26
|
<%= javascript_include_tag "trestle/admin", 'data-turbolinks-track': 'reload' %>
|
27
27
|
|
28
28
|
<%= javascript_tag nonce: true do %>
|
29
|
-
<%
|
30
|
-
Trestle.i18n['<%= key %>'] = "<%= escape_javascript(
|
29
|
+
<% i18n_javascript_translations.each do |key, value| %>
|
30
|
+
Trestle.i18n['<%= key %>'] = "<%= escape_javascript(value) %>";
|
31
31
|
<% end %>
|
32
32
|
|
33
|
-
Trestle.localize(<%= i18n_fallbacks.map { |l| "'#{l}'" }.join(", ").html_safe %>);
|
33
|
+
Trestle.localize(<%= Trestle.i18n_fallbacks.map { |l| "'#{l}'" }.join(", ").html_safe %>);
|
34
34
|
<% end %>
|
35
35
|
|
36
36
|
<%= hook :javascripts %>
|
@@ -4,11 +4,11 @@
|
|
4
4
|
<% breadcrumb(title) %>
|
5
5
|
|
6
6
|
<% toolbar(:primary) do |t| %>
|
7
|
-
<%= t.
|
7
|
+
<%= t.save if admin.actions.include?(:update) %>
|
8
8
|
<% end %>
|
9
9
|
|
10
10
|
<% toolbar(:secondary) do |t| %>
|
11
|
-
<%= t.
|
11
|
+
<%= t.delete if admin.actions.include?(:destroy) %>
|
12
12
|
<% end %>
|
13
13
|
|
14
14
|
<%= trestle_form_for instance, url: admin.actions.include?(:update) ? admin.instance_path(instance, action: :update) : "#", method: :patch do |f| %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<% content_for(:title, admin.t("titles.index", default: "Listing %{pluralized_model_name}")) %>
|
2
2
|
|
3
3
|
<% toolbar(:primary) do |t| %>
|
4
|
-
<%= t.
|
4
|
+
<%= t.new if admin.actions.include?(:new) %>
|
5
5
|
<% end %>
|
6
6
|
|
7
7
|
<% content_for(:utilities) do %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<% breadcrumb(title) %>
|
5
5
|
|
6
6
|
<% toolbar(:primary) do |t| %>
|
7
|
-
<%= t.
|
7
|
+
<%= t.save if admin.actions.include?(:create) %>
|
8
8
|
<% end %>
|
9
9
|
|
10
10
|
<%= trestle_form_for instance, url: admin.path(:create), method: :post do |f| %>
|
@@ -4,11 +4,11 @@
|
|
4
4
|
<% breadcrumb(title) unless admin.singular? %>
|
5
5
|
|
6
6
|
<% toolbar(:primary) do |t| %>
|
7
|
-
<%= t.
|
7
|
+
<%= t.save if admin.actions.include?(:update) %>
|
8
8
|
<% end %>
|
9
9
|
|
10
10
|
<% toolbar(:secondary) do |t| %>
|
11
|
-
<%= t.
|
11
|
+
<%= t.delete if admin.actions.include?(:destroy) %>
|
12
12
|
<% end %>
|
13
13
|
|
14
14
|
<%= trestle_form_for instance, url: admin.actions.include?(:update) ? admin.instance_path(instance, action: :update) : "#", method: :patch do |f| %>
|
@@ -20,7 +20,7 @@
|
|
20
20
|
|
21
21
|
<% items.each do |item| %>
|
22
22
|
<li<% if current_navigation_item?(item) %> class="active"<% end %>>
|
23
|
-
<%= link_to item.path do %>
|
23
|
+
<%= link_to item.path, item.html_options do %>
|
24
24
|
<%= icon("nav-icon", item.icon) %>
|
25
25
|
<span class="nav-label"><%= item.label %></span>
|
26
26
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
de: {
|
3
|
+
date: {
|
4
|
+
formats: {
|
5
|
+
trestle_date: "%-d. %b. %Y",
|
6
|
+
trestle_calendar: "%d.%m.%Y"
|
7
|
+
}
|
8
|
+
},
|
9
|
+
|
10
|
+
time: {
|
11
|
+
formats: {
|
12
|
+
trestle_date: "%-d. %b. %Y",
|
13
|
+
trestle_time: "%H:%M",
|
14
|
+
trestle_time_with_seconds: "%H:%M:%S"
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
de:
|
2
|
+
trestle:
|
3
|
+
title: "Trestle Admin"
|
4
|
+
footer: "Powered by Trestle"
|
5
|
+
version: "Version"
|
6
|
+
|
7
|
+
helpers:
|
8
|
+
page_entries_info:
|
9
|
+
one_page:
|
10
|
+
display_entries:
|
11
|
+
zero: "Kein %{entry_name} vorhanden"
|
12
|
+
one: "<strong>1</strong> %{entry_name}"
|
13
|
+
other: "<strong>%{count}</strong> %{entry_name}"
|
14
|
+
|
15
|
+
more_pages:
|
16
|
+
display_entries: "%{entry_name} <strong>%{first} - %{last}</strong> von <b>%{total}</b>"
|
17
|
+
|
18
|
+
onboarding:
|
19
|
+
welcome: "Willkommen bei Trestle"
|
20
|
+
no_admins: "Um zu beginnen, erstelle eine Trestle.resource in <code>app/admin</code>."
|
21
|
+
no_template: "Um dieses Tempalte anzupassen, erstelle <code>%{path}</code>."
|
22
|
+
no_form: "Definiere einen form block oder erstelle ein Partial <code>_form.html</code>."
|
23
|
+
|
24
|
+
ui:
|
25
|
+
toggle_navigation: "Navigation ein-/ausblenden"
|
26
|
+
toggle_sidebar: "Seitenleiste ein-/ausblenden"
|
27
|
+
toggle_dropdown: "Menü ein-/ausblenden"
|
28
|
+
|
29
|
+
dialog:
|
30
|
+
error: "Fehler: Die Aktion konnte nicht erfolgreich ausgeführt werden."
|
31
|
+
|
32
|
+
confirmation:
|
33
|
+
title: "Sind Sie sicher?"
|
34
|
+
delete: "Löschen"
|
35
|
+
cancel: "Abbrechen"
|
36
|
+
|
37
|
+
file:
|
38
|
+
choose_file: "Datei wählen..."
|
39
|
+
browse: "Durchsuchen"
|
40
|
+
|
41
|
+
admin:
|
42
|
+
titles:
|
43
|
+
index: "Liste von %{pluralized_model_name}"
|
44
|
+
new: "%{model_name} (neu)"
|
45
|
+
edit: "%{model_name} (bearbeiten)"
|
46
|
+
|
47
|
+
buttons:
|
48
|
+
new: "Neu"
|
49
|
+
save: "Speichern"
|
50
|
+
delete: "Löschen"
|
51
|
+
show: "Details"
|
52
|
+
edit: "Bearbeiten"
|
53
|
+
ok: "OK"
|
54
|
+
|
55
|
+
breadcrumbs:
|
56
|
+
home: "Home"
|
57
|
+
|
58
|
+
flash:
|
59
|
+
create:
|
60
|
+
success:
|
61
|
+
title: "Erfolgreich erstellt"
|
62
|
+
message: "%{model_name} wurde erfolgreich erstellt."
|
63
|
+
|
64
|
+
failure:
|
65
|
+
title: "Warnung: %{model_name} nicht erstellt"
|
66
|
+
message: "Bitte korrigieren Sie die unten angezeigten Fehler."
|
67
|
+
|
68
|
+
update:
|
69
|
+
success:
|
70
|
+
title: "Erfolgreich bearbeitet"
|
71
|
+
message: "%{model_name} wurde erfolgreich gespeichert."
|
72
|
+
|
73
|
+
failure:
|
74
|
+
title: "Warnung: %{model_name} nicht gespeichert"
|
75
|
+
message: "Bitte korrigieren Sie die unten angezeigten Fehler."
|
76
|
+
|
77
|
+
destroy:
|
78
|
+
success:
|
79
|
+
title: "Erfolgreich gelöscht"
|
80
|
+
message: "%{model_name} wurde erfolgreich gelöscht."
|
81
|
+
|
82
|
+
failure:
|
83
|
+
title: "Warnung: Nicht gelöscht"
|
84
|
+
message: "%{model_name} konnte nicht gelöscht werden."
|
85
|
+
|
86
|
+
table:
|
87
|
+
headers:
|
88
|
+
id: "ID"
|
89
|
+
|
90
|
+
form:
|
91
|
+
select:
|
92
|
+
prompt: "- %{attribute_name} auswählen -"
|
93
|
+
|
94
|
+
format:
|
95
|
+
blank: "n. a."
|
96
|
+
|
97
|
+
datepicker:
|
98
|
+
formats:
|
99
|
+
date: "d.m.Y"
|
100
|
+
datetime: "d.m.Y H:i"
|
101
|
+
time: "H:i"
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import $ from 'jquery'
|
4
4
|
|
5
|
-
import { init
|
5
|
+
import { init } from '../core/events'
|
6
6
|
|
7
7
|
const TAB_SELECTOR = 'a[data-toggle="tab"]'
|
8
8
|
const TAB_PANE_SELECTOR = '.tab-pane'
|
@@ -21,7 +21,7 @@ init(function (root) {
|
|
21
21
|
})
|
22
22
|
|
23
23
|
// Restore active tab when loading
|
24
|
-
|
24
|
+
init(function () {
|
25
25
|
focusActiveTab()
|
26
26
|
})
|
27
27
|
|
@@ -3,10 +3,24 @@ module Trestle
|
|
3
3
|
class ResourceGenerator < ::Rails::Generators::NamedBase
|
4
4
|
desc "Creates a Trestle admin resource"
|
5
5
|
|
6
|
+
class_option :singular, type: :boolean, default: false, desc: "Generate a singular resource"
|
7
|
+
|
6
8
|
source_root File.expand_path("../templates", __FILE__)
|
7
9
|
|
8
10
|
def create_admin
|
9
|
-
template "admin.rb.erb", File.join("app/admin", class_path, "#{
|
11
|
+
template "admin.rb.erb", File.join("app/admin", class_path, "#{admin_name}_admin.rb")
|
12
|
+
end
|
13
|
+
|
14
|
+
def admin_name
|
15
|
+
singular? ? singular_name : plural_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def parameter_name
|
19
|
+
singular_name.singularize
|
20
|
+
end
|
21
|
+
|
22
|
+
def singular?
|
23
|
+
options[:singular]
|
10
24
|
end
|
11
25
|
|
12
26
|
protected
|
@@ -1,8 +1,14 @@
|
|
1
|
-
Trestle.resource(:<%=
|
1
|
+
Trestle.resource(:<%= admin_name %><% if module? %>, scope: <%= module_name %><% end %><% if singular? %>, singular: true<% end %>) do
|
2
2
|
menu do
|
3
|
-
item :<%=
|
3
|
+
item :<%= admin_name %>, icon: "fa fa-star"
|
4
4
|
end
|
5
5
|
|
6
|
+
<%- if singular? -%>
|
7
|
+
instance do
|
8
|
+
# Override this block to define the instance that this resource represents.
|
9
|
+
raise NotImplementedError, "Singular resources must define an instance block."
|
10
|
+
end
|
11
|
+
<%- else -%>
|
6
12
|
# Customize the table columns shown on the index view.
|
7
13
|
#
|
8
14
|
# table do
|
@@ -10,19 +16,20 @@ Trestle.resource(:<%= plural_name %><% if module? %>, scope: <%= module_name %><
|
|
10
16
|
# column :created_at, align: :center
|
11
17
|
# actions
|
12
18
|
# end
|
19
|
+
<%- end -%>
|
13
20
|
|
14
|
-
# Customize the form fields shown on the new/edit views
|
21
|
+
# Customize the form fields shown on the <% if singular? %>edit view<% else %>new/edit views<% end %>.
|
15
22
|
#
|
16
|
-
# form do |<%=
|
23
|
+
# form do |<%= parameter_name %>|
|
17
24
|
# text_field :name
|
18
25
|
#
|
19
26
|
# row do
|
20
|
-
# col
|
21
|
-
# col
|
27
|
+
# col { datetime_field :updated_at }
|
28
|
+
# col { datetime_field :created_at }
|
22
29
|
# end
|
23
30
|
# end
|
24
31
|
|
25
|
-
# By default, all parameters passed to the update and create actions will be
|
32
|
+
# By default, all parameters passed to the <% if singular? %>update action<% else %>update and create actions<% end %> will be
|
26
33
|
# permitted. If you do not have full trust in your users, you should explicitly
|
27
34
|
# define the list of permitted parameters.
|
28
35
|
#
|
@@ -30,6 +37,6 @@ Trestle.resource(:<%= plural_name %><% if module? %>, scope: <%= module_name %><
|
|
30
37
|
# http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters
|
31
38
|
#
|
32
39
|
# params do |params|
|
33
|
-
# params.require(:<%=
|
40
|
+
# params.require(:<%= parameter_name %>).permit(:name, ...)
|
34
41
|
# end
|
35
42
|
end
|