todoro 0.1.4 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e52adb97a2a743b91e09a3b654b3ac9d7782298c6ee3b933bad51d1a2a94caf
4
- data.tar.gz: d8b17e1c03b70d67aec43528fe6b8acec2e9519e6ad0c2797f48af22012b5147
3
+ metadata.gz: 5fdd815954054219782d8b2281bced824aca47cb6c7add82408cf6d1fdad3f4d
4
+ data.tar.gz: aa21ff161a69f8d9ae91cc5634871fdb354aeead527c92b8798b6526e576812b
5
5
  SHA512:
6
- metadata.gz: 4638b8767f43a852653c6f68ef6ae718fc70a1281f39d09d32554facf1038d4507e11f46669de3b39d4345153876a6dad15aa1d74a120021a2f4416cd64f25e1
7
- data.tar.gz: 0b5232015299955bf5f2825bcbe540fcad438f75d1346fa7c3758d23978f6f0d80c46ebe2cfc473101a743861e76fc3030b7b77021b400a4b5d2c6d885302ccb
6
+ metadata.gz: d5c69c6f569090184889b7536ea4d380c5a616be36155a70941e7f2cae1095a73780b4844013539d3ff536da18db1f34b48ce62e5dd9d5bcce37e00239c1d1d6
7
+ data.tar.gz: ee5361a8bc6904bba93619563a6b4d504a5e2b359c8c75ad25b44fda556b8b2d79349011a50b6ad32c1af5613459eec36320317584cec7ff753345d68216fe2d
data/README.md CHANGED
@@ -1,6 +1,3 @@
1
- To enhance the **Todoro** gem's documentation, here's an updated version of the `README.md` file that includes instructions on mounting the engine and customizing the mount path:
2
-
3
- ```markdown
4
1
  # Todoro
5
2
 
6
3
  A lightweight Rails engine that lets any model manage task lists and tasks with a simple, flexible API.
@@ -54,6 +51,22 @@ task = project.add_task_to_list(task_list, "Build homepage", "Implement UI compo
54
51
  puts project.tasks # Lists all tasks in all its task lists
55
52
  ```
56
53
 
54
+ ## Automatic Route Generation
55
+
56
+ Todoro automatically generates routes for any model that includes `acts_as_taskable`. For example, if `Project` includes `acts_as_taskable`, the following routes are created:
57
+
58
+ ```sh
59
+ GET /projects/:project_id/task_lists(.:format)
60
+ POST /projects/:project_id/task_lists(.:format)
61
+ GET /projects/:project_id/task_lists/:id(.:format)
62
+ PATCH /projects/:project_id/task_lists/:id(.:format)
63
+ DELETE /projects/:project_id/task_lists/:id(.:format)
64
+ GET /projects/:project_id/task_lists/:task_list_id/tasks(.:format)
65
+ POST /projects/:project_id/task_lists/:task_list_id/tasks(.:format)
66
+ ```
67
+
68
+ These routes are automatically configured, so no additional setup is needed.
69
+
57
70
  ## Mounting the Engine
58
71
 
59
72
  To access Todoro's routes, mount the engine in your application's routes file:
@@ -61,31 +74,13 @@ To access Todoro's routes, mount the engine in your application's routes file:
61
74
  ```ruby
62
75
  # config/routes.rb
63
76
  Rails.application.routes.draw do
64
- mount Todoro::Engine, at: '/todoro'
77
+ mount Todoro::Engine, at: "/"
65
78
  # Other routes...
66
79
  end
67
80
  ```
68
81
 
69
- This makes Todoro's features accessible under the `/todoro` path (e.g., `http://yourdomain.com/todoro`).
70
-
71
- ### Customizing the Mount Path
72
-
73
- You can mount Todoro at any path that fits your application's structure:
74
-
75
- - To mount at the root:
76
-
77
- ```ruby
78
- Rails.application.routes.draw do
79
- mount Todoro::Engine, at: '/'
80
- # Other routes...
81
- end
82
- ```
83
-
84
- Now, Todoro's routes are available at the root URL (e.g., `http://yourdomain.com/`).
85
-
82
+ Since Todoro dynamically registers routes for `acts_as_taskable` models, no further route configuration is required.
86
83
 
87
84
  ## License
88
85
 
89
86
  The gem is available as open source under the terms of the [MIT License](MIT-LICENSE).
90
-
91
- ```
@@ -0,0 +1,6 @@
1
+ class TaskableModelsConstraint
2
+ def matches?(request)
3
+ taskable_type = request.params[:taskable_type]&.classify
4
+ Todoro.taskable_models.include?(taskable_type)
5
+ end
6
+ end
@@ -1,4 +1,26 @@
1
1
  module Todoro
2
- class ApplicationController < ActionController::Base
2
+ class ApplicationController < ::ApplicationController
3
+ before_action :set_taskable
4
+ before_action :validate_taskable!
5
+
6
+ private
7
+
8
+ def set_taskable
9
+ taskable_type = params[:taskable_type]&.classify
10
+ taskable_id_param = "#{params[:taskable_type]}_id"
11
+ taskable_id = params[taskable_id_param]
12
+
13
+ if taskable_type.present? && taskable_id.present? && Todoro.taskable_models.include?(taskable_type)
14
+ @taskable = taskable_type.safe_constantize&.find_by(id: taskable_id)
15
+ end
16
+
17
+ render json: { error: "Taskable not found" }, status: :not_found unless @taskable
18
+ end
19
+
20
+ def validate_taskable!
21
+ unless @taskable && Todoro.taskable_models.include?(@taskable.class.name)
22
+ render json: { error: "Unauthorized taskable model" }, status: :forbidden
23
+ end
24
+ end
3
25
  end
4
26
  end
@@ -1,7 +1,6 @@
1
1
  module Todoro
2
2
  class TaskListsController < ApplicationController
3
3
  before_action :set_task_list, only: %i[show edit update destroy]
4
- before_action :set_taskable, only: %i[index new create]
5
4
 
6
5
  def index
7
6
  @task_lists = @taskable.task_lists
@@ -16,7 +15,7 @@ module Todoro
16
15
  def create
17
16
  @task_list = @taskable.task_lists.new(task_list_params)
18
17
  if @task_list.save
19
- redirect_to task_lists_path(taskable: @taskable), notice: "Task list created successfully."
18
+ redirect_to [ @taskable, @task_list ], notice: "Task list created successfully."
20
19
  else
21
20
  render :new
22
21
  end
@@ -26,7 +25,7 @@ module Todoro
26
25
 
27
26
  def update
28
27
  if @task_list.update(task_list_params)
29
- redirect_to task_lists_path(taskable: @task_list.taskable), notice: "Task list updated successfully."
28
+ redirect_to [ @taskable, @task_list ], notice: "Task list updated successfully."
30
29
  else
31
30
  render :edit
32
31
  end
@@ -35,23 +34,14 @@ module Todoro
35
34
  def destroy
36
35
  taskable = @task_list.taskable
37
36
  @task_list.destroy
38
- redirect_to task_lists_path(taskable: taskable), notice: "Task list deleted."
37
+
38
+ redirect_to [ @taskable, :task_list ], notice: "Task list deleted."
39
39
  end
40
40
 
41
41
  private
42
42
 
43
43
  def set_task_list
44
- @task_list = TaskList.find(params[:id])
45
- end
46
-
47
- def set_taskable
48
- @taskable = find_taskable
49
- end
50
-
51
- def find_taskable
52
- return unless params[:taskable] && params[:taskable_id]
53
-
54
- params[:taskable].constantize.find(params[:taskable_id])
44
+ @task_list = @taskable.task_lists.find(params[:id])
55
45
  end
56
46
 
57
47
  def task_list_params
@@ -8,9 +8,10 @@ module Todoro
8
8
  end
9
9
 
10
10
  def create
11
- @task = @task_list.tasks.new(task_params)
11
+ @task = @task_list.tasks.build(task_params)
12
+
12
13
  if @task.save
13
- redirect_to task_list_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, id: @task_list.id), notice: "Task created successfully."
14
+ redirect_to [ @taskable, @task_list ], notice: "Task created successfully."
14
15
  else
15
16
  render :new
16
17
  end
@@ -20,7 +21,7 @@ module Todoro
20
21
 
21
22
  def update
22
23
  if @task.update(task_params)
23
- redirect_to @task_list, notice: "Task updated successfully."
24
+ redirect_to [ @taskable, @task_list ], notice: "Task updated successfully."
24
25
  else
25
26
  render :edit
26
27
  end
@@ -28,26 +29,26 @@ module Todoro
28
29
 
29
30
  def destroy
30
31
  @task.destroy
31
- redirect_to @task_list, notice: "Task deleted."
32
+ redirect_to [ @taskable, @task_list ], notice: "Task deleted."
32
33
  end
33
34
 
34
35
  def complete
35
36
  @task.update(status: "completed")
36
- redirect_to task_list_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, id: @task_list.id), notice: "Task marked as completed."
37
+ redirect_to [ @taskable, @task_list ], notice: "Task marked as completed."
37
38
  end
38
39
 
39
40
  private
40
41
 
41
- def set_task_list
42
- @task_list = Todoro::TaskList.find(params[:task_list_id])
43
- end
44
-
45
42
  def set_task
46
43
  @task = @task_list.tasks.find(params[:id])
47
44
  end
48
45
 
46
+ def set_task_list
47
+ @task_list = @taskable.task_lists.find(params[:task_list_id])
48
+ end
49
+
49
50
  def task_params
50
- params.require(:task).permit(:title, :description, :expiry_date, :status)
51
+ params.require(:task).permit(:title, :description, :status, :expiry_date)
51
52
  end
52
53
  end
53
54
  end
@@ -4,7 +4,6 @@ module Todoro
4
4
  has_many :reminders, dependent: :destroy
5
5
 
6
6
  validates :title, presence: true
7
- validates :description, presence: true
8
7
  validates :status, presence: true, inclusion: { in: %w[pending completed] }
9
8
 
10
9
  enum :status, { pending: "pending", completed: "completed" }
@@ -1,12 +1,9 @@
1
- <%= form_with model: [taskable, task_list], local: true do |form| %>
1
+ <%= form_with model: task_list, url: polymorphic_path([@taskable, task_list]), local: true do |form| %>
2
2
  <div class="form-group">
3
3
  <%= form.label :name %>
4
4
  <%= form.text_field :name, class: "form-control", required: true %>
5
5
  </div>
6
6
 
7
- <%= form.hidden_field :taskable_id, value: taskable.id %>
8
- <%= form.hidden_field :taskable_type, value: taskable.class.name %>
9
7
 
10
8
  <%= form.submit class: "btn btn-success" %>
11
9
  <% end %>
12
-
@@ -2,4 +2,4 @@
2
2
 
3
3
  <%= render "form", task_list: @task_list %>
4
4
 
5
- <%= link_to "Back to Task Lists", task_lists_path, class: "btn btn-secondary" %>
5
+ <%= link_to "Back to Task Lists", polymorphic_path([@task_list.taskable, :task_lists]), class: "btn btn-secondary" %>
@@ -1,15 +1,15 @@
1
1
  <h1>Task Lists for <%= @taskable.class.name %>: <%= @taskable.try(:name) || "Unknown" %></h1>
2
2
 
3
- <%= link_to "Create New Task List", new_taskable_task_list_path(taskable: @taskable.class.name, taskable_id: @taskable.id), class: "btn btn-primary" %>
3
+ <%= link_to "Create New Task List", new_polymorphic_path([@taskable, :task_list]), class: "btn btn-primary" %>
4
4
 
5
5
  <ul>
6
6
  <% @task_lists.each do |task_list| %>
7
7
  <li>
8
- <%= link_to task_list.name, taskable_task_list_path(taskable: @taskable.class.name, taskable_id: @taskable.id, id: task_list.id) %>
9
- (<%= link_to "Edit", edit_taskable_task_list_path(taskable: @taskable.class.name, taskable_id: @taskable.id, id: task_list.id) %> |
10
- <%= link_to "Delete", taskable_task_list_path(taskable: @taskable.class.name, taskable_id: @taskable.id, id: task_list.id), method: :delete, data: { confirm: "Are you sure?" } %>)
8
+ <%= link_to task_list.name, polymorphic_path([ @taskable, task_list]) %>
9
+ (<%= link_to "Edit", edit_polymorphic_path([@taskable, task_list ])%> |
10
+ <%= link_to "Delete", polymorphic_path([ @taskable, task_list ]), method: :delete, data: { confirm: "Are you sure?" } %>)
11
11
  </li>
12
12
  <% end %>
13
13
  </ul>
14
14
 
15
- <%= link_to "Back", root_path, class: "btn btn-secondary" %>
15
+ <%= link_to "Back", polymorphic_path(@taskable), class: "btn btn-secondary" %>
@@ -2,4 +2,4 @@
2
2
 
3
3
  <%= render "form", task_list: @task_list %>
4
4
 
5
- <%= link_to "Back to Task Lists", task_lists_path, class: "btn btn-secondary" %>
5
+ <%= link_to "Back to Task Lists", polymorphic_path([@taskable, @task_list]), class: "btn btn-secondary" %>
@@ -1,21 +1,24 @@
1
1
  <h1><%= @task_list.name %></h1>
2
2
 
3
- <%= link_to "Edit", edit_taskable_task_list_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, id: @task_list.id), class: "btn btn-warning" %>
4
- <%= link_to "Delete", taskable_task_list_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, id: @task_list.id), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-danger" %>
3
+ <%= link_to "Edit", edit_polymorphic_path([@task_list.taskable, @task_list]), class: "btn btn-warning" %>
4
+
5
+ <%= button_to "Delete", polymorphic_path([@task_list.taskable, @task_list]), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-danger btn-link" %>
5
6
 
6
7
  <h2>Tasks</h2>
7
- <%= link_to "Add Task", new_taskable_task_list_task_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, task_list_id: @task_list.id), class: "btn btn-primary" %>
8
+ <%= link_to "Add Task", new_polymorphic_path([@task_list.taskable, @task_list, :task]), class: "btn btn-primary" %>
8
9
 
9
10
  <ul>
10
11
  <% @task_list.tasks.each do |task| %>
11
12
  <li>
12
13
  <%= task.title %> - <%= task.status %>
13
- (<%= link_to "Edit", edit_taskable_task_list_task_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, task_list_id: @task_list.id, id: task.id) %> |
14
- <%= link_to "Complete", complete_taskable_task_list_task_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, task_list_id: @task_list.id, id: task.id), method: :patch if task.status == "pending" %> |
15
- <%= link_to "Delete", taskable_task_list_task_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id, task_list_id: @task_list.id, id: task.id), method: :delete, data: { confirm: "Are you sure?" } %>)
14
+ <%= link_to "Edit", edit_polymorphic_path([@task_list.taskable, @task_list, task]) %> |
15
+
16
+ <%= button_to "Complete", polymorphic_path([@task_list.taskable, @task_list, task], action: :complete), method: :patch, class: "btn btn-success btn-link", data: { turbo: true } if task.status == "pending" %>
17
+
18
+ <%= button_to "Delete", polymorphic_path([@task_list.taskable, @task_list, task]), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-danger btn-link" %>
19
+
16
20
  </li>
17
21
  <% end %>
18
22
  </ul>
19
23
 
20
- <%= link_to "Back to Task Lists", taskable_task_lists_path(taskable: @task_list.taskable.class.name, taskable_id: @task_list.taskable.id), class: "btn btn-secondary" %>
21
-
24
+ <%= link_to "Back to Task Lists", polymorphic_path([@task_list.taskable, :task_lists]), class: "btn btn-secondary" %>
@@ -1,4 +1,4 @@
1
- <%= form_with model: [task.task_list, task], local: true do |form| %>
1
+ <%= form_with model: task, url: polymorphic_path([@taskable, @task_list, @task]), local: true do |form| %>
2
2
  <div class="form-group">
3
3
  <%= form.label :title %>
4
4
  <%= form.text_field :title, class: "form-control", required: true %>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <div class="form-group">
8
8
  <%= form.label :description %>
9
- <%= form.text_area :description, class: "form-control", required: true %>
9
+ <%= form.text_area :description, class: "form-control" %>
10
10
  </div>
11
11
 
12
12
  <div class="form-group">
@@ -2,4 +2,4 @@
2
2
 
3
3
  <%= render "form", task: @task %>
4
4
 
5
- <%= link_to "Back to Task List", task_list_path(@task_list), class: "btn btn-secondary" %>
5
+ <%= link_to "Back to Task List", polymorphic_path([@taskable, @task_list]), class: "btn btn-secondary" %>
@@ -2,4 +2,4 @@
2
2
 
3
3
  <%= render "form", task: @task %>
4
4
 
5
- <%= link_to "Back to Task List", task_list_path(@task_list), class: "btn btn-secondary" %>
5
+ <%= link_to "Back to Task List", polymorphic_path([@taskable, @task_list]), class: "btn btn-secondary" %>
data/config/routes.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  Todoro::Engine.routes.draw do
2
- resources :task_lists do
3
- resources :tasks do
4
- member do
5
- patch :complete
2
+ Todoro.taskable_models.each do |model|
3
+ resource_name = model.underscore.pluralize.to_sym
4
+
5
+ resources resource_name, defaults: { taskable_type: model.downcase }, constraints: TaskableModelsConstraint.new do
6
+ resources :task_lists do
7
+ resources :tasks, only: [ :new, :edit, :create, :update, :destroy ] do
8
+ patch :complete, on: :member
9
+ end
6
10
  end
7
11
  end
8
12
  end
@@ -1,3 +1,3 @@
1
1
  module Todoro
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
data/lib/todoro.rb CHANGED
@@ -3,4 +3,11 @@ require "todoro/engine"
3
3
  require "todoro/taskable"
4
4
 
5
5
  module Todoro
6
+ def self.taskable_models
7
+ Rails.application.eager_load!
8
+
9
+ ActiveRecord::Base.descendants.select do |model|
10
+ model.included_modules.include?(Todoro::Taskable)
11
+ end.map(&:name)
12
+ end
6
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: todoro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Despo Pentara
8
8
  - Evangelos Giataganas
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-15 00:00:00.000000000 Z
11
+ date: 2025-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -37,6 +37,7 @@ files:
37
37
  - README.md
38
38
  - Rakefile
39
39
  - app/assets/stylesheets/todoro/application.css
40
+ - app/constraints/taskable_models_constraint.rb
40
41
  - app/controllers/todoro/application_controller.rb
41
42
  - app/controllers/todoro/task_lists_controller.rb
42
43
  - app/controllers/todoro/tasks_controller.rb
@@ -45,7 +46,6 @@ files:
45
46
  - app/helpers/todoro/tasks_helper.rb
46
47
  - app/jobs/todoro/application_job.rb
47
48
  - app/mailers/todoro/application_mailer.rb
48
- - app/models/concerns/taskable.rb
49
49
  - app/models/todoro/application_record.rb
50
50
  - app/models/todoro/reminder.rb
51
51
  - app/models/todoro/task.rb
@@ -1,21 +0,0 @@
1
- module Todoro
2
- module Taskable
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- has_many :task_lists, as: :taskable, class_name: "Todoro::TaskList", dependent: :destroy
7
- end
8
-
9
- def tasks
10
- Todoro::Task.joins(:task_list).where(task_lists: { taskable_id: id, taskable_type: self.class.name })
11
- end
12
-
13
- def create_task_list(name)
14
- task_lists.create(name: name)
15
- end
16
-
17
- def add_task_to_list(task_list, title, description, expiry_date = nil)
18
- task_list.tasks.create(title: title, description: description, expiry_date: expiry_date, status: "pending")
19
- end
20
- end
21
- end