uchi 0.1.6 → 0.1.8
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/README.md +7 -0
- data/app/assets/javascripts/controllers/fields/belongs_to_controller.js +130 -0
- data/app/assets/javascripts/controllers/fields/has_many_controller.js +146 -0
- data/app/assets/javascripts/uchi/application.js +804 -3
- data/app/assets/javascripts/uchi.js +9 -0
- data/app/assets/stylesheets/uchi/application.css +81 -1549
- data/app/assets/tailwind/uchi.css +2 -2
- data/app/components/uchi/field/belongs_to/edit.html.erb +73 -1
- data/app/components/uchi/field/belongs_to.rb +60 -26
- data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +1 -1
- data/app/components/uchi/field/has_many/edit.html.erb +86 -1
- data/app/components/uchi/field/has_many/show.html.erb +1 -1
- data/app/components/uchi/field/has_many.rb +59 -11
- data/app/components/uchi/flowbite/button.rb +1 -1
- data/app/components/uchi/flowbite/input/field.rb +6 -0
- data/app/components/uchi/flowbite/input/label.rb +2 -0
- data/app/components/uchi/flowbite/input_field.rb +11 -1
- data/app/components/uchi/flowbite/styles.rb +34 -0
- data/app/components/uchi/ui/actions/dropdown/dropdown.html.erb +1 -1
- data/app/components/uchi/ui/navigation/navigation.html.erb +1 -1
- data/app/components/uchi/ui/page_header/page_header.html.erb +7 -7
- data/app/controllers/uchi/belongs_to/associated_records_controller.rb +89 -0
- data/app/controllers/uchi/has_many/associated_records_controller.rb +89 -0
- data/app/controllers/uchi/repository_controller.rb +24 -8
- data/app/views/layouts/uchi/_javascript.html.erb +1 -0
- data/app/views/layouts/uchi/_stylesheets.html.erb +1 -0
- data/app/views/layouts/uchi/application.html.erb +5 -5
- data/app/views/uchi/belongs_to/associated_records/index.html.erb +13 -0
- data/app/views/uchi/has_many/associated_records/index.html.erb +26 -0
- data/app/views/uchi/navigation/_main.html.erb +83 -0
- data/app/views/uchi/repository/new.html.erb +2 -2
- data/lib/generators/uchi/controller/controller_generator.rb +0 -4
- data/lib/generators/uchi/install/install_generator.rb +1 -1
- data/lib/uchi/field/configuration.rb +2 -2
- data/lib/uchi/field.rb +11 -0
- data/lib/uchi/plugins.rb +21 -0
- data/lib/uchi/repository/routes.rb +9 -5
- data/lib/uchi/repository.rb +43 -5
- data/lib/uchi/routes.rb +67 -0
- data/lib/uchi/version.rb +1 -1
- data/lib/uchi.rb +9 -1
- metadata +12 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uchi/pagination/controller"
|
|
4
|
+
|
|
5
|
+
module Uchi
|
|
6
|
+
module HasMany
|
|
7
|
+
# Companion controller for the Stimulus-based has_many_controller.
|
|
8
|
+
#
|
|
9
|
+
# Provides backend support for fetching associated records for a has_many
|
|
10
|
+
# field via AJAX.
|
|
11
|
+
class AssociatedRecordsController < Uchi::ApplicationController
|
|
12
|
+
layout false
|
|
13
|
+
|
|
14
|
+
def index
|
|
15
|
+
@current_values = field.value(parent_record) || []
|
|
16
|
+
|
|
17
|
+
@field_name = params[:field]
|
|
18
|
+
@records = field.collection_query.call(find_all_records_from_association)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
protected
|
|
22
|
+
|
|
23
|
+
helper_method def record_title(record)
|
|
24
|
+
return "" if record.nil?
|
|
25
|
+
|
|
26
|
+
associated_repository.title(record)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
helper_method def source_repository
|
|
30
|
+
@source_repository ||= begin
|
|
31
|
+
model_name = params[:model]
|
|
32
|
+
repository_class = Uchi::Repository.for_model(model_name)
|
|
33
|
+
raise NameError, "No repository found for model #{model_name}" unless repository_class
|
|
34
|
+
|
|
35
|
+
repository_class.new
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def associated_repository
|
|
42
|
+
@associated_repository ||= begin
|
|
43
|
+
associated_repository = Uchi::Repository.for_model(association.klass)&.new
|
|
44
|
+
raise NameError, "No repository found for associated model #{association.klass}" unless associated_repository
|
|
45
|
+
|
|
46
|
+
associated_repository
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def association
|
|
51
|
+
@association ||= begin
|
|
52
|
+
association = source_repository.model.reflect_on_association(field.name.to_sym)
|
|
53
|
+
raise NameError, "No association named #{field.name} on #{source_repository.model}" unless association
|
|
54
|
+
|
|
55
|
+
association
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def field
|
|
60
|
+
@field ||= begin
|
|
61
|
+
field_name = params[:field]
|
|
62
|
+
field = source_repository.fields.find { |f| f.name == field_name.to_sym }
|
|
63
|
+
raise NameError, "No field named #{field_name} on #{source_repository.model}" unless field
|
|
64
|
+
|
|
65
|
+
field
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def find_all_records(repository:, scope: nil)
|
|
70
|
+
# Duplicated from Uchi::RepositoryController; consider refactoring.
|
|
71
|
+
repository
|
|
72
|
+
.find_all(
|
|
73
|
+
scope: scope,
|
|
74
|
+
search: params[:query]
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def find_all_records_from_association
|
|
79
|
+
find_all_records(repository: associated_repository)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def parent_record
|
|
83
|
+
return nil unless params[:record_id].present?
|
|
84
|
+
|
|
85
|
+
source_repository.find(params[:record_id])
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -9,7 +9,7 @@ module Uchi
|
|
|
9
9
|
before_action :set_repository
|
|
10
10
|
|
|
11
11
|
def create
|
|
12
|
-
@record =
|
|
12
|
+
@record = build_record_for_new
|
|
13
13
|
if @record.save
|
|
14
14
|
flash[:success] = @repository.translate.successful_create
|
|
15
15
|
redirect_to(@repository.routes.path_for(:show, id: @record.id), status: :see_other)
|
|
@@ -54,7 +54,7 @@ module Uchi
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def new
|
|
57
|
-
@record =
|
|
57
|
+
@record = build_record_for_new
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def show
|
|
@@ -63,7 +63,7 @@ module Uchi
|
|
|
63
63
|
|
|
64
64
|
def update
|
|
65
65
|
@record = find_record
|
|
66
|
-
if @record.update(
|
|
66
|
+
if @record.update(record_params_for_update)
|
|
67
67
|
flash[:success] = @repository.translate.successful_update
|
|
68
68
|
redirect_to(@repository.routes.path_for(:show, id: @record.id, uniq: rand), status: :see_other)
|
|
69
69
|
else
|
|
@@ -73,8 +73,8 @@ module Uchi
|
|
|
73
73
|
|
|
74
74
|
private
|
|
75
75
|
|
|
76
|
-
def
|
|
77
|
-
@repository.build(
|
|
76
|
+
def build_record_for_new
|
|
77
|
+
@repository.build(record_params_for_new)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# Returns the path to use for the cancel link
|
|
@@ -128,10 +128,26 @@ module Uchi
|
|
|
128
128
|
25
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
def
|
|
132
|
-
|
|
131
|
+
def permitted_params_for_edit
|
|
132
|
+
@repository.fields_for_edit.map(&:permitted_param)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def permitted_params_for_new
|
|
136
|
+
@repository.fields_for_new.map(&:permitted_param)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def record_params_for_edit
|
|
140
|
+
(params[@repository.model_param_key] || ActionController::Parameters.new)
|
|
141
|
+
.permit(*permitted_params_for_edit)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def record_params_for_update
|
|
145
|
+
record_params_for_edit
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def record_params_for_new
|
|
133
149
|
(params[@repository.model_param_key] || ActionController::Parameters.new)
|
|
134
|
-
.permit(*
|
|
150
|
+
.permit(*permitted_params_for_new)
|
|
135
151
|
end
|
|
136
152
|
|
|
137
153
|
# Returns the repository class associated with this controller.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= javascript_include_tag "uchi/application", "data-turbo-track": "reload" %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= stylesheet_link_tag "uchi/application", media: "all", "data-turbo-track": "reload" %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
|
|
3
|
-
<html>
|
|
3
|
+
<html class="h-full">
|
|
4
4
|
<head>
|
|
5
5
|
<title>
|
|
6
6
|
<%= content_for?(:page_title) ? yield(:page_title) : "Uchi" %>
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
13
13
|
|
|
14
|
-
<%=
|
|
15
|
-
<%=
|
|
14
|
+
<%= render "layouts/uchi/javascript" %>
|
|
15
|
+
<%= render "layouts/uchi/stylesheets" %>
|
|
16
16
|
</head>
|
|
17
17
|
|
|
18
|
-
<body class="antialiased bg-neutral-secondary-medium p-2">
|
|
19
|
-
<div class="md:flex">
|
|
18
|
+
<body class="antialiased bg-neutral-secondary-medium h-full p-2">
|
|
19
|
+
<div class="md:flex h-full">
|
|
20
20
|
<%= render(partial: "uchi/navigation/main") %>
|
|
21
21
|
|
|
22
22
|
<main class="py-6 overflow-x-hidden grow md:px-6"><%= yield %></main>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<ul class="p-2 text-sm text-body font-medium">
|
|
2
|
+
<% @records.each do |record| %>
|
|
3
|
+
<li
|
|
4
|
+
class="inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded aria-selected:bg-brand aria-selected:text-white"
|
|
5
|
+
data-action="belongs-to#selectOption"
|
|
6
|
+
data-id="<%= record.id %>"
|
|
7
|
+
id="<%= dom_id(record, [source_repository.model, @field_name].join("-")) %>"
|
|
8
|
+
role="option"
|
|
9
|
+
>
|
|
10
|
+
<%= record_title(record) %>
|
|
11
|
+
</li>
|
|
12
|
+
<% end %>
|
|
13
|
+
</ul>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<ul class="p-4 text-sm text-body font-medium space-y-4">
|
|
2
|
+
<% @records.each do |record| %>
|
|
3
|
+
<%
|
|
4
|
+
checkbox_id = dom_id(record, [source_repository.model, @field_name, 'checkbox'].join("-"))
|
|
5
|
+
%>
|
|
6
|
+
<li
|
|
7
|
+
data-id="<%= record.id %>"
|
|
8
|
+
id="<%= dom_id(record, [source_repository.model, @field_name].join("-")) %>"
|
|
9
|
+
role="option"
|
|
10
|
+
>
|
|
11
|
+
<div class="flex items-center">
|
|
12
|
+
<input
|
|
13
|
+
type="checkbox"
|
|
14
|
+
id="<%= checkbox_id %>"
|
|
15
|
+
class="w-4 h-4 border border-default-strong rounded-xs bg-neutral-secondary-strong focus:ring-2 focus:ring-brand-soft cursor-pointer"
|
|
16
|
+
data-action="change->has-many#handleCheckboxChange"
|
|
17
|
+
data-has-many-target="checkbox"
|
|
18
|
+
<%= 'checked' if @current_values.include?(record) %>
|
|
19
|
+
>
|
|
20
|
+
<label for="<%= checkbox_id %>" class="ms-2 text-sm font-medium text-heading cursor-pointer">
|
|
21
|
+
<%= record_title(record) %>
|
|
22
|
+
</label>
|
|
23
|
+
</div>
|
|
24
|
+
</li>
|
|
25
|
+
<% end %>
|
|
26
|
+
</ul>
|
|
@@ -2,8 +2,91 @@
|
|
|
2
2
|
class="
|
|
3
3
|
w-64 h-full px-3 py-4 overflow-y-auto shrink-0
|
|
4
4
|
bg-neutral-secondary-medium
|
|
5
|
+
hidden md:block
|
|
5
6
|
"
|
|
6
7
|
aria-label="Main"
|
|
7
8
|
>
|
|
8
9
|
<%= render Uchi::Ui::Navigation.new %>
|
|
9
10
|
</nav>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
class="md:hidden">
|
|
14
|
+
<button
|
|
15
|
+
aria-controls="main-navigation"
|
|
16
|
+
class="
|
|
17
|
+
text-body bg-transparent hover:text-heading
|
|
18
|
+
hover:bg-neutral-tertiary rounded-base w-9 h-9
|
|
19
|
+
flex items-center justify-center
|
|
20
|
+
"
|
|
21
|
+
popovertarget="main-navigation"
|
|
22
|
+
type="button"
|
|
23
|
+
>
|
|
24
|
+
<svg
|
|
25
|
+
aria-hidden="true"
|
|
26
|
+
class="w-6 h-6"
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
width="24"
|
|
29
|
+
height="24"
|
|
30
|
+
fill="none"
|
|
31
|
+
viewBox="0 0 24 24"
|
|
32
|
+
>
|
|
33
|
+
<path
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
stroke-linecap="round"
|
|
36
|
+
stroke-width="2"
|
|
37
|
+
d="M5 7h14M5 12h14M5 17h14"
|
|
38
|
+
/>
|
|
39
|
+
</svg>
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<nav
|
|
44
|
+
aria-label="Main"
|
|
45
|
+
class="
|
|
46
|
+
fixed top-0 left-0 z-40 h-screen p-2
|
|
47
|
+
flex-col
|
|
48
|
+
bg-neutral-primary-soft w-80
|
|
49
|
+
border-e border-default
|
|
50
|
+
backdrop:bg-dark-backdrop/70
|
|
51
|
+
"
|
|
52
|
+
id="main-navigation"
|
|
53
|
+
popover
|
|
54
|
+
tabindex="-1"
|
|
55
|
+
>
|
|
56
|
+
<div>
|
|
57
|
+
<button
|
|
58
|
+
aria-controls="main-navigation"
|
|
59
|
+
class="
|
|
60
|
+
text-body bg-transparent hover:text-heading
|
|
61
|
+
hover:bg-neutral-tertiary rounded-base w-9 h-9
|
|
62
|
+
flex items-center justify-center
|
|
63
|
+
"
|
|
64
|
+
popovertarget="main-navigation"
|
|
65
|
+
type="button"
|
|
66
|
+
>
|
|
67
|
+
<svg
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
class="w-5 h-5"
|
|
70
|
+
fill="none"
|
|
71
|
+
height="24"
|
|
72
|
+
viewBox="0 0 24 24"
|
|
73
|
+
width="24"
|
|
74
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
75
|
+
>
|
|
76
|
+
<path
|
|
77
|
+
stroke="currentColor"
|
|
78
|
+
stroke-linecap="round"
|
|
79
|
+
stroke-linejoin="round"
|
|
80
|
+
stroke-width="2"
|
|
81
|
+
d="M6 18 17.94 6M18 18 6.06 6"
|
|
82
|
+
/>
|
|
83
|
+
</svg>
|
|
84
|
+
|
|
85
|
+
<span class="sr-only">Close menu</span>
|
|
86
|
+
</button>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="overflow-y-auto max-h-full">
|
|
90
|
+
<%= render Uchi::Ui::Navigation.new %>
|
|
91
|
+
</div>
|
|
92
|
+
</nav>
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
<%= render(Uchi::Flowbite::Card.new) do %>
|
|
16
16
|
<%= form_with model: @record, url: @repository.routes.path_for(:create) do |form| %>
|
|
17
17
|
<div class="space-y-6">
|
|
18
|
-
<% @repository.
|
|
18
|
+
<% @repository.fields_for_new.each do |field| %>
|
|
19
19
|
<div>
|
|
20
20
|
<%= render(
|
|
21
|
-
field.
|
|
21
|
+
field.new_component(
|
|
22
22
|
form: form,
|
|
23
23
|
repository: @repository,
|
|
24
24
|
label: @repository.translate.field_label(field),
|
|
@@ -5,7 +5,7 @@ module Uchi
|
|
|
5
5
|
module Configuration
|
|
6
6
|
class Unset; end
|
|
7
7
|
|
|
8
|
-
DEFAULT_READER = ->(record, field_name) { record
|
|
8
|
+
DEFAULT_READER = ->(record, field_name) { record&.public_send(field_name) }
|
|
9
9
|
|
|
10
10
|
def initialize(*args)
|
|
11
11
|
super
|
|
@@ -127,7 +127,7 @@ module Uchi
|
|
|
127
127
|
protected
|
|
128
128
|
|
|
129
129
|
def default_on
|
|
130
|
-
[:edit, :index, :show]
|
|
130
|
+
[:edit, :index, :new, :show]
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def default_searchable?
|
data/lib/uchi/field.rb
CHANGED
|
@@ -51,6 +51,17 @@ module Uchi
|
|
|
51
51
|
@name = name.to_sym
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
def new_component(form:, repository:, label: nil, hint: nil)
|
|
55
|
+
# For now all components use the same component for Edit and New. Override
|
|
56
|
+
# this method in a subclass to provide a different component.
|
|
57
|
+
edit_component(
|
|
58
|
+
form: form,
|
|
59
|
+
hint: hint,
|
|
60
|
+
label: label,
|
|
61
|
+
repository: repository
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
54
65
|
# Returns the key that this field is expected to use in params
|
|
55
66
|
def param_key
|
|
56
67
|
name.to_sym
|
data/lib/uchi/plugins.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Uchi
|
|
2
|
+
class Plugins
|
|
3
|
+
attr_reader :registered
|
|
4
|
+
|
|
5
|
+
def hook(hook_name, **kwargs)
|
|
6
|
+
registered.each do |plugin_class|
|
|
7
|
+
next unless plugin_class.hooks_into?(hook_name)
|
|
8
|
+
|
|
9
|
+
plugin_class.hook(hook_name, **kwargs)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@registered = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def register(plugin_class)
|
|
18
|
+
@registered << plugin_class
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -22,7 +22,7 @@ module Uchi
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def root_path
|
|
25
|
-
"/#{
|
|
25
|
+
"/#{uchi_path}/"
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
@@ -34,7 +34,7 @@ module Uchi
|
|
|
34
34
|
|
|
35
35
|
def plural_path_for(_action, **options)
|
|
36
36
|
parts = [
|
|
37
|
-
|
|
37
|
+
uchi_as,
|
|
38
38
|
plural,
|
|
39
39
|
"path"
|
|
40
40
|
].compact
|
|
@@ -47,15 +47,19 @@ module Uchi
|
|
|
47
47
|
action = nil if action == :destroy
|
|
48
48
|
parts = [
|
|
49
49
|
action,
|
|
50
|
-
|
|
50
|
+
uchi_as,
|
|
51
51
|
singular,
|
|
52
52
|
"path"
|
|
53
53
|
].compact
|
|
54
54
|
call_url_helper_in_main_app(parts, **options)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
def
|
|
58
|
-
|
|
57
|
+
def uchi_as
|
|
58
|
+
Uchi.routes.mount_as
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def uchi_path
|
|
62
|
+
Uchi.routes.mount_at
|
|
59
63
|
end
|
|
60
64
|
end
|
|
61
65
|
end
|
data/lib/uchi/repository.rb
CHANGED
|
@@ -12,6 +12,13 @@ module Uchi
|
|
|
12
12
|
}
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
# Returns the "name" of the controller that handles requests for this
|
|
16
|
+
# repository. Note that this is different from the controllers class name
|
|
17
|
+
# and is intended for generating URLs.
|
|
18
|
+
def controller_name
|
|
19
|
+
model_param_key.pluralize
|
|
20
|
+
end
|
|
21
|
+
|
|
15
22
|
# Returns the repository for the given model, or nil if none is found.
|
|
16
23
|
def for_model(model)
|
|
17
24
|
all.find { |repository| repository.model.to_s == model.to_s }
|
|
@@ -21,6 +28,10 @@ module Uchi
|
|
|
21
28
|
def model
|
|
22
29
|
@model ||= name.demodulize.constantize
|
|
23
30
|
end
|
|
31
|
+
|
|
32
|
+
def model_param_key
|
|
33
|
+
model.model_name.param_key
|
|
34
|
+
end
|
|
24
35
|
end
|
|
25
36
|
|
|
26
37
|
# Returns a new, unsaved instance of the model this repository manages.
|
|
@@ -32,7 +43,7 @@ module Uchi
|
|
|
32
43
|
# repository. Note that this is different from the controllers class name
|
|
33
44
|
# and is intended for generating URLs.
|
|
34
45
|
def controller_name
|
|
35
|
-
|
|
46
|
+
self.class.controller_name
|
|
36
47
|
end
|
|
37
48
|
|
|
38
49
|
def default_sort_order
|
|
@@ -40,18 +51,31 @@ module Uchi
|
|
|
40
51
|
end
|
|
41
52
|
|
|
42
53
|
# Returns an array of fields to show on the edit page.
|
|
54
|
+
#
|
|
55
|
+
# @return [Array<Uchi::Field>]
|
|
43
56
|
def fields_for_edit
|
|
44
|
-
|
|
57
|
+
fields_for(:edit)
|
|
45
58
|
end
|
|
46
59
|
|
|
47
60
|
# Returns an array of fields to show on the index page.
|
|
61
|
+
#
|
|
62
|
+
# @return [Array<Uchi::Field>]
|
|
48
63
|
def fields_for_index
|
|
49
|
-
|
|
64
|
+
fields_for(:index)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns an array of fields to show on the new page.
|
|
68
|
+
#
|
|
69
|
+
# @return [Array<Uchi::Field>]
|
|
70
|
+
def fields_for_new
|
|
71
|
+
fields_for(:new)
|
|
50
72
|
end
|
|
51
73
|
|
|
52
74
|
# Returns an array of fields to show on the show page.
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<Uchi::Field>]
|
|
53
77
|
def fields_for_show
|
|
54
|
-
|
|
78
|
+
fields_for(:show)
|
|
55
79
|
end
|
|
56
80
|
|
|
57
81
|
def find_all(search: nil, scope: model.all, sort_order: default_sort_order)
|
|
@@ -104,7 +128,7 @@ module Uchi
|
|
|
104
128
|
end
|
|
105
129
|
|
|
106
130
|
def model_param_key
|
|
107
|
-
|
|
131
|
+
self.class.model_param_key
|
|
108
132
|
end
|
|
109
133
|
|
|
110
134
|
# Returns an instance of Uchi::Repository::Routes for this repository,
|
|
@@ -174,6 +198,20 @@ module Uchi
|
|
|
174
198
|
end
|
|
175
199
|
end
|
|
176
200
|
|
|
201
|
+
# Returns an array of fields to show for the given action.
|
|
202
|
+
#
|
|
203
|
+
# @param action [Symbol] The action to get fields for. One of :index, :show,
|
|
204
|
+
# :new, :edit.
|
|
205
|
+
#
|
|
206
|
+
# @return [Array<Uchi::Field>]
|
|
207
|
+
def fields_for(action)
|
|
208
|
+
fields_with_repository.select { |field| field.on.include?(action) }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def fields_with_repository
|
|
212
|
+
fields.each { |field| field.repository = self }
|
|
213
|
+
end
|
|
214
|
+
|
|
177
215
|
def searchable_fields
|
|
178
216
|
@searchable_fields ||= fields.select { |field| field.searchable? }
|
|
179
217
|
end
|
data/lib/uchi/routes.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Uchi
|
|
2
|
+
class Routes
|
|
3
|
+
# Mounts the Uchi engine routes onto the host application's routes.
|
|
4
|
+
#
|
|
5
|
+
# Example usage in host application's routes.rb that install Uchi at /uchi:
|
|
6
|
+
#
|
|
7
|
+
# Rails.application.routes.draw do
|
|
8
|
+
# Uchi.routes.mount(self)
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# @param host_routes [ActionDispatch::Routing::Mapper] The host
|
|
12
|
+
# application's routes mapper.
|
|
13
|
+
#
|
|
14
|
+
# @param at [Symbol] The path segment where Uchi should be mounted.
|
|
15
|
+
def mount(host_routes, at: default_at)
|
|
16
|
+
@mount_at = (at || default_at).to_sym
|
|
17
|
+
host_routes.mount(
|
|
18
|
+
Uchi::Engine,
|
|
19
|
+
as: mount_as,
|
|
20
|
+
at: mount_at
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
draw_repository_routes(host_routes, at: mount_at)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def draw_root_route(routes, repository:, at: default_at)
|
|
27
|
+
return unless repository
|
|
28
|
+
|
|
29
|
+
routes.namespace(at, as: mount_as) do
|
|
30
|
+
routes.root to: "#{repository.controller_name}#index"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def draw_repository_routes(routes, at: default_at)
|
|
35
|
+
repositories = Uchi::Repository.all
|
|
36
|
+
repositories.each do |repository_class|
|
|
37
|
+
resources_name = repository_class.controller_name
|
|
38
|
+
routes.namespace(at, as: mount_as) do
|
|
39
|
+
routes.resources(resources_name)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
draw_root_route(routes, at: at, repository: repositories.first)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns the name to use when generating routing helper method names
|
|
47
|
+
def mount_as
|
|
48
|
+
:uchi
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the path prefix for the routes, i.e. the first URL segment where
|
|
52
|
+
# Uchi can be requested.
|
|
53
|
+
def mount_at
|
|
54
|
+
@mount_at ||= default_at
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def default_at
|
|
60
|
+
:uchi
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def mount_path
|
|
64
|
+
mount_at
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/uchi/version.rb
CHANGED
data/lib/uchi.rb
CHANGED
|
@@ -11,10 +11,18 @@ require "uchi/action"
|
|
|
11
11
|
require "uchi/action_response"
|
|
12
12
|
require "uchi/field"
|
|
13
13
|
require "uchi/i18n"
|
|
14
|
+
require "uchi/plugins"
|
|
14
15
|
require "uchi/repository"
|
|
16
|
+
require "uchi/routes"
|
|
15
17
|
require "uchi/sort_order"
|
|
16
18
|
require "uchi/repository/translate"
|
|
17
19
|
|
|
18
20
|
module Uchi
|
|
19
|
-
|
|
21
|
+
def self.plugins
|
|
22
|
+
@plugins ||= Plugins.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.routes
|
|
26
|
+
@routes ||= Routes.new
|
|
27
|
+
end
|
|
20
28
|
end
|