headmin 0.3.2 → 0.3.3
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/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +27 -23
- data/app/assets/images/document.docx +0 -0
- data/app/assets/images/document.pdf +0 -0
- data/app/assets/images/image.jpg +0 -0
- data/app/assets/images/spreadsheet.xls +0 -0
- data/app/assets/images/video.mp4 +0 -0
- data/app/assets/javascripts/headmin/controllers/table_actions_controller.js +2 -2
- data/app/assets/javascripts/headmin/controllers/table_controller.js +28 -13
- data/app/assets/javascripts/headmin/index.js +0 -4
- data/app/assets/javascripts/headmin.js +33 -696
- data/app/assets/stylesheets/headmin/form.scss +4 -4
- data/app/assets/stylesheets/headmin/vendor/tom-select-bootstrap.css +1 -2
- data/app/assets/stylesheets/headmin.css +3 -3
- data/app/controllers/concerns/headmin/authentication.rb +1 -1
- data/app/controllers/concerns/headmin/searchable.rb +1 -1
- data/app/controllers/concerns/headmin/sortable.rb +7 -7
- data/app/helpers/headmin/admin_helper.rb +1 -1
- data/app/helpers/headmin/bootstrap_helper.rb +2 -24
- data/app/helpers/headmin/documentation_helper.rb +1 -1
- data/app/helpers/headmin/filter_helper.rb +1 -1
- data/app/helpers/headmin/form_helper.rb +3 -3
- data/app/helpers/headmin/notification_helper.rb +21 -21
- data/app/helpers/headmin/request_helper.rb +3 -3
- data/app/models/concerns/headmin/block.rb +1 -2
- data/app/models/concerns/headmin/blockable.rb +1 -1
- data/app/models/concerns/headmin/field.rb +1 -1
- data/app/models/concerns/headmin/fieldable.rb +8 -8
- data/app/models/headmin/documentation_renderer.rb +4 -4
- data/app/models/headmin/form/base.rb +1 -2
- data/app/models/headmin/form/text.rb +5 -7
- data/app/models/headmin/thumbnail.rb +61 -0
- data/app/services/block_service.rb +53 -53
- data/app/views/headmin/_thumbnail.html.erb +42 -40
- data/app/views/headmin/dropdown/_item.html.erb +1 -1
- data/app/views/headmin/forms/_checkbox.html.erb +1 -1
- data/app/views/headmin/forms/_file.html.erb +1 -1
- data/app/views/headmin/forms/_repeater.html.erb +1 -1
- data/app/views/headmin/forms/actions/_destroy.html.erb +3 -3
- data/app/views/headmin/forms/fields/_group.html.erb +1 -1
- data/app/views/headmin/nav/item/_devise.html.erb +1 -1
- data/app/views/headmin/table/_actions.html.erb +1 -1
- data/app/views/headmin/views/devise/registrations/_edit.html.erb +2 -2
- data/config/initializers/customize_input_error.rb +4 -4
- data/lib/generators/headmin/blocks_generator.rb +8 -8
- data/lib/generators/headmin/devise_generator.rb +4 -4
- data/lib/generators/headmin/fields_generator.rb +9 -9
- data/lib/generators/templates/controllers/auth/confirmations_controller.rb +1 -1
- data/lib/generators/templates/controllers/auth/omniauth_callbacks_controller.rb +1 -1
- data/lib/generators/templates/controllers/auth/passwords_controller.rb +1 -1
- data/lib/generators/templates/controllers/auth/registrations_controller.rb +1 -1
- data/lib/generators/templates/controllers/auth/sessions_controller.rb +1 -1
- data/lib/generators/templates/controllers/auth/unlocks_controller.rb +1 -1
- data/lib/generators/templates/models/block.rb +1 -1
- data/lib/headmin/engine.rb +6 -6
- data/lib/headmin/version.rb +1 -1
- data/package.json +0 -1
- data/yarn.lock +1 -258
- metadata +9 -4
- data/.rubocop.yml +0 -13
@@ -27,16 +27,16 @@ input[type="search"] {
|
|
27
27
|
.repeater-row {
|
28
28
|
position: relative;
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
&:hover {
|
31
|
+
.repeater-row-remove {
|
32
32
|
visibility: visible;
|
33
33
|
}
|
34
34
|
|
35
|
-
|
35
|
+
.repeater-row-add {
|
36
36
|
visibility: visible;
|
37
37
|
}
|
38
38
|
|
39
|
-
|
39
|
+
.repeater-row-handle {
|
40
40
|
visibility: visible;
|
41
41
|
}
|
42
42
|
}
|
@@ -12937,13 +12937,13 @@ input[type=search] {
|
|
12937
12937
|
.repeater-row {
|
12938
12938
|
position: relative;
|
12939
12939
|
}
|
12940
|
-
.repeater-row
|
12940
|
+
.repeater-row:hover .repeater-row-remove {
|
12941
12941
|
visibility: visible;
|
12942
12942
|
}
|
12943
|
-
.repeater-row
|
12943
|
+
.repeater-row:hover .repeater-row-add {
|
12944
12944
|
visibility: visible;
|
12945
12945
|
}
|
12946
|
-
.repeater-row
|
12946
|
+
.repeater-row:hover .repeater-row-handle {
|
12947
12947
|
visibility: visible;
|
12948
12948
|
}
|
12949
12949
|
.repeater-row-remove {
|
@@ -12,8 +12,8 @@ module Headmin
|
|
12
12
|
|
13
13
|
def sort_by_params(collection)
|
14
14
|
sort_params.each do |key, value|
|
15
|
-
next unless %w
|
16
|
-
attribute = key.gsub(
|
15
|
+
next unless %w[asc desc].include?(value)
|
16
|
+
attribute = key.gsub("sort_", "")
|
17
17
|
|
18
18
|
# Sort on model columns first, then check for translation columns
|
19
19
|
if collection.attribute_names.include?(attribute.to_s)
|
@@ -26,19 +26,19 @@ module Headmin
|
|
26
26
|
|
27
27
|
order_scope = "order_by_#{attribute}".to_sym
|
28
28
|
sort_scope = "sort_by_#{attribute}".to_sym
|
29
|
-
if collection.respond_to?(order_scope)
|
30
|
-
collection
|
29
|
+
collection = if collection.respond_to?(order_scope)
|
30
|
+
collection.send(order_scope, value)
|
31
31
|
elsif collection.respond_to?(sort_scope)
|
32
|
-
collection
|
32
|
+
collection.send(sort_scope, value)
|
33
33
|
else
|
34
|
-
collection
|
34
|
+
collection.order(column.send(value))
|
35
35
|
end
|
36
36
|
end
|
37
37
|
collection
|
38
38
|
end
|
39
39
|
|
40
40
|
def sort_params
|
41
|
-
params.permit!.select { |p| p.to_s.include?(
|
41
|
+
params.permit!.select { |p| p.to_s.include?("sort_") }
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -2,30 +2,8 @@ module Headmin
|
|
2
2
|
module BootstrapHelper
|
3
3
|
def bootstrap_icon(icon, options = {})
|
4
4
|
tag_name = options.extract!(:tag_name).fetch(:tag_name, :i)
|
5
|
-
options[:class] = [
|
5
|
+
options[:class] = ["bi", "bi-#{icon}", options.dig(:class)].compact.join(" ")
|
6
6
|
content_tag(tag_name, options) {}
|
7
7
|
end
|
8
|
-
|
9
|
-
# References
|
10
|
-
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
11
|
-
def bootstrap_icon_for_mime_type(mime_type, options = {})
|
12
|
-
type_map = {
|
13
|
-
image: %w[image/bmp image/gif image/vnd.microsoft.icon image/jpeg image/png image/svg+xml image/tiff image/webp],
|
14
|
-
play: %w[video/mp4 video/mpeg video/ogg video/mp2t video/webm video/3gpp video/3gpp2],
|
15
|
-
music: %w[audio/aac audio/midi audio/x-midi audio/mpeg audio/ogg audio/opus audio/wav audio/webm audio/3gpp audio/3gpp2],
|
16
|
-
word: %w[application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document],
|
17
|
-
ppt: %w[application/vnd.ms-powerpoint application/vnd.openxmlformats-officedocument.presentationml.presentation],
|
18
|
-
excel: %w[application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet],
|
19
|
-
slides: %w[application/vnd.oasis.opendocument.presentation],
|
20
|
-
spreadsheet: %w[application/vnd.oasis.opendocument.spreadsheet],
|
21
|
-
richtext: %w[application/vnd.oasis.opendocument.text],
|
22
|
-
zip: %w[application/zip application/x-7z-compressed application/x-bzip application/x-bzip2 application/gzip application/vnd.rar],
|
23
|
-
pdf: %w[application/pdf],
|
24
|
-
}
|
25
|
-
icon_name = type_map.find{|key, mime_types| mime_types.include?(mime_type)}.first
|
26
|
-
|
27
|
-
icon = ['file', 'earmark', icon_name].compact.join('-')
|
28
|
-
bootstrap_icon(icon, options)
|
29
|
-
end
|
30
8
|
end
|
31
|
-
end
|
9
|
+
end
|
@@ -7,7 +7,7 @@ module Headmin
|
|
7
7
|
between = text[/#{starts_with}(.*?)#{ends_with}/m, 1]
|
8
8
|
|
9
9
|
# Remove leading whitespaces from each line
|
10
|
-
between.lines.map { |line| line.sub(
|
10
|
+
between.lines.map { |line| line.sub(" ", "") }.join
|
11
11
|
end
|
12
12
|
|
13
13
|
def markdown_to_html(text)
|
@@ -5,12 +5,12 @@ module Headmin
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def form_field_validation_id(form, name)
|
8
|
-
[form.object_name, name.to_s,
|
8
|
+
[form.object_name, name.to_s, "validation"].join("_").parameterize.underscore
|
9
9
|
end
|
10
10
|
|
11
11
|
def form_field_validation_class(form, name)
|
12
12
|
return nil if request.get?
|
13
|
-
form.object.errors.has_key?(name) ?
|
13
|
+
form.object.errors.has_key?(name) ? "is-invalid" : "is-valid"
|
14
14
|
end
|
15
15
|
|
16
16
|
def human_attribute_name(attribute)
|
@@ -37,4 +37,4 @@ module Headmin
|
|
37
37
|
test.join.html_safe
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end
|
@@ -2,30 +2,30 @@ module Headmin
|
|
2
2
|
module NotificationHelper
|
3
3
|
def notification_color(name)
|
4
4
|
color = {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
5
|
+
notice: {
|
6
|
+
background: "bg-success",
|
7
|
+
text: "text-white"
|
8
|
+
},
|
9
|
+
error: {
|
10
|
+
text: "text-dark",
|
11
|
+
background: "bg-danger"
|
12
|
+
},
|
13
|
+
alert: {
|
14
|
+
text: "text-dark",
|
15
|
+
background: "bg-warning"
|
16
|
+
},
|
17
|
+
timedout: {
|
18
|
+
text: "text-dark",
|
19
|
+
background: "bg-warning"
|
20
|
+
},
|
21
|
+
fallback: {
|
22
|
+
text: "text-light",
|
23
|
+
background: "bg-info"
|
24
|
+
}
|
25
25
|
}
|
26
26
|
|
27
27
|
name = name.to_sym
|
28
28
|
color.key?(name) ? color[name] : color[:fallback]
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -3,7 +3,7 @@ module Headmin
|
|
3
3
|
def current_url?(url)
|
4
4
|
uri = URI(url)
|
5
5
|
path = uri.path
|
6
|
-
query_string = uri.query ||
|
6
|
+
query_string = uri.query || ""
|
7
7
|
matches_path = request.path.include?(path)
|
8
8
|
matches_query = request.query_string.include?(query_string)
|
9
9
|
matches_path && matches_query
|
@@ -28,7 +28,7 @@ module Headmin
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def is_sort_param_key?(key)
|
31
|
-
key.to_s.include?(
|
31
|
+
key.to_s.include?("sort_")
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
@@ -24,27 +24,27 @@ module Headmin
|
|
24
24
|
when Hash
|
25
25
|
fields.build(
|
26
26
|
name: key,
|
27
|
-
field_type:
|
27
|
+
field_type: "group",
|
28
28
|
fields: parse_fields_hash(value)
|
29
29
|
)
|
30
30
|
when Array
|
31
31
|
fields.build(
|
32
32
|
name: key,
|
33
|
-
field_type:
|
33
|
+
field_type: "list",
|
34
34
|
fields: value.map do |item|
|
35
|
-
fields.new(name:
|
35
|
+
fields.new(name: "item", field_type: "group", fields: parse_fields_hash(item))
|
36
36
|
end
|
37
37
|
)
|
38
38
|
when String
|
39
39
|
fields.build(
|
40
40
|
name: key,
|
41
|
-
field_type:
|
41
|
+
field_type: "text",
|
42
42
|
value: value
|
43
43
|
)
|
44
44
|
else
|
45
45
|
fields.build(
|
46
46
|
name: key,
|
47
|
-
field_type:
|
47
|
+
field_type: "file",
|
48
48
|
file: value
|
49
49
|
)
|
50
50
|
end
|
@@ -52,7 +52,7 @@ module Headmin
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def parse_hash_tree(hash_tree)
|
55
|
-
|
55
|
+
hash_tree.map do |field, children|
|
56
56
|
case field.field_type.to_sym
|
57
57
|
when :group
|
58
58
|
children = children.any? ? children : field.hash_tree
|
@@ -67,8 +67,8 @@ module Headmin
|
|
67
67
|
else
|
68
68
|
[field.name, field.value]
|
69
69
|
end
|
70
|
-
end
|
70
|
+
end.to_h
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
|
-
end
|
74
|
+
end
|
@@ -2,7 +2,7 @@ module Headmin
|
|
2
2
|
class DocumentationRenderer < Redcarpet::Render::HTML
|
3
3
|
def block_code(code, language)
|
4
4
|
if language == "erbx"
|
5
|
-
"<div class=\"card mb-3\"><div class=\"card-body\">#{render_code(code)}</div><div class=\"card-footer\"><pre><code class=\"h-syntax\">#{highlight_syntax(code,
|
5
|
+
"<div class=\"card mb-3\"><div class=\"card-body\">#{render_code(code)}</div><div class=\"card-footer\"><pre><code class=\"h-syntax\">#{highlight_syntax(code, "erb")}</code></pre></div></div>"
|
6
6
|
else
|
7
7
|
"<pre><code class=\"h-syntax\">#{highlight_syntax(code, language)}</code></pre></div>"
|
8
8
|
end
|
@@ -22,9 +22,9 @@ module Headmin
|
|
22
22
|
|
23
23
|
def render_code(code)
|
24
24
|
ApplicationController.renderer.new(
|
25
|
-
|
26
|
-
controller:
|
27
|
-
action:
|
25
|
+
"action_dispatch.request.path_parameters" => {
|
26
|
+
controller: "main",
|
27
|
+
action: "index"
|
28
28
|
}
|
29
29
|
).render(inline: code)
|
30
30
|
end
|
@@ -19,18 +19,16 @@ module Headmin
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def options
|
22
|
-
option_keys = %i
|
22
|
+
option_keys = %i[aria autocomplete autofocus data disabled id maxlength minlength list pattern placeholder readonly required spellcheck size value]
|
23
23
|
@local_assigns.slice(*option_keys).merge(
|
24
|
-
aria: {
|
24
|
+
aria: {describedby: form_field_validation_id(form, attribute)},
|
25
25
|
class: ["form-control", form_field_validation_class(form, attribute)].join(" "),
|
26
26
|
list: @local_assigns[:list] ? "#{attribute}_list" : nil,
|
27
27
|
placeholder: placeholder
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
@local_assigns
|
33
|
-
end
|
31
|
+
attr_reader :local_assigns
|
34
32
|
|
35
33
|
def form_field_valid?(form, name)
|
36
34
|
return unless form.object
|
@@ -46,8 +44,8 @@ module Headmin
|
|
46
44
|
|
47
45
|
def form_field_validation_class(form, name)
|
48
46
|
return nil if @request.get?
|
49
|
-
form.object.errors.has_key?(name) ?
|
47
|
+
form.object.errors.has_key?(name) ? "is-invalid" : "is-valid"
|
50
48
|
end
|
51
49
|
end
|
52
50
|
end
|
53
|
-
end
|
51
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Headmin
|
2
|
+
class Thumbnail
|
3
|
+
def initialize(local_assigns)
|
4
|
+
@local_assigns = local_assigns
|
5
|
+
end
|
6
|
+
|
7
|
+
def class_names
|
8
|
+
class_names = [@local_assigns[:class]]
|
9
|
+
class_names << "img-thumbnail h-thumbnail"
|
10
|
+
class_names.join(" ")
|
11
|
+
end
|
12
|
+
|
13
|
+
def width
|
14
|
+
@local_assigns[:width] || 150
|
15
|
+
end
|
16
|
+
|
17
|
+
def height
|
18
|
+
@local_assigns[:height] || 150
|
19
|
+
end
|
20
|
+
|
21
|
+
def src
|
22
|
+
@local_assigns[:src]
|
23
|
+
end
|
24
|
+
|
25
|
+
def mime_type
|
26
|
+
if src
|
27
|
+
stripped_path = URI.parse(src).path
|
28
|
+
extension = File.extname(stripped_path)
|
29
|
+
Rack::Mime.mime_type(extension)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def image?
|
34
|
+
mime_type&.match?(/^image/)
|
35
|
+
end
|
36
|
+
|
37
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
38
|
+
def icon_name
|
39
|
+
type_map = {
|
40
|
+
image: %w[image/bmp image/gif image/vnd.microsoft.icon image/jpeg image/png image/svg+xml image/tiff image/webp],
|
41
|
+
play: %w[video/mp4 video/mpeg video/ogg video/mp2t video/webm video/3gpp video/3gpp2],
|
42
|
+
music: %w[audio/aac audio/midi audio/x-midi audio/mpeg audio/ogg audio/opus audio/wav audio/webm audio/3gpp audio/3gpp2],
|
43
|
+
word: %w[application/msword application/vnd.openxmlformats-officedocument.wordprocessingml.document],
|
44
|
+
ppt: %w[application/vnd.ms-powerpoint application/vnd.openxmlformats-officedocument.presentationml.presentation],
|
45
|
+
excel: %w[application/vnd.ms-excel application/vnd.openxmlformats-officedocument.spreadsheetml.sheet],
|
46
|
+
slides: %w[application/vnd.oasis.opendocument.presentation],
|
47
|
+
spreadsheet: %w[application/vnd.oasis.opendocument.spreadsheet],
|
48
|
+
richtext: %w[application/vnd.oasis.opendocument.text],
|
49
|
+
zip: %w[application/zip application/x-7z-compressed application/x-bzip application/x-bzip2 application/gzip application/vnd.rar],
|
50
|
+
pdf: %w[application/pdf]
|
51
|
+
}
|
52
|
+
|
53
|
+
type = type_map.find { |key, mime_types| mime_types.include?(mime_type) }
|
54
|
+
if type
|
55
|
+
["file", "earmark", type.first].compact.join("-")
|
56
|
+
else
|
57
|
+
"question"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,72 +1,72 @@
|
|
1
1
|
class BlockService
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
class << self
|
3
|
+
def block_names(path:)
|
4
|
+
form_views(path: path).map do |view|
|
5
|
+
view_name(view)
|
6
|
+
end
|
5
7
|
end
|
6
|
-
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def form_view(name, path:)
|
10
|
+
view_by_name(name, form_view_paths(path))
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def frontend_view(name, path:)
|
14
|
+
view_by_name(name, frontend_view_paths(path))
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def frontend_views(path:)
|
18
|
+
views(frontend_view_paths(path))
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def form_views(path:)
|
22
|
+
views(form_view_paths(path))
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
def view_name(view)
|
26
|
+
filename = File.basename(view.split(".")[0])
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
# Removes leading _ if exists
|
29
|
+
filename.sub(/^_/, "")
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
+
# Looks for a block view by its name.
|
33
|
+
#
|
34
|
+
# It will first search in a specified path and will fallback to the default path
|
35
|
+
# An exception will be raised if the view is nowhere to be found
|
36
|
+
def view_by_name(name, paths)
|
37
|
+
views = views(paths)
|
38
|
+
view = views.find { |view| view.include?("/_#{name}.html.erb") }
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def self.view_by_name(name, paths)
|
38
|
-
views = views(paths)
|
39
|
-
view = views.find { |view| view.include?("/_#{name}.html.erb") }
|
40
|
+
unless view
|
41
|
+
prefixes = paths.map { |path| path.split("app/views/").last }
|
42
|
+
raise ActionView::MissingTemplate.new(paths, name, prefixes, true, "headmin") unless view
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
prefixes = paths.map { |path| path.split('app/views/').last }
|
43
|
-
raise ActionView::MissingTemplate.new(paths, name, prefixes, true, "headmin") unless view
|
45
|
+
view
|
44
46
|
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def self.form_view_paths(path)
|
50
|
-
[app_view_path(path), app_view_path('admin/blocks')].uniq
|
51
|
-
end
|
48
|
+
def form_view_paths(path)
|
49
|
+
[app_view_path(path), app_view_path("admin/blocks")].uniq
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
def frontend_view_paths(path)
|
53
|
+
[app_view_path(path), app_view_path("website/blocks")].uniq
|
54
|
+
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
def app_view_path(path)
|
57
|
+
base_path = "app/views"
|
58
|
+
[base_path, path].join("/")
|
59
|
+
end
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
def views(paths)
|
62
|
+
paths.map { |path| views_in_path(path) }.flatten
|
63
|
+
end
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
def views_in_path(path)
|
66
|
+
files = Dir.glob("#{path}/*")
|
67
|
+
files.map do |file|
|
68
|
+
file.split("app/views/").last
|
69
|
+
end
|
70
70
|
end
|
71
71
|
end
|
72
|
-
end
|
72
|
+
end
|