recycle_bin 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ecca5bc61bc12faa00187f0a37d0f73eebd8658b1192e8b45d61e9f839cdbc48
4
+ data.tar.gz: cf86c31ba03eeb84276afb3d213a436cb7a4d0d989037d6a2f22f6fab11bfb1c
5
+ SHA512:
6
+ metadata.gz: dcea5e5846a8ae0377132fa8de1e6fb39b9e3566999ada4c396697a7ddc40eaea27a0569ede0c2d367e56849e334bf2ea1b4a1ef0c5ad68d83d49d9212c62883
7
+ data.tar.gz: 40bf89241c9f86b8d2923c95f6bf03140dbc78a0c7fceccec451a433c3552e9021b15e293ec77e76f3f379ccbe901b1f12d05f2c347e1cc5a2bf4f37e2fcefe2
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0] - 2025-05-25
4
+
5
+ ### Added
6
+ - Initial release of RecycleBin gem
7
+ - Soft delete functionality for ActiveRecord models
8
+ - Web interface for managing trashed items
9
+ - Restore functionality for deleted records
10
+ - Bulk operations for multiple items
11
+ - JSON API support for programmatic access
12
+
13
+ ### Contributors
14
+ - Rishi Somani
15
+ - Raghav Agrawal
16
+ - Shobhit Jain
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in recycle_bin.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 13.0'
9
+ gem 'rspec', '~> 3.0'
10
+
11
+ # Development dependencies
12
+ group :development, :test do
13
+ gem 'factory_bot_rails', '~> 6.2'
14
+ gem 'rspec-rails', '>= 6.0'
15
+ gem 'sqlite3', '~> 2.0'
16
+ end
17
+
18
+ group :development do
19
+ gem 'rubocop', '~> 1.50', require: false
20
+ gem 'rubocop-rails', '~> 2.19', require: false
21
+ gem 'rubocop-rspec', '~> 2.20', require: false
22
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Rishi Somani, Raghav Agrawal, Shobhit Jain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,425 @@
1
+ # RecycleBin 🗑️
2
+
3
+ A simple and elegant soft delete solution for Rails applications with a beautiful web interface to manage your deleted records.
4
+
5
+ **RecycleBin** provides a "trash can" or "recycle bin" functionality where deleted records are marked as deleted instead of being permanently removed from your database. You can easily restore them or permanently delete them through a clean web interface.
6
+
7
+ ## Features ✨
8
+
9
+ - **Soft Delete**: Records are marked as deleted instead of being permanently removed
10
+ - **Web Interface**: Beautiful, responsive dashboard to view and manage deleted items
11
+ - **Easy Integration**: Simple module inclusion in your models
12
+ - **Bulk Operations**: Restore or permanently delete multiple items at once
13
+ - **Model Filtering**: Filter deleted items by model type and time
14
+ - **Rails Generators**: Automated setup with generators
15
+ - **Configurable**: Flexible configuration options
16
+ - **Statistics Dashboard**: Overview of your deleted items
17
+
18
+ ## Installation 📦
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'recycle_bin'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ ```bash
29
+ $ bundle install
30
+ ```
31
+
32
+ Or install it yourself as:
33
+
34
+ ```bash
35
+ $ gem install recycle_bin
36
+ ```
37
+
38
+ ## Quick Setup 🚀
39
+
40
+ ### 1. Run the install generator
41
+
42
+ ```bash
43
+ $ rails generate recycle_bin:install
44
+ ```
45
+
46
+ This will:
47
+ - Create a configuration file at `config/initializers/recycle_bin.rb`
48
+ - Add the mount point to your routes
49
+ - Display setup instructions
50
+
51
+ ### 2. Add deleted_at column to your models
52
+
53
+ For each model you want to soft delete, run:
54
+
55
+ ```bash
56
+ $ rails generate recycle_bin:add_deleted_at ModelName
57
+ ```
58
+
59
+ For example:
60
+ ```bash
61
+ $ rails generate recycle_bin:add_deleted_at User
62
+ $ rails generate recycle_bin:add_deleted_at Post
63
+ $ rails generate recycle_bin:add_deleted_at Comment
64
+ ```
65
+
66
+ This generates a migration to add the `deleted_at` column and index.
67
+
68
+ ### 3. Run the migrations
69
+
70
+ ```bash
71
+ $ rails db:migrate
72
+ ```
73
+
74
+ ### 4. Include the module in your models
75
+
76
+ Add `RecycleBin::SoftDeletable` to any model you want to soft delete:
77
+
78
+ ```ruby
79
+ class User < ApplicationRecord
80
+ include RecycleBin::SoftDeletable
81
+ end
82
+
83
+ class Post < ApplicationRecord
84
+ include RecycleBin::SoftDeletable
85
+ end
86
+
87
+ class Comment < ApplicationRecord
88
+ include RecycleBin::SoftDeletable
89
+ end
90
+ ```
91
+
92
+ ### 5. Visit the web interface
93
+
94
+ Navigate to `/recycle_bin` in your Rails application to see the web interface!
95
+
96
+ ## Usage 💡
97
+
98
+ ### Basic Operations
99
+
100
+ Once you've included the `RecycleBin::SoftDeletable` module in your models:
101
+
102
+ ```ruby
103
+ # Create a record
104
+ user = User.create(name: "John Doe", email: "john@example.com")
105
+
106
+ # Soft delete (goes to trash)
107
+ user.destroy
108
+ # or
109
+ user.soft_delete
110
+
111
+ # Check if deleted
112
+ user.deleted? # => true
113
+
114
+ # Restore from trash
115
+ user.restore
116
+
117
+ # Permanently delete (careful!)
118
+ user.destroy!
119
+ ```
120
+
121
+ ### Querying Records
122
+
123
+ The module provides several useful scopes:
124
+
125
+ ```ruby
126
+ # Get all active (non-deleted) records (default scope)
127
+ User.all
128
+
129
+ # Get only deleted records
130
+ User.deleted
131
+
132
+ # Get all records including deleted ones
133
+ User.with_deleted
134
+
135
+ # Get only deleted records (alias)
136
+ User.only_deleted
137
+
138
+ # Get non-deleted records explicitly
139
+ User.not_deleted
140
+
141
+ # Restore a deleted record by ID
142
+ User.restore(123)
143
+ ```
144
+
145
+ ### Web Interface Features
146
+
147
+ The web interface (`/recycle_bin`) provides:
148
+
149
+ - **Dashboard Overview**: Statistics about deleted items
150
+ - **Item Listing**: View all deleted records with details
151
+ - **Filtering**: Filter by model type (User, Post, Comment, etc.)
152
+ - **Time Filters**: View items deleted today, this week, or this month
153
+ - **Individual Actions**: Restore or permanently delete single items
154
+ - **Bulk Actions**: Select multiple items to restore or delete
155
+ - **Item Details**: Click on any item to see full details and history
156
+
157
+ ## Configuration ⚙️
158
+
159
+ Configure RecycleBin in `config/initializers/recycle_bin.rb`:
160
+
161
+ ```ruby
162
+ RecycleBin.configure do |config|
163
+ # Enable/disable web interface (default: true)
164
+ config.enable_web_interface = true
165
+
166
+ # Items per page in web interface (default: 25)
167
+ config.items_per_page = 50
168
+
169
+ # Auto-cleanup items after specified time (default: nil - disabled)
170
+ config.auto_cleanup_after = 30.days
171
+
172
+ # Method to get current user for audit trail (default: :current_user)
173
+ config.current_user_method = :current_user
174
+
175
+ # Authorization callback - restrict access to admins only
176
+ config.authorize_with do |controller|
177
+ # Example: Only allow admins
178
+ controller.current_user&.admin?
179
+ end
180
+ end
181
+ ```
182
+
183
+ ### Authorization
184
+
185
+ To restrict access to the web interface, use the `authorize_with` configuration:
186
+
187
+ ```ruby
188
+ RecycleBin.configure do |config|
189
+ config.authorize_with do |controller|
190
+ # Only allow admins
191
+ controller.current_user&.admin?
192
+ end
193
+ end
194
+ ```
195
+
196
+ If authorization fails, users will be redirected with an "Access denied" message.
197
+
198
+ ## Advanced Usage 🔧
199
+
200
+ ### Custom Title Display
201
+
202
+ By default, RecycleBin tries to use `title`, `name`, or `email` fields for display. You can customize this:
203
+
204
+ ```ruby
205
+ class User < ApplicationRecord
206
+ include RecycleBin::SoftDeletable
207
+
208
+ def recyclable_title
209
+ "#{first_name} #{last_name} (#{email})"
210
+ end
211
+ end
212
+ ```
213
+
214
+ ### Associations and Dependencies
215
+
216
+ RecycleBin works with Rails associations. The web interface will show related items:
217
+
218
+ ```ruby
219
+ class User < ApplicationRecord
220
+ include RecycleBin::SoftDeletable
221
+ has_many :posts, dependent: :destroy
222
+ end
223
+
224
+ class Post < ApplicationRecord
225
+ include RecycleBin::SoftDeletable
226
+ belongs_to :user
227
+ has_many :comments, dependent: :destroy
228
+ end
229
+ ```
230
+
231
+ ### Statistics and Reporting
232
+
233
+ Get statistics about your deleted items:
234
+
235
+ ```ruby
236
+ # Get overall statistics
237
+ RecycleBin.stats
238
+ # => { deleted_items: 45, models_with_soft_delete: ["User", "Post", "Comment"] }
239
+
240
+ # Count deleted items across all models
241
+ RecycleBin.count_deleted_items # => 45
242
+
243
+ # Get models that have soft delete enabled
244
+ RecycleBin.models_with_soft_delete # => ["User", "Post", "Comment"]
245
+ ```
246
+
247
+ ## API Reference 📚
248
+
249
+ ### Instance Methods
250
+
251
+ - `soft_delete` - Mark record as deleted
252
+ - `restore` - Restore deleted record
253
+ - `deleted?` - Check if record is deleted
254
+ - `destroy` - Soft delete (overrides Rails default)
255
+ - `destroy!` - Permanently delete from database
256
+ - `recyclable_title` - Display title for web interface
257
+
258
+ ### Class Methods
259
+
260
+ - `.deleted` - Scope for deleted records only
261
+ - `.not_deleted` - Scope for active records only
262
+ - `.with_deleted` - Scope for all records including deleted
263
+ - `.only_deleted` - Alias for `.deleted`
264
+ - `.restore(id)` - Restore a record by ID
265
+ - `.deleted_records` - Get all deleted records
266
+
267
+ ### Configuration Options
268
+
269
+ - `enable_web_interface` - Enable/disable web UI (default: true)
270
+ - `items_per_page` - Pagination limit (default: 25)
271
+ - `auto_cleanup_after` - Auto-delete after time period (default: nil)
272
+ - `current_user_method` - Method to get current user (default: :current_user)
273
+ - `authorize_with` - Authorization callback block
274
+
275
+ ## Generators 🛠️
276
+
277
+ ### Install Generator
278
+
279
+ ```bash
280
+ $ rails generate recycle_bin:install
281
+ ```
282
+
283
+ Sets up RecycleBin in your Rails application.
284
+
285
+ ### Add DeletedAt Generator
286
+
287
+ ```bash
288
+ $ rails generate recycle_bin:add_deleted_at ModelName
289
+ ```
290
+
291
+ Adds the `deleted_at` column to the specified model.
292
+
293
+ ## Web Interface Routes 🛣️
294
+
295
+ The gem adds these routes to your application:
296
+
297
+ ```
298
+ GET /recycle_bin # Dashboard/index
299
+ GET /recycle_bin/trash # List all deleted items
300
+ GET /recycle_bin/trash/:model_type/:id # Show specific item
301
+ PATCH /recycle_bin/trash/:model_type/:id/restore # Restore item
302
+ DELETE /recycle_bin/trash/:model_type/:id # Permanently delete
303
+ PATCH /recycle_bin/trash/bulk_restore # Bulk restore
304
+ DELETE /recycle_bin/trash/bulk_destroy # Bulk delete
305
+ ```
306
+
307
+ ## Requirements 📋
308
+
309
+ - **Ruby**: >= 2.7.0
310
+ - **Rails**: >= 6.0
311
+ - **Database**: Any database supported by Rails (PostgreSQL, MySQL, SQLite, etc.)
312
+
313
+ ## Examples 💼
314
+
315
+ ### E-commerce Store
316
+
317
+ ```ruby
318
+ class Product < ApplicationRecord
319
+ include RecycleBin::SoftDeletable
320
+
321
+ def recyclable_title
322
+ "#{name} - #{sku}"
323
+ end
324
+ end
325
+
326
+ # Soft delete a product
327
+ product = Product.find(1)
328
+ product.destroy # Goes to trash, can be restored
329
+
330
+ # View deleted products in admin panel at /recycle_bin
331
+ ```
332
+
333
+ ### Blog System
334
+
335
+ ```ruby
336
+ class Post < ApplicationRecord
337
+ include RecycleBin::SoftDeletable
338
+ belongs_to :author, class_name: 'User'
339
+
340
+ def recyclable_title
341
+ title.truncate(50)
342
+ end
343
+ end
344
+
345
+ class Comment < ApplicationRecord
346
+ include RecycleBin::SoftDeletable
347
+ belongs_to :post
348
+ belongs_to :user
349
+
350
+ def recyclable_title
351
+ "Comment by #{user.name}: #{body.truncate(30)}"
352
+ end
353
+ end
354
+ ```
355
+
356
+ ### User Management
357
+
358
+ ```ruby
359
+ class User < ApplicationRecord
360
+ include RecycleBin::SoftDeletable
361
+
362
+ def recyclable_title
363
+ "#{name} (#{email})"
364
+ end
365
+ end
366
+
367
+ # Admin can restore accidentally deleted users
368
+ User.deleted.each do |user|
369
+ puts "Deleted user: #{user.recyclable_title}"
370
+ end
371
+ ```
372
+
373
+ ## Troubleshooting 🔍
374
+
375
+ ### Common Issues
376
+
377
+ **1. "uninitialized constant RecycleBin" error**
378
+ - Make sure you've added the gem to your Gemfile and run `bundle install`
379
+ - Restart your Rails server
380
+
381
+ **2. Routes not working**
382
+ - Ensure you've run `rails generate recycle_bin:install`
383
+ - Check that `mount RecycleBin::Engine => '/recycle_bin'` is in your `config/routes.rb`
384
+
385
+ **3. Records not appearing in trash**
386
+ - Verify you've included `RecycleBin::SoftDeletable` in your model
387
+ - Ensure the `deleted_at` column exists (run the migration)
388
+ - Check that you're calling `.destroy` not `.delete`
389
+
390
+ **4. Web interface shows "Access denied"**
391
+ - Check your authorization configuration in the initializer
392
+ - Ensure the current user meets your authorization requirements
393
+
394
+ ### Debugging
395
+
396
+ Enable debug logging to see what's happening:
397
+
398
+ ```ruby
399
+ # In development.rb or console
400
+ Rails.logger.level = :debug
401
+ ```
402
+
403
+ ## Contributing 🤝
404
+
405
+ Bug reports and pull requests are welcome on GitHub at https://github.com/R95-del/recycle_bin.
406
+
407
+ ## License 📄
408
+
409
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
410
+
411
+ ## Changelog 📝
412
+
413
+ ### Version 0.1.0 (2025-05-24)
414
+ - Initial release
415
+ - Basic soft delete functionality
416
+ - Web interface for managing deleted items
417
+ - Rails generators for easy setup
418
+ - Bulk operations support
419
+ - Model filtering and statistics
420
+
421
+ ---
422
+
423
+ **Made with ❤️ for the Rails community**
424
+
425
+ *Need help? Open an issue on [GitHub](https://github.com/R95-del/recycle_bin) or check out the web interface at `/recycle_bin` in your Rails app.*
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RecycleBin
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+
7
+ before_action :authorize_access
8
+
9
+ layout 'recycle_bin/application'
10
+
11
+ protected
12
+
13
+ # Helper method to handle RecycleBin-specific errors gracefully
14
+ def handle_recycle_bin_error(error)
15
+ log_error(error)
16
+ error_message = user_friendly_error_message(error)
17
+ respond_with_error(error_message)
18
+ end
19
+
20
+ # Helper method to check if the current request is for JSON
21
+ def json_request?
22
+ request.format.json?
23
+ end
24
+
25
+ # Helper method to safely constantize model names
26
+ def safe_constantize_model(model_name)
27
+ return nil unless model_name.present?
28
+
29
+ model_class = model_name.constantize
30
+ model_class < ActiveRecord::Base ? model_class : nil
31
+ rescue NameError
32
+ nil
33
+ end
34
+
35
+ # Helper method to format timestamps for consistent display
36
+ def format_timestamp(timestamp)
37
+ return 'N/A' unless timestamp
38
+
39
+ timestamp.strftime('%B %d, %Y at %I:%M %p')
40
+ end
41
+
42
+ # Helper to check current page for navigation
43
+ def current_page?(path)
44
+ request.path == path
45
+ end
46
+ helper_method :current_page?
47
+
48
+ private
49
+
50
+ def authorize_access
51
+ return true unless RecycleBin.config.authorization_method
52
+
53
+ return if instance_eval(&RecycleBin.config.authorization_method)
54
+
55
+ if defined?(main_app)
56
+ redirect_to main_app.root_path, alert: 'Access denied.'
57
+ else
58
+ render plain: 'Access denied.', status: :forbidden
59
+ end
60
+ end
61
+
62
+ def log_error(error)
63
+ return unless Rails.logger
64
+
65
+ Rails.logger.error "RecycleBin Error: #{error.message}"
66
+ Rails.logger.error error.backtrace.join("\n") if error.backtrace
67
+ end
68
+
69
+ def user_friendly_error_message(error)
70
+ case error
71
+ when ActiveRecord::RecordNotFound
72
+ 'The requested item could not be found.'
73
+ when NameError
74
+ 'Invalid model type specified.'
75
+ when NoMethodError
76
+ 'This operation is not supported for this item type.'
77
+ else
78
+ 'An unexpected error occurred. Please try again.'
79
+ end
80
+ end
81
+
82
+ def respond_with_error(message)
83
+ respond_to do |format|
84
+ format.html { redirect_with_flash_error(message) }
85
+ format.json { render_json_error(message) }
86
+ end
87
+ end
88
+
89
+ def redirect_with_flash_error(message)
90
+ flash[:alert] = message
91
+ redirect_to main_app.respond_to?(:recycle_bin_path) ? main_app.recycle_bin_path : root_path
92
+ end
93
+
94
+ def render_json_error(message)
95
+ render json: {
96
+ message: message,
97
+ status: 'error',
98
+ error_type: 'RecycleBinError'
99
+ }, status: :internal_server_error
100
+ end
101
+ end
102
+ end