layered-resource-rails 0.1.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 +7 -0
- data/.claude/skills/layered-resource-rails/SKILL.md +449 -0
- data/AGENTS.md +36 -0
- data/CHANGELOG.md +45 -0
- data/CLA.md +10 -0
- data/LICENSE +201 -0
- data/NOTICE +7 -0
- data/README.md +912 -0
- data/Rakefile +23 -0
- data/TRADEMARK.md +31 -0
- data/app/controllers/layered/resource/controller.rb +284 -0
- data/app/controllers/layered/resource/internal/breadcrumbs.rb +80 -0
- data/app/controllers/layered/resource/internal/columns.rb +207 -0
- data/app/controllers/layered/resource/internal/routing.rb +60 -0
- data/app/controllers/layered/resource/resources_controller.rb +20 -0
- data/app/helpers/layered/resource/filters_helper.rb +321 -0
- data/app/views/layered/resource/columns/_badge.html.erb +2 -0
- data/app/views/layered/resource/columns/_boolean.html.erb +1 -0
- data/app/views/layered/resource/columns/_datetime.html.erb +1 -0
- data/app/views/layered/resource/columns/_text.html.erb +1 -0
- data/app/views/layered/resource/resources/_filter_control.html.erb +87 -0
- data/app/views/layered/resource/resources/_filters.html.erb +60 -0
- data/app/views/layered/resource/resources/edit.html.erb +16 -0
- data/app/views/layered/resource/resources/index.html.erb +106 -0
- data/app/views/layered/resource/resources/new.html.erb +16 -0
- data/app/views/layered/resource/resources/show.html.erb +34 -0
- data/config/locales/en.yml +9 -0
- data/lib/generators/layered/resource/column/column_generator.rb +63 -0
- data/lib/generators/layered/resource/controller/controller_generator.rb +54 -0
- data/lib/generators/layered/resource/controller/templates/controller.rb.tt +31 -0
- data/lib/generators/layered/resource/install_agent_skill_generator.rb +26 -0
- data/lib/generators/layered/resource/resource_generator.rb +63 -0
- data/lib/generators/layered/resource/scaffold/scaffold_generator.rb +94 -0
- data/lib/generators/layered/resource/templates/resource.rb.tt +19 -0
- data/lib/generators/layered/resource/views/views_generator.rb +49 -0
- data/lib/layered/resource/base.rb +625 -0
- data/lib/layered/resource/engine.rb +33 -0
- data/lib/layered/resource/routing.rb +366 -0
- data/lib/layered/resource/version.rb +5 -0
- data/lib/layered/resource.rb +61 -0
- data/lib/layered-resource-rails.rb +1 -0
- metadata +299 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<% record_label = layered_record_label(@record) %>
|
|
2
|
+
<% index_path = @_route_entry[:actions].include?(:index) ? layered_collection_path : nil %>
|
|
3
|
+
|
|
4
|
+
<div class="l-ui-spread">
|
|
5
|
+
<div>
|
|
6
|
+
<% if layered_breadcrumbs.any? || index_path %>
|
|
7
|
+
<%= l_ui_breadcrumbs do %>
|
|
8
|
+
<% layered_breadcrumbs.each do |crumb| %>
|
|
9
|
+
<%= l_ui_breadcrumb_item crumb[:label], crumb[:path] %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% if index_path %>
|
|
12
|
+
<%= l_ui_breadcrumb_item @model.model_name.human.pluralize, index_path %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<h1 class="l-ui-heading"><%= record_label %></h1>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="l-ui-spread">
|
|
21
|
+
<% if resource_can?(:edit, @record) %>
|
|
22
|
+
<%= link_to "Edit",
|
|
23
|
+
layered_member_path(@record, action: :edit),
|
|
24
|
+
class: "l-ui-button l-ui-button--primary" %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% if resource_can?(:destroy, @record) %>
|
|
27
|
+
<%= button_to "Delete",
|
|
28
|
+
layered_member_path(@record),
|
|
29
|
+
method: :delete,
|
|
30
|
+
class: "l-ui-button l-ui-button--outline-danger",
|
|
31
|
+
data: { turbo_confirm: "Are you sure?" } %>
|
|
32
|
+
<% end %>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require "rails/generators/base"
|
|
2
|
+
|
|
3
|
+
module Layered
|
|
4
|
+
module Resource
|
|
5
|
+
module Generators
|
|
6
|
+
# Ejects a built-in column partial into the host app, or scaffolds a
|
|
7
|
+
# custom one. Lookup order at runtime is per-resource → host-wide →
|
|
8
|
+
# gem default, so ejected partials override the gem's built-ins.
|
|
9
|
+
#
|
|
10
|
+
# rails g layered:resource:column badge # → app/views/layered/resource/columns/_badge.html.erb
|
|
11
|
+
# rails g layered:resource:column badge questions # → app/views/layered/questions/columns/_badge.html.erb
|
|
12
|
+
# rails g layered:resource:column priority_badge # → scaffold a new type (host-wide)
|
|
13
|
+
class ColumnGenerator < ::Rails::Generators::Base
|
|
14
|
+
BUILT_INS = %w[text datetime badge boolean].freeze
|
|
15
|
+
|
|
16
|
+
argument :type, type: :string, banner: "type"
|
|
17
|
+
argument :resource, type: :string, default: nil, optional: true,
|
|
18
|
+
banner: "[resource]"
|
|
19
|
+
|
|
20
|
+
source_root Layered::Resource::Engine.root.join("app/views/layered/resource/columns")
|
|
21
|
+
|
|
22
|
+
desc "Eject a built-in column partial, or scaffold a custom one."
|
|
23
|
+
|
|
24
|
+
def create_partial
|
|
25
|
+
if BUILT_INS.include?(type)
|
|
26
|
+
copy_file "_#{type}.html.erb", target_path
|
|
27
|
+
else
|
|
28
|
+
create_file target_path, scaffold_template
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def show_next_steps
|
|
33
|
+
say ""
|
|
34
|
+
say "Edit #{target_path} to customise rendering."
|
|
35
|
+
say ""
|
|
36
|
+
say "Use it from a resource with:"
|
|
37
|
+
say " columns [{ attribute: :something, as: :#{type} }]"
|
|
38
|
+
say ""
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def target_path
|
|
44
|
+
dir = resource.present? ? "app/views/layered/#{resource}/columns" : "app/views/layered/resource/columns"
|
|
45
|
+
File.join(dir, "_#{type}.html.erb")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Starter content for a new (non-built-in) column type. Locals
|
|
49
|
+
# contract matches the runtime: (record, value, options).
|
|
50
|
+
def scaffold_template
|
|
51
|
+
<<~ERB
|
|
52
|
+
<%# Column partial for as: :#{type}.
|
|
53
|
+
Locals:
|
|
54
|
+
record - the model instance
|
|
55
|
+
value - record.public_send(column_attribute)
|
|
56
|
+
options - the column hash (read keys like :variants, :format) %>
|
|
57
|
+
<%= value %>
|
|
58
|
+
ERB
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "rails/generators/named_base"
|
|
2
|
+
|
|
3
|
+
module Layered
|
|
4
|
+
module Resource
|
|
5
|
+
module Generators
|
|
6
|
+
# Creates a controller that inherits from
|
|
7
|
+
# Layered::Resource::ResourcesController so the user can override
|
|
8
|
+
# individual actions while keeping the rest of the gem's behaviour.
|
|
9
|
+
#
|
|
10
|
+
# rails g layered:resource:controller articles
|
|
11
|
+
#
|
|
12
|
+
# Generates app/controllers/articles_controller.rb and prints the
|
|
13
|
+
# routes change needed to wire it up.
|
|
14
|
+
class ControllerGenerator < ::Rails::Generators::NamedBase
|
|
15
|
+
source_root File.expand_path("templates", __dir__)
|
|
16
|
+
|
|
17
|
+
desc "Generate a controller that inherits from Layered::Resource::ResourcesController."
|
|
18
|
+
|
|
19
|
+
def create_controller_file
|
|
20
|
+
path = File.join("app/controllers", class_path, "#{file_name}_controller.rb")
|
|
21
|
+
full_path = File.join(destination_root, path)
|
|
22
|
+
if File.exist?(full_path) && !options[:force]
|
|
23
|
+
raise Thor::Error,
|
|
24
|
+
"#{path} already exists. The layered:resource:controller generator " \
|
|
25
|
+
"scaffolds a fresh subclass and would overwrite your existing file. " \
|
|
26
|
+
"Pass --force to overwrite, or delete/rename the existing file first."
|
|
27
|
+
end
|
|
28
|
+
template "controller.rb.tt", path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def show_routing_instructions
|
|
32
|
+
say ""
|
|
33
|
+
say "Point the route at the new controller:"
|
|
34
|
+
say ""
|
|
35
|
+
indent = " "
|
|
36
|
+
class_path.each_with_index do |segment, depth|
|
|
37
|
+
say "#{indent * (depth + 1)}namespace :#{segment} do"
|
|
38
|
+
end
|
|
39
|
+
say "#{indent * (class_path.length + 1)}layered_resources :#{file_name}, controller: \"#{file_name}\""
|
|
40
|
+
class_path.length.downto(1) do |depth|
|
|
41
|
+
say "#{indent * depth}end"
|
|
42
|
+
end
|
|
43
|
+
say ""
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def file_name
|
|
49
|
+
@_file_name ||= super.sub(/_?controller$/i, "")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<% module_namespacing do -%>
|
|
2
|
+
class <%= class_name %>Controller < Layered::Resource::ResourcesController
|
|
3
|
+
# Override any of the standard CRUD actions (#index, #show, #new,
|
|
4
|
+
# #create, #edit, #update, #destroy) and call `super` when you only
|
|
5
|
+
# want to tweak behaviour. Drop the inheritance and write a plain
|
|
6
|
+
# Rails controller if you outgrow the gem entirely.
|
|
7
|
+
#
|
|
8
|
+
# Custom member/collection actions declared in the matching
|
|
9
|
+
# `layered_resources` block dispatch here. @resource, @model,
|
|
10
|
+
# @columns, @fields, and the @resource_can_* flags are already set.
|
|
11
|
+
#
|
|
12
|
+
# Member actions also get @record auto-loaded from params[:id] via
|
|
13
|
+
# @resource.scope(self).find — add
|
|
14
|
+
# `skip_before_action :load_layered_member_record, only: [:foo]`
|
|
15
|
+
# to opt out. Collection actions never auto-load (no :id in the
|
|
16
|
+
# path), so do any lookups yourself.
|
|
17
|
+
#
|
|
18
|
+
# Example member action:
|
|
19
|
+
#
|
|
20
|
+
# def publish
|
|
21
|
+
# @record.update!(published_at: Time.current)
|
|
22
|
+
# redirect_to layered_member_path(@record), notice: "Published"
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Path helpers available:
|
|
26
|
+
# layered_collection_path # index
|
|
27
|
+
# layered_member_path(record) # show/update/destroy
|
|
28
|
+
# layered_routes.<name>_path(...) # any registered route,
|
|
29
|
+
# # parent params filled in
|
|
30
|
+
end
|
|
31
|
+
<% end -%>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Layered
|
|
2
|
+
module Resource
|
|
3
|
+
module Generators
|
|
4
|
+
class InstallAgentSkillGenerator < Rails::Generators::Base
|
|
5
|
+
desc "Copy the layered-resource-rails agent skill into the host application"
|
|
6
|
+
|
|
7
|
+
def self.source_root
|
|
8
|
+
Layered::Resource::Engine.root
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def copy_skill
|
|
12
|
+
skill_source = File.join(self.class.source_root, ".claude/skills/layered-resource-rails")
|
|
13
|
+
skill_dest = ".claude/skills/layered-resource-rails"
|
|
14
|
+
|
|
15
|
+
directory skill_source, skill_dest
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def show_instructions
|
|
19
|
+
say ""
|
|
20
|
+
say "Agent skill installed to .claude/skills/layered-resource-rails/", :green
|
|
21
|
+
say ""
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require "rails/generators/named_base"
|
|
2
|
+
|
|
3
|
+
module Layered
|
|
4
|
+
module Resource
|
|
5
|
+
module Generators
|
|
6
|
+
# Generates an `app/layered_resources/<name>_resource.rb` file and
|
|
7
|
+
# appends a `layered_resources :name` route. Use
|
|
8
|
+
# `rails g layered:resource:scaffold` for the full one-shot that also
|
|
9
|
+
# creates the model and migration.
|
|
10
|
+
#
|
|
11
|
+
# rails g layered:resource article title:string body:text
|
|
12
|
+
class ResourceGenerator < ::Rails::Generators::NamedBase
|
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
|
14
|
+
|
|
15
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
|
16
|
+
|
|
17
|
+
class_option :skip_route, type: :boolean, default: false,
|
|
18
|
+
desc: "Skip appending a layered_resources route"
|
|
19
|
+
|
|
20
|
+
desc "Generate an app/layered_resources/<name>_resource.rb file and a route."
|
|
21
|
+
|
|
22
|
+
def create_resource_file
|
|
23
|
+
template "resource.rb.tt",
|
|
24
|
+
File.join("app/layered_resources", "#{singular_name}_resource.rb")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_route
|
|
28
|
+
return if options[:skip_route]
|
|
29
|
+
|
|
30
|
+
route "layered_resources :#{plural_name}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def singular_name
|
|
36
|
+
file_name.singularize
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def plural_name
|
|
40
|
+
file_name.pluralize
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def resource_class_name
|
|
44
|
+
singular_name.camelize
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def column_attributes
|
|
48
|
+
attributes.reject { |a| a.reference? || a.password_digest? }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def field_attributes
|
|
52
|
+
column_attributes
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def field_as(attr)
|
|
56
|
+
case attr.type
|
|
57
|
+
when :text then ", as: :text"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require "rails/generators/named_base"
|
|
2
|
+
require "generators/layered/resource/resource_generator"
|
|
3
|
+
require "generators/layered/resource/views/views_generator"
|
|
4
|
+
|
|
5
|
+
module Layered
|
|
6
|
+
module Resource
|
|
7
|
+
module Generators
|
|
8
|
+
# One-shot scaffolder: generates the migration + model (via Rails'
|
|
9
|
+
# built-in model generator), an `app/layered_resources/<name>_resource.rb`
|
|
10
|
+
# (via `layered:resource`), and appends a `layered_resources :name` route.
|
|
11
|
+
# Views are intentionally left to the gem's defaults - run
|
|
12
|
+
# `rails g layered:resource:views <name>` to eject them.
|
|
13
|
+
#
|
|
14
|
+
# rails g layered:resource:scaffold article title:string body:text
|
|
15
|
+
class ScaffoldGenerator < ::Rails::Generators::NamedBase
|
|
16
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
|
17
|
+
|
|
18
|
+
class_option :skip_model, type: :boolean, default: false,
|
|
19
|
+
desc: "Skip generating the migration and model (use when they already exist)"
|
|
20
|
+
class_option :actions, type: :array, default: nil, banner: "index show new create edit update destroy",
|
|
21
|
+
desc: "Restrict the generated route to these actions (passed to layered_resources only:)"
|
|
22
|
+
class_option :except, type: :array, default: nil, banner: "destroy",
|
|
23
|
+
desc: "Exclude these actions from the generated route (passed to layered_resources except:)"
|
|
24
|
+
class_option :controller, type: :boolean, default: false,
|
|
25
|
+
desc: "Also eject a controller (invokes layered:resource:controller) and wire it into the route"
|
|
26
|
+
class_option :views, type: :boolean, default: false,
|
|
27
|
+
desc: "Also eject the view templates (invokes layered:resource:views)"
|
|
28
|
+
|
|
29
|
+
desc "Generate a model, migration, resource class, and route in one shot."
|
|
30
|
+
|
|
31
|
+
def create_model
|
|
32
|
+
return if options[:skip_model]
|
|
33
|
+
|
|
34
|
+
invoke "model", [singular_name, *attributes.map(&:to_s)]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_resource_file
|
|
38
|
+
invoke ResourceGenerator, [singular_name, *attributes.map(&:to_s)], skip_route: true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def eject_controller
|
|
42
|
+
invoke "layered:resource:controller", [plural_name] if options[:controller]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def eject_views
|
|
46
|
+
invoke ViewsGenerator, [plural_name] if options[:views]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def add_route
|
|
50
|
+
route "layered_resources :#{plural_name}#{route_controller_option}#{route_action_filter}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def show_next_steps
|
|
54
|
+
say ""
|
|
55
|
+
say "Next steps for app/layered_resources/#{singular_name}_resource.rb:"
|
|
56
|
+
say " - Add `validates` calls to #{resource_class_name} for any required fields - the form marks fields required based on unconditional presence validators."
|
|
57
|
+
say " - Consider `default_sort attribute: :created_at, direction: :desc` (or another column) so the index has a stable order."
|
|
58
|
+
say " - Consider `search_fields [...]` listing the columns the index search box should match against."
|
|
59
|
+
say ""
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def route_controller_option
|
|
65
|
+
", controller: \"#{plural_name}\"" if options[:controller]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def route_action_filter
|
|
69
|
+
if options[:actions].present?
|
|
70
|
+
", only: #{action_list(options[:actions])}"
|
|
71
|
+
elsif options[:except].present?
|
|
72
|
+
", except: #{action_list(options[:except])}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def action_list(actions)
|
|
77
|
+
"[#{actions.map { |a| ":#{a}" }.join(', ')}]"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def singular_name
|
|
81
|
+
file_name.singularize
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def plural_name
|
|
85
|
+
file_name.pluralize
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def resource_class_name
|
|
89
|
+
singular_name.camelize
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class <%= resource_class_name %>Resource < Layered::Resource::Base
|
|
2
|
+
model <%= resource_class_name %>
|
|
3
|
+
<% if column_attributes.any? -%>
|
|
4
|
+
|
|
5
|
+
columns [
|
|
6
|
+
<% column_attributes.each_with_index do |attr, i| -%>
|
|
7
|
+
{ attribute: :<%= attr.name %><%= ", primary: true" if i.zero? %> }<%= "," unless i == column_attributes.length - 1 %>
|
|
8
|
+
<% end -%>
|
|
9
|
+
]
|
|
10
|
+
<% end -%>
|
|
11
|
+
<% if field_attributes.any? -%>
|
|
12
|
+
|
|
13
|
+
fields [
|
|
14
|
+
<% field_attributes.each_with_index do |attr, i| -%>
|
|
15
|
+
{ attribute: :<%= attr.name %><%= field_as(attr) %> }<%= "," unless i == field_attributes.length - 1 %>
|
|
16
|
+
<% end -%>
|
|
17
|
+
]
|
|
18
|
+
<% end -%>
|
|
19
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "rails/generators/named_base"
|
|
2
|
+
|
|
3
|
+
module Layered
|
|
4
|
+
module Resource
|
|
5
|
+
module Generators
|
|
6
|
+
# Copies the gem's view templates into the host app so they can be
|
|
7
|
+
# edited freely. The controller's view-resolution escape hatch picks
|
|
8
|
+
# them up automatically - no further wiring needed.
|
|
9
|
+
#
|
|
10
|
+
# rails g layered:resource:views articles
|
|
11
|
+
#
|
|
12
|
+
# Generates app/views/layered/articles/{index,show,new,edit}.html.erb.
|
|
13
|
+
class ViewsGenerator < ::Rails::Generators::NamedBase
|
|
14
|
+
source_root Layered::Resource::Engine.root.join("app/views/layered/resource/resources")
|
|
15
|
+
|
|
16
|
+
desc "Copy the gem's view templates into app/views/layered/<name>/ for full customisation."
|
|
17
|
+
|
|
18
|
+
VIEWS = %w[index.html.erb show.html.erb new.html.erb edit.html.erb
|
|
19
|
+
_filters.html.erb _filter_control.html.erb].freeze
|
|
20
|
+
|
|
21
|
+
def copy_views
|
|
22
|
+
VIEWS.each do |view|
|
|
23
|
+
copy_file view, File.join("app/views/layered", resource_directory, view)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def show_next_steps
|
|
28
|
+
say ""
|
|
29
|
+
say "Edit any of the templates in app/views/layered/#{resource_directory}/ - the gem will use them in place of its defaults."
|
|
30
|
+
say ""
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# The controller's view-resolution escape hatch keys off the
|
|
36
|
+
# plural name passed to `layered_resources`, ignoring any Rails
|
|
37
|
+
# namespace. Mirror that here so generated files actually get
|
|
38
|
+
# picked up.
|
|
39
|
+
def resource_directory
|
|
40
|
+
file_name.pluralize
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def file_name
|
|
44
|
+
@_file_name ||= super.singularize
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|