lesli 5.0.23 → 5.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 +4 -4
- data/app/assets/stylesheets/lesli/application.css +1 -192
- data/app/controllers/lesli/abouts_controller.rb +1 -1
- data/app/controllers/lesli/{items → item}/activities_controller.rb +1 -1
- data/app/controllers/lesli/item/discussions_controller.rb +126 -0
- data/app/controllers/lesli/item/tasks_controller.rb +126 -0
- data/app/helpers/lesli/customization_helper.rb +1 -1
- data/app/helpers/lesli/html_helper.rb +1 -1
- data/app/models/concerns/lesli/item/activities.rb +106 -0
- data/app/models/concerns/lesli/item/discussions.rb +92 -0
- data/app/models/concerns/lesli/item/tasks.rb +95 -0
- data/app/models/lesli/account.rb +1 -0
- data/app/models/lesli/{items → item}/activity.rb +5 -13
- data/app/models/lesli/item/discussion.rb +10 -0
- data/{lib/lesli/r_spec.rb → app/models/lesli/item/task.rb} +9 -10
- data/app/views/lesli/partials/_application-lesli-header.html.erb +12 -0
- data/config/initializers/lesli_migration_helpers.rb +2 -2
- data/db/migrate/v1/0000000210_create_lesli_users.rb +1 -1
- data/lib/generators/lesli/base_generator.rb +172 -0
- data/lib/generators/lesli/controller/controller_generator.rb +40 -0
- data/lib/generators/lesli/controller/templates/controller.rb.tt +86 -0
- data/lib/generators/lesli/install/install_generator.rb +1 -1
- data/lib/generators/lesli/model/model_generator.rb +44 -0
- data/lib/generators/lesli/model/templates/model.rb.tt +16 -0
- data/lib/generators/lesli/scaffold/scaffold_generator.rb +47 -0
- data/lib/generators/lesli/service/service_generator.rb +40 -0
- data/lib/generators/lesli/service/templates/service.rb.tt +44 -0
- data/lib/generators/lesli/views/templates/_form.html.erb.tt +22 -0
- data/lib/generators/lesli/views/templates/index.html.erb.tt +17 -0
- data/lib/generators/lesli/views/templates/new.html.erb.tt +4 -0
- data/lib/generators/lesli/views/templates/show.html.erb.tt +22 -0
- data/lib/generators/lesli/views/views_generator.rb +106 -0
- data/lib/lesli/engine.rb +1 -23
- data/lib/lesli/router.rb +42 -43
- data/lib/lesli/version.rb +2 -2
- data/lib/migrate/common.rb +1 -1
- data/lib/migrate/items/activity_structure.rb +11 -5
- data/lib/migrate/items/discussion_structure.rb +9 -3
- data/lib/migrate/items/{action_structure.rb → task_structure.rb} +12 -6
- data/lib/migrate/shared/catalog_structure.rb +16 -0
- data/lib/tasks/lesli/db.rake +9 -0
- data/readme.md +1 -1
- metadata +58 -79
- data/app/assets/config/lesli_manifest.js +0 -42
- data/app/assets/images/lesli/brand/app-auth.svg +0 -9
- data/app/assets/images/lesli/brand/app-icon.svg +0 -48
- data/app/assets/images/lesli/brand/app-logo.png +0 -0
- data/app/assets/images/lesli/brand/app-logo.svg +0 -4
- data/app/assets/images/lesli/brand/favicon.png +0 -0
- data/app/assets/images/lesli/brand/favicon.svg +0 -61
- data/app/assets/images/lesli/brand/login-background.jpg +0 -0
- data/app/assets/images/lesli/brand/register-background.jpg +0 -0
- data/app/assets/javascripts/lesli/application.js +0 -1
- data/app/controllers/lesli/items/actions_controller.rb +0 -122
- data/app/controllers/lesli/items/discussions_controller.rb +0 -93
- data/app/models/lesli/items/action.rb +0 -15
- data/app/models/lesli/items/discussion.rb +0 -15
- data/config/importmap.rb +0 -1
- data/db/structure/00000000_locations.json +0 -32
- data/db/structure/00000020_catalogs.json +0 -0
- data/db/structure/00000201_workflows.json +0 -17
- data/db/structure/00000202_workflow_statuses.json +0 -24
- data/db/structure/00000203_workflow_associations.json +0 -14
- data/db/structure/00000204_workflow_actions.json +0 -39
- data/db/structure/00000205_workflow_checks.json +0 -27
- data/db/structure/00000300_custom_fields.json +0 -8
- data/db/structure/00000301_custom_fields.json +0 -38
- data/db/structure/00000401_custom_validations.json +0 -14
- data/db/structure/00000402_custom_validation_rules.json +0 -23
- data/db/structure/00000403_custom_validation_fields.json +0 -14
- data/lib/generators/application_lesli_generator_base.rb +0 -164
- data/lib/generators/lesli/spec/USAGE +0 -8
- data/lib/generators/lesli/spec/spec_generator.rb +0 -22
- data/lib/generators/lesli/spec/templates/spec-factory.template +0 -17
- data/lib/generators/lesli/spec/templates/spec-model.template +0 -70
- data/lib/generators/lesli/view/USAGE +0 -8
- data/lib/generators/lesli/view/templates/spec-factory.template +0 -17
- data/lib/generators/lesli/view/templates/spec-model.template +0 -70
- data/lib/generators/lesli/view/view_generator.rb +0 -22
- data/lib/scss/_apps.scss +0 -94
- data/lib/scss/application.scss +0 -34
- data/lib/tasks/lesli/docs.rake +0 -47
- data/lib/tasks/lesli/git.rake +0 -70
- data/lib/tasks/lesli/github.rake +0 -89
- data/lib/test/config.rb +0 -111
- data/lib/test/helpers/response_integration_helper.rb +0 -46
- data/lib/test/lesli.rb +0 -61
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
Lesli
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
|
+
|
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
This program is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU General Public License
|
|
18
|
+
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
19
|
+
|
|
20
|
+
Lesli · Ruby on Rails SaaS Development Framework.
|
|
21
|
+
|
|
22
|
+
Made with ♥ by LesliTech
|
|
23
|
+
Building a better future, one line of code at a time.
|
|
24
|
+
|
|
25
|
+
@contact hello@lesli.tech
|
|
26
|
+
@website https://www.lesli.tech
|
|
27
|
+
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
28
|
+
|
|
29
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
|
+
// ·
|
|
31
|
+
=end
|
|
32
|
+
|
|
33
|
+
module Lesli
|
|
34
|
+
module Item
|
|
35
|
+
module Discussions
|
|
36
|
+
extend ActiveSupport::Concern
|
|
37
|
+
|
|
38
|
+
included do
|
|
39
|
+
class_attribute :lesli_discussions_class, instance_accessor: false
|
|
40
|
+
lesli_discussions_setup
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class_methods do
|
|
44
|
+
|
|
45
|
+
# This acts as our allowlist flag
|
|
46
|
+
def is_lesli_commentable?
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Automatically sets the associations to the
|
|
51
|
+
# engine model that is implementing the task items
|
|
52
|
+
def lesli_discussions_setup(use: nil, as: :discussable, association_name: :discussions)
|
|
53
|
+
klass = if use
|
|
54
|
+
use.to_s.constantize
|
|
55
|
+
else
|
|
56
|
+
"#{name.deconstantize}::Item::Discussion".constantize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
self.lesli_discussions_class = klass
|
|
60
|
+
|
|
61
|
+
# ✅ Auto-fix table name (works even when engine defines table_name_prefix)
|
|
62
|
+
expected_table = lesli_expected_discussions_table_name_for(klass)
|
|
63
|
+
|
|
64
|
+
if klass.table_name != expected_table
|
|
65
|
+
klass.table_name = expected_table
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Set associations
|
|
69
|
+
# Example:
|
|
70
|
+
# class LesliSupport::Ticket
|
|
71
|
+
# has_many :lesli_support_tasks
|
|
72
|
+
# end
|
|
73
|
+
has_many(association_name,
|
|
74
|
+
-> { where(deleted_at: nil).order(created_at: :desc) },
|
|
75
|
+
as: as,
|
|
76
|
+
class_name: klass.name,
|
|
77
|
+
dependent: :destroy)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
# Build: "lesli_support_item_activities" from "LesliSupport::Item::Activity"
|
|
83
|
+
#
|
|
84
|
+
# We DO NOT rely on ActiveRecord's table_name_prefix, because that’s what breaks your case.
|
|
85
|
+
def lesli_expected_discussions_table_name_for(klass)
|
|
86
|
+
parts = klass.name.split("::").map(&:underscore) # ["lesli_support", "item", "discussion"]
|
|
87
|
+
"#{parts.join('_').pluralize}" # "lesli_support_item_discussions"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
|
|
3
|
+
Lesli
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
|
+
|
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
This program is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU General Public License
|
|
18
|
+
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
19
|
+
|
|
20
|
+
Lesli · Ruby on Rails SaaS Development Framework.
|
|
21
|
+
|
|
22
|
+
Made with ♥ by LesliTech
|
|
23
|
+
Building a better future, one line of code at a time.
|
|
24
|
+
|
|
25
|
+
@contact hello@lesli.tech
|
|
26
|
+
@website https://www.lesli.tech
|
|
27
|
+
@license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
|
|
28
|
+
|
|
29
|
+
// · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
|
|
30
|
+
// ·
|
|
31
|
+
=end
|
|
32
|
+
|
|
33
|
+
module Lesli
|
|
34
|
+
module Item
|
|
35
|
+
module Tasks
|
|
36
|
+
extend ActiveSupport::Concern
|
|
37
|
+
|
|
38
|
+
included do
|
|
39
|
+
class_attribute :lesli_tasks_class, instance_accessor: false
|
|
40
|
+
lesli_tasks_setup
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class_methods do
|
|
44
|
+
|
|
45
|
+
# This acts as our allowlist flag
|
|
46
|
+
def is_lesli_taskable?
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Automatically sets the associations to the
|
|
51
|
+
# engine model that is implementing the task items
|
|
52
|
+
def lesli_tasks_setup(use: nil, as: :taskable, association_name: :tasks)
|
|
53
|
+
klass = if use
|
|
54
|
+
use.to_s.constantize
|
|
55
|
+
else
|
|
56
|
+
"#{name.deconstantize}::Item::Task".constantize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
self.lesli_tasks_class = klass
|
|
60
|
+
|
|
61
|
+
# ✅ Auto-fix table name (works even when engine defines table_name_prefix)
|
|
62
|
+
expected_table = lesli_expected_task_table_name_for(klass)
|
|
63
|
+
|
|
64
|
+
if klass.table_name != expected_table
|
|
65
|
+
klass.table_name = expected_table
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Set associations
|
|
69
|
+
# Example:
|
|
70
|
+
# class LesliSupport::Ticket
|
|
71
|
+
# has_many :lesli_support_tasks
|
|
72
|
+
# end
|
|
73
|
+
has_many(association_name,
|
|
74
|
+
-> { where(deleted_at: nil).order(created_at: :desc) },
|
|
75
|
+
as: as,
|
|
76
|
+
class_name: klass.name,
|
|
77
|
+
dependent: :destroy)
|
|
78
|
+
rescue NameError
|
|
79
|
+
raise NameError,
|
|
80
|
+
"Task class not found for #{name}. Expected #{name.deconstantize}::Task or pass `use:`"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Build: "lesli_support_item_activities" from "LesliSupport::Item::Activity"
|
|
86
|
+
#
|
|
87
|
+
# We DO NOT rely on ActiveRecord's table_name_prefix, because that’s what breaks your case.
|
|
88
|
+
def lesli_expected_task_table_name_for(klass)
|
|
89
|
+
parts = klass.name.split("::").map(&:underscore) # ["lesli_support", "item", "task"]
|
|
90
|
+
"#{parts.join('_').pluralize}" # "lesli_support_item_tasks"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/app/models/lesli/account.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lesli
|
|
4
4
|
|
|
5
|
-
Copyright (c)
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
6
|
|
|
7
7
|
This program is free software: you can redistribute it and/or modify
|
|
8
8
|
it under the terms of the GNU General Public License as published by
|
|
@@ -31,20 +31,12 @@ Building a better future, one line of code at a time.
|
|
|
31
31
|
=end
|
|
32
32
|
|
|
33
33
|
module Lesli
|
|
34
|
-
module
|
|
34
|
+
module Item
|
|
35
35
|
class Activity < ApplicationRecord
|
|
36
36
|
self.abstract_class = true
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
enum :activity_type, {
|
|
41
|
-
activity: "activity",
|
|
42
|
-
system: "system"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# if needed should be defined
|
|
46
|
-
# by the user at resource level
|
|
47
|
-
# enum activity_code: { }
|
|
37
|
+
belongs_to :user, class_name: "Lesli::User"
|
|
38
|
+
belongs_to :account, class_name: "Lesli::Account"
|
|
39
|
+
belongs_to :subject, polymorphic: true
|
|
48
40
|
end
|
|
49
41
|
end
|
|
50
42
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Lesli
|
|
4
4
|
|
|
5
|
-
Copyright (c)
|
|
5
|
+
Copyright (c) 2026, Lesli Technologies, S. A.
|
|
6
6
|
|
|
7
7
|
This program is free software: you can redistribute it and/or modify
|
|
8
8
|
it under the terms of the GNU General Public License as published by
|
|
@@ -30,14 +30,13 @@ Building a better future, one line of code at a time.
|
|
|
30
30
|
// ·
|
|
31
31
|
=end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
module
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Lesli::
|
|
33
|
+
module Lesli
|
|
34
|
+
module Item
|
|
35
|
+
class Task < ApplicationRecord
|
|
36
|
+
self.abstract_class = true
|
|
37
|
+
belongs_to :user, class_name: "Lesli::User"
|
|
38
|
+
belongs_to :account, class_name: "Lesli::Account"
|
|
39
|
+
belongs_to :taskable, polymorphic: true
|
|
38
40
|
end
|
|
39
|
-
|
|
40
|
-
Lesli::Engine.root.join("lib/rspec/testers/model")
|
|
41
|
-
end
|
|
42
|
-
end
|
|
41
|
+
end
|
|
43
42
|
end
|
|
@@ -56,6 +56,18 @@ Building a better future, one line of code at a time.
|
|
|
56
56
|
</button>
|
|
57
57
|
</div>
|
|
58
58
|
<div class="navbar-menu" :class="{ 'is-active': navActive }">
|
|
59
|
+
<div class="navbar-start is-flex is-flex-grow-1 is-justify-content-center">
|
|
60
|
+
<%# ["favorite","sentiment_excited","person_pin_circle","manage_accounts"].each do |icon| %>
|
|
61
|
+
<% [].each do |icon| %>
|
|
62
|
+
<div class="navbar-item">
|
|
63
|
+
<a data-turbo-frame="_top" href="/babel/labels" class="icon py-4 px-5">
|
|
64
|
+
<span class="material-symbols-outlined">
|
|
65
|
+
<%= icon %>
|
|
66
|
+
</span>
|
|
67
|
+
</a>
|
|
68
|
+
</div>
|
|
69
|
+
<% end %>
|
|
70
|
+
</div>
|
|
59
71
|
<div class="navbar-end is-flex is-justify-content-center">
|
|
60
72
|
|
|
61
73
|
<!-- notifications -->
|
|
@@ -32,7 +32,7 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
require(Lesli::Engine.root.join("lib/migrate/common"))
|
|
34
34
|
|
|
35
|
-
require(Lesli::Engine.root.join("lib/migrate/items/
|
|
35
|
+
require(Lesli::Engine.root.join("lib/migrate/items/task_structure"))
|
|
36
36
|
require(Lesli::Engine.root.join("lib/migrate/items/activity_structure"))
|
|
37
37
|
require(Lesli::Engine.root.join("lib/migrate/items/attachment_structure"))
|
|
38
38
|
require(Lesli::Engine.root.join("lib/migrate/items/discussion_structure"))
|
|
@@ -45,7 +45,7 @@ require(Lesli::Engine.root.join("lib/migrate/shared/catalog_structure"))
|
|
|
45
45
|
|
|
46
46
|
ActiveRecord::Migration.include(MigrationHelpers::Common)
|
|
47
47
|
|
|
48
|
-
ActiveRecord::Migration.include(MigrationHelpers::Items::
|
|
48
|
+
ActiveRecord::Migration.include(MigrationHelpers::Items::TaskStructure)
|
|
49
49
|
ActiveRecord::Migration.include(MigrationHelpers::Items::ActivityStructure)
|
|
50
50
|
ActiveRecord::Migration.include(MigrationHelpers::Items::AttachmentStructure)
|
|
51
51
|
ActiveRecord::Migration.include(MigrationHelpers::Items::DiscussionStructure)
|
|
@@ -94,7 +94,7 @@ class CreateLesliUsers < ActiveRecord::Migration[7.2]
|
|
|
94
94
|
add_reference(:lesli_users, :account, foreign_key: { to_table: :lesli_accounts })
|
|
95
95
|
add_reference(:lesli_accounts, :user, foreign_key: { to_table: :lesli_users })
|
|
96
96
|
|
|
97
|
-
add_index(:lesli_users, :uid,
|
|
97
|
+
add_index(:lesli_users, :uid, unique: true)
|
|
98
98
|
add_index(:lesli_users, :email, unique: true)
|
|
99
99
|
add_index(:lesli_users, :unlock_token, unique: true)
|
|
100
100
|
add_index(:lesli_users, :confirmation_token, unique: true)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Lesli
|
|
6
|
+
module Generators
|
|
7
|
+
# Shared base generator for all Lesli generators.
|
|
8
|
+
#
|
|
9
|
+
# This class centralizes the logic used by all generators, such as:
|
|
10
|
+
#
|
|
11
|
+
# - engine name detection
|
|
12
|
+
# - engine namespace detection
|
|
13
|
+
# - domain name normalization
|
|
14
|
+
# - resource naming helpers
|
|
15
|
+
#
|
|
16
|
+
# This avoids duplicating the same helper methods in every generator.
|
|
17
|
+
class BaseGenerator < Rails::Generators::NamedBase
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Returns the current engine folder name in snake_case.
|
|
22
|
+
#
|
|
23
|
+
# Examples:
|
|
24
|
+
# "/code/lesli_support" -> "lesli_support"
|
|
25
|
+
# "/code/my_engine" -> "my_engine"
|
|
26
|
+
# "/code/store" -> "store"
|
|
27
|
+
def engine_name
|
|
28
|
+
File.basename(ENGINE_ROOT.to_s).underscore
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the current engine Ruby namespace in CamelCase.
|
|
32
|
+
#
|
|
33
|
+
# Examples:
|
|
34
|
+
# "lesli_support" -> "LesliSupport"
|
|
35
|
+
# "my_engine" -> "MyEngine"
|
|
36
|
+
# "store" -> "Store"
|
|
37
|
+
def engine_namespace
|
|
38
|
+
engine_name.camelize
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns the domain name derived from the engine namespace.
|
|
42
|
+
#
|
|
43
|
+
# Rules:
|
|
44
|
+
# - If the engine starts with "Lesli", remove that prefix
|
|
45
|
+
# - Otherwise use the full engine namespace
|
|
46
|
+
# - Convert the result to snake_case
|
|
47
|
+
#
|
|
48
|
+
# Examples:
|
|
49
|
+
# "LesliSupport" -> "support"
|
|
50
|
+
# "LesliCalendar" -> "calendar"
|
|
51
|
+
# "MyEngine" -> "my_engine"
|
|
52
|
+
# "Store" -> "store"
|
|
53
|
+
def engine_domain_name
|
|
54
|
+
namespace = engine_namespace
|
|
55
|
+
|
|
56
|
+
if namespace.start_with?("Lesli") && namespace != "Lesli"
|
|
57
|
+
namespace.delete_prefix("Lesli").underscore
|
|
58
|
+
else
|
|
59
|
+
namespace.underscore
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the singular normalized resource name.
|
|
64
|
+
#
|
|
65
|
+
# Examples:
|
|
66
|
+
# "tickets" -> "ticket"
|
|
67
|
+
# "products" -> "product"
|
|
68
|
+
# "store" -> "store"
|
|
69
|
+
#
|
|
70
|
+
# This is the main workaround for NamedBase, because by default
|
|
71
|
+
# a plural input like "products" may produce class_name = "Products".
|
|
72
|
+
def resource_name
|
|
73
|
+
name.to_s.singularize.underscore
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Returns the plural normalized resource name.
|
|
77
|
+
#
|
|
78
|
+
# Examples:
|
|
79
|
+
# "ticket" -> "tickets"
|
|
80
|
+
# "product" -> "products"
|
|
81
|
+
def resource_collection_name
|
|
82
|
+
resource_name.pluralize
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns the Ruby model class name.
|
|
86
|
+
#
|
|
87
|
+
# Examples:
|
|
88
|
+
# "ticket" -> "Ticket"
|
|
89
|
+
# "product" -> "Product"
|
|
90
|
+
def resource_class_name
|
|
91
|
+
resource_name.camelize
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the plural controller class name.
|
|
95
|
+
#
|
|
96
|
+
# Examples:
|
|
97
|
+
# "tickets" -> "Tickets"
|
|
98
|
+
# "products" -> "Products"
|
|
99
|
+
def controller_class_name
|
|
100
|
+
resource_collection_name.camelize
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Returns the service class name.
|
|
104
|
+
#
|
|
105
|
+
# Examples:
|
|
106
|
+
# "Ticket" -> "TicketService"
|
|
107
|
+
# "Product" -> "ProductService"
|
|
108
|
+
def service_class_name
|
|
109
|
+
"#{resource_class_name}Service"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns the resource instance variable name.
|
|
113
|
+
#
|
|
114
|
+
# Examples:
|
|
115
|
+
# "ticket" -> "@ticket"
|
|
116
|
+
# "product" -> "@product"
|
|
117
|
+
def resource_instance
|
|
118
|
+
"@#{resource_name}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns the resource collection instance variable name.
|
|
122
|
+
#
|
|
123
|
+
# Examples:
|
|
124
|
+
# "tickets" -> "@tickets"
|
|
125
|
+
# "products" -> "@products"
|
|
126
|
+
def resource_collection_instance
|
|
127
|
+
"@#{resource_collection_name}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns the before_action setter method name.
|
|
131
|
+
#
|
|
132
|
+
# Examples:
|
|
133
|
+
# "ticket" -> "set_ticket"
|
|
134
|
+
# "product" -> "set_product"
|
|
135
|
+
def set_resource_method_name
|
|
136
|
+
"set_#{resource_name}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Returns the params method name.
|
|
140
|
+
#
|
|
141
|
+
# Examples:
|
|
142
|
+
# "ticket" -> "ticket_params"
|
|
143
|
+
# "product" -> "product_params"
|
|
144
|
+
def params_method_name
|
|
145
|
+
"#{resource_name}_params"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the namespaced model class name.
|
|
149
|
+
#
|
|
150
|
+
# Examples:
|
|
151
|
+
# "LesliSupport::Ticket"
|
|
152
|
+
# "MyEngine::Product"
|
|
153
|
+
def namespaced_resource_class_name
|
|
154
|
+
"#{engine_namespace}::#{resource_class_name}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Returns only the attribute names passed to the generator.
|
|
158
|
+
#
|
|
159
|
+
# Example:
|
|
160
|
+
# ["subject:string", "owner_id:integer"] -> ["subject", "owner_id"]
|
|
161
|
+
def permitted_attributes
|
|
162
|
+
return [] unless respond_to?(:attributes)
|
|
163
|
+
|
|
164
|
+
attributes.map(&:name)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def namespaced_model_name
|
|
168
|
+
"#{engine_namespace}::#{resource_class_name}"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require_relative "../base_generator"
|
|
5
|
+
|
|
6
|
+
module Lesli
|
|
7
|
+
module Generators
|
|
8
|
+
# Generates a Lesli-style controller inside the current engine.
|
|
9
|
+
class ControllerGenerator < BaseGenerator
|
|
10
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
11
|
+
|
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
|
13
|
+
|
|
14
|
+
# Creates the controller file from the template.
|
|
15
|
+
def create_controller_file
|
|
16
|
+
template "controller.rb.tt", controller_file_path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Returns the controller directory inside the engine namespace.
|
|
22
|
+
#
|
|
23
|
+
# Example:
|
|
24
|
+
# app/controllers/lesli_support
|
|
25
|
+
# app/controllers/my_engine
|
|
26
|
+
# app/controllers/store
|
|
27
|
+
def controller_directory
|
|
28
|
+
File.join(ENGINE_ROOT, "app/controllers", engine_name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the final controller file path.
|
|
32
|
+
#
|
|
33
|
+
# Example:
|
|
34
|
+
# app/controllers/lesli_support/tickets_controller.rb
|
|
35
|
+
def controller_file_path
|
|
36
|
+
File.join(controller_directory, "#{resource_collection_name}_controller.rb")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= engine_namespace %>
|
|
4
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
|
5
|
+
before_action :<%= set_resource_method_name %>, only: [:show, :edit, :update, :destroy]
|
|
6
|
+
|
|
7
|
+
def index
|
|
8
|
+
<%= resource_collection_instance %> = respond_with_pagination(
|
|
9
|
+
<%= service_class_name %>.new(current_user, query).index
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def show
|
|
14
|
+
<%= resource_instance %> = <%= resource_instance %>.show
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def new
|
|
18
|
+
<%= resource_instance %> = <%= resource_class_name %>.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def edit
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create
|
|
25
|
+
<%= resource_name %> = <%= service_class_name %>.new(current_user, query).create(<%= params_method_name %>)
|
|
26
|
+
|
|
27
|
+
<%= resource_instance %> = <%= resource_name %>.result
|
|
28
|
+
|
|
29
|
+
if <%= resource_name %>.successful?
|
|
30
|
+
success("<%= resource_name %> creado de forma exitosa")
|
|
31
|
+
|
|
32
|
+
respond_with_lesli(
|
|
33
|
+
turbo: stream_redirection(<%= resource_name %>_path(<%= resource_instance %>.id))
|
|
34
|
+
)
|
|
35
|
+
else
|
|
36
|
+
respond_with_lesli(
|
|
37
|
+
turbo: <%= resource_name %>.errors_as_sentence
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def update
|
|
43
|
+
if <%= resource_instance %>.update(<%= params_method_name %>)
|
|
44
|
+
respond_with_lesli(
|
|
45
|
+
turbo: stream_notification_success("<%= resource_class_name %> updated successfully")
|
|
46
|
+
)
|
|
47
|
+
else
|
|
48
|
+
respond_with_lesli(
|
|
49
|
+
turbo: stream_notification_danger(<%= resource_instance %>.errors)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def destroy
|
|
55
|
+
return respond_with_not_found unless <%= resource_instance %>
|
|
56
|
+
return respond_with_unauthorized unless <%= resource_instance %>.is_editable_by?(current_user)
|
|
57
|
+
|
|
58
|
+
<%= resource_name %>_destroy_response = <%= service_class_name %>.destroy(current_user, <%= resource_instance %>)
|
|
59
|
+
|
|
60
|
+
if <%= resource_name %>_destroy_response.successful?
|
|
61
|
+
respond_with_successful
|
|
62
|
+
else
|
|
63
|
+
respond_with_error(<%= resource_name %>_destroy_response.payload.errors.full_messages.to_sentence)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def <%= set_resource_method_name %>
|
|
70
|
+
<%= resource_instance %> = <%= service_class_name %>.new(current_user, query).find(id: params[:id])
|
|
71
|
+
return respond_with_not_found unless <%= resource_instance %>.found?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def <%= params_method_name %>
|
|
75
|
+
params.require(:<%= resource_name %>).permit(
|
|
76
|
+
<% if permitted_attributes.any? -%>
|
|
77
|
+
<% permitted_attributes.each_with_index do |attribute, index| -%>
|
|
78
|
+
:<%= attribute %><%= "," unless index == permitted_attributes.length - 1 %>
|
|
79
|
+
<% end -%>
|
|
80
|
+
<% else -%>
|
|
81
|
+
# Add permitted attributes here
|
|
82
|
+
<% end -%>
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require_relative "../base_generator"
|
|
5
|
+
|
|
6
|
+
module Lesli
|
|
7
|
+
module Generators
|
|
8
|
+
# Generates a Lesli-style model inside the current engine.
|
|
9
|
+
#
|
|
10
|
+
# Example:
|
|
11
|
+
# rails generate lesli:model tickets
|
|
12
|
+
#
|
|
13
|
+
# Result:
|
|
14
|
+
# app/models/lesli_support/ticket.rb
|
|
15
|
+
class ModelGenerator < BaseGenerator
|
|
16
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
17
|
+
|
|
18
|
+
source_root File.expand_path("templates", __dir__)
|
|
19
|
+
|
|
20
|
+
# Creates the model file from the template.
|
|
21
|
+
def create_model_file
|
|
22
|
+
template "model.rb.tt", model_file_path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Returns the directory where the model file should be created.
|
|
28
|
+
#
|
|
29
|
+
# Example:
|
|
30
|
+
# app/models/lesli_support
|
|
31
|
+
def model_directory
|
|
32
|
+
File.join(ENGINE_ROOT, "app/models", engine_name)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns the final path of the generated model file.
|
|
36
|
+
#
|
|
37
|
+
# Example:
|
|
38
|
+
# app/models/lesli_support/ticket.rb
|
|
39
|
+
def model_file_path
|
|
40
|
+
File.join(model_directory, "#{resource_name}.rb")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|