recycle_bin 1.0.0 → 1.1.1

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.
data/docs/logo.svg ADDED
@@ -0,0 +1,71 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
2
+ <defs>
3
+ <!-- Gradient for the main container -->
4
+ <linearGradient id="containerGradient" x1="0%" y1="0%" x2="100%" y2="100%">
5
+ <stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
6
+ <stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
7
+ </linearGradient>
8
+
9
+ <!-- Gradient for the lid -->
10
+ <linearGradient id="lidGradient" x1="0%" y1="0%" x2="100%" y2="100%">
11
+ <stop offset="0%" style="stop-color:#8b9cf7;stop-opacity:1" />
12
+ <stop offset="100%" style="stop-color:#667eea;stop-opacity:1" />
13
+ </linearGradient>
14
+
15
+ <!-- Shadow filter -->
16
+ <filter id="dropShadow" x="-50%" y="-50%" width="200%" height="200%">
17
+ <feDropShadow dx="3" dy="6" stdDeviation="4" flood-color="rgba(0,0,0,0.3)"/>
18
+ </filter>
19
+
20
+ <!-- Glow effect -->
21
+ <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
22
+ <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
23
+ <feMerge>
24
+ <feMergeNode in="coloredBlur"/>
25
+ <feMergeNode in="SourceGraphic"/>
26
+ </feMerge>
27
+ </filter>
28
+ </defs>
29
+
30
+ <!-- Background circle -->
31
+ <circle cx="100" cy="100" r="95" fill="url(#containerGradient)" filter="url(#dropShadow)" opacity="0.1"/>
32
+
33
+ <!-- Main trash bin body -->
34
+ <rect x="60" y="80" width="80" height="90" rx="8" ry="8" fill="url(#containerGradient)" filter="url(#dropShadow)"/>
35
+
36
+ <!-- Trash bin lid -->
37
+ <rect x="50" y="70" width="100" height="12" rx="6" ry="6" fill="url(#lidGradient)" filter="url(#dropShadow)"/>
38
+
39
+ <!-- Lid handle -->
40
+ <rect x="90" y="55" width="20" height="8" rx="4" ry="4" fill="url(#lidGradient)"/>
41
+ <rect x="95" y="63" width="10" height="12" fill="url(#lidGradient)"/>
42
+
43
+ <!-- Vertical lines on trash bin -->
44
+ <line x1="80" y1="90" x2="80" y2="155" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-linecap="round"/>
45
+ <line x1="100" y1="90" x2="100" y2="155" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-linecap="round"/>
46
+ <line x1="120" y1="90" x2="120" y2="155" stroke="rgba(255,255,255,0.3)" stroke-width="2" stroke-linecap="round"/>
47
+
48
+ <!-- Restore arrow (curved arrow indicating recovery) -->
49
+ <g transform="translate(130, 40)" filter="url(#glow)">
50
+ <path d="M 0 15 Q 10 5 20 15 Q 15 10 15 20" stroke="#28a745" stroke-width="3" fill="none" stroke-linecap="round"/>
51
+ <polygon points="12,18 18,18 15,24" fill="#28a745"/>
52
+ </g>
53
+
54
+ <!-- Floating particles (representing restored items) -->
55
+ <circle cx="45" cy="50" r="3" fill="#28a745" opacity="0.7">
56
+ <animate attributeName="cy" values="50;45;50" dur="2s" repeatCount="indefinite"/>
57
+ </circle>
58
+ <circle cx="155" cy="60" r="2" fill="#ffc107" opacity="0.8">
59
+ <animate attributeName="cy" values="60;55;60" dur="1.5s" repeatCount="indefinite"/>
60
+ <animate attributeName="opacity" values="0.8;0.4;0.8" dur="1.5s" repeatCount="indefinite"/>
61
+ </circle>
62
+ <circle cx="40" cy="120" r="2.5" fill="#17a2b8" opacity="0.6">
63
+ <animate attributeName="cy" values="120;115;120" dur="2.5s" repeatCount="indefinite"/>
64
+ </circle>
65
+
66
+ <!-- Subtle highlight on the bin -->
67
+ <rect x="62" y="82" width="15" height="60" rx="4" ry="4" fill="rgba(255,255,255,0.2)"/>
68
+
69
+ <!-- Brand text (small) -->
70
+ <text x="100" y="190" text-anchor="middle" font-family="Inter, sans-serif" font-size="14" font-weight="600" fill="url(#containerGradient)">RecycleBin</text>
71
+ </svg>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RecycleBin
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.1'
5
5
  end
data/lib/recycle_bin.rb CHANGED
@@ -26,13 +26,15 @@ module RecycleBin
26
26
  configuration || (self.configuration = Configuration.new)
27
27
  end
28
28
 
29
- # Simple stats for V1
29
+ # Improved stats for better performance
30
30
  def self.stats
31
31
  return {} unless defined?(Rails) && Rails.application
32
32
 
33
33
  {
34
34
  deleted_items: count_deleted_items,
35
- models_with_soft_delete: models_with_soft_delete
35
+ models_with_soft_delete: models_with_soft_delete,
36
+ deleted_today: count_deleted_items_today,
37
+ deleted_this_week: count_deleted_items_this_week
36
38
  }
37
39
  end
38
40
 
@@ -47,6 +49,28 @@ module RecycleBin
47
49
  0
48
50
  end
49
51
 
52
+ def self.count_deleted_items_today
53
+ total = 0
54
+ models_with_soft_delete.each do |model_name|
55
+ total += count_deleted_items_for_model_since(model_name, 1.day.ago)
56
+ end
57
+ total
58
+ rescue StandardError => e
59
+ log_debug_message("Error counting today's deleted items: #{e.message}")
60
+ 0
61
+ end
62
+
63
+ def self.count_deleted_items_this_week
64
+ total = 0
65
+ models_with_soft_delete.each do |model_name|
66
+ total += count_deleted_items_for_model_since(model_name, 1.week.ago)
67
+ end
68
+ total
69
+ rescue StandardError => e
70
+ log_debug_message("Error counting this week's deleted items: #{e.message}")
71
+ 0
72
+ end
73
+
50
74
  def self.count_deleted_items_for_model(model_name)
51
75
  model = model_name.constantize
52
76
  model.respond_to?(:deleted) ? model.deleted.count : 0
@@ -55,6 +79,18 @@ module RecycleBin
55
79
  0
56
80
  end
57
81
 
82
+ def self.count_deleted_items_for_model_since(model_name, since_time)
83
+ model = model_name.constantize
84
+ if model.respond_to?(:deleted)
85
+ model.deleted.where('deleted_at >= ?', since_time).count
86
+ else
87
+ 0
88
+ end
89
+ rescue StandardError => e
90
+ log_debug_message("Error counting deleted items for #{model_name} since #{since_time}: #{e.message}")
91
+ 0
92
+ end
93
+
58
94
  def self.models_with_soft_delete
59
95
  return [] unless rails_application_available?
60
96
 
@@ -97,14 +133,44 @@ module RecycleBin
97
133
  Rails.logger.debug(message) if defined?(Rails) && Rails.logger
98
134
  end
99
135
 
136
+ # Get all deleted items across all models (for advanced queries)
137
+ def self.all_deleted_items(limit: nil, offset: nil, order_by: :deleted_at, order_direction: :desc)
138
+ all_items = []
139
+
140
+ models_with_soft_delete.each do |model_name|
141
+ model = model_name.constantize
142
+ if model.respond_to?(:deleted)
143
+ items = model.deleted.to_a
144
+ all_items.concat(items)
145
+ end
146
+ rescue StandardError => e
147
+ log_debug_message("Error loading deleted items for #{model_name}: #{e.message}")
148
+ end
149
+
150
+ # Sort items
151
+ sorted_items = all_items.sort_by { |item| item.send(order_by) }
152
+ sorted_items.reverse! if order_direction == :desc
153
+
154
+ # Apply offset and limit
155
+ sorted_items = sorted_items.drop(offset) if offset&.positive?
156
+
157
+ sorted_items = sorted_items.first(limit) if limit&.positive?
158
+
159
+ sorted_items
160
+ rescue StandardError => e
161
+ log_debug_message("Error getting all deleted items: #{e.message}")
162
+ []
163
+ end
164
+
100
165
  # Configuration class for RecycleBin
101
166
  class Configuration
102
167
  attr_accessor :enable_web_interface, :items_per_page, :ui_theme,
103
- :auto_cleanup_after, :current_user_method
168
+ :auto_cleanup_after, :current_user_method, :max_items_per_page
104
169
 
105
170
  def initialize
106
171
  @enable_web_interface = true
107
172
  @items_per_page = 25
173
+ @max_items_per_page = 1000
108
174
  @ui_theme = 'default'
109
175
  @auto_cleanup_after = nil
110
176
  @current_user_method = :current_user
data/recycle_bin.gemspec CHANGED
@@ -29,12 +29,12 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ['lib']
30
30
 
31
31
  # Runtime dependencies - Use pessimistic version constraints
32
- spec.add_runtime_dependency 'rails', '>= 6.0', '< 9.0'
32
+ spec.add_dependency 'rails', '>= 6.0', '< 9.0'
33
33
 
34
- # Development dependencies with proper version constraints
34
+ # Development dependencies - FIXED to match Gemfile
35
35
  spec.add_development_dependency 'factory_bot_rails', '~> 6.2'
36
- spec.add_development_dependency 'rspec-rails', '~> 6.0'
37
- spec.add_development_dependency 'sqlite3', '~> 2.1'
36
+ spec.add_development_dependency 'rspec-rails', '>= 6.0' # Changed from '~> 6.0'
37
+ spec.add_development_dependency 'sqlite3', '~> 2.0' # Changed from '~> 2.1'
38
38
 
39
39
  # Gem metadata with distinct URIs
40
40
  spec.metadata['rubygems_mfa_required'] = 'true'
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recycle_bin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rishi Somani
8
8
  - Shobhit Jain
9
9
  - Raghav Agrawal
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2025-05-25 00:00:00.000000000 Z
12
+ date: 2025-05-26 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rails
@@ -50,14 +49,14 @@ dependencies:
50
49
  name: rspec-rails
51
50
  requirement: !ruby/object:Gem::Requirement
52
51
  requirements:
53
- - - "~>"
52
+ - - ">="
54
53
  - !ruby/object:Gem::Version
55
54
  version: '6.0'
56
55
  type: :development
57
56
  prerelease: false
58
57
  version_requirements: !ruby/object:Gem::Requirement
59
58
  requirements:
60
- - - "~>"
59
+ - - ">="
61
60
  - !ruby/object:Gem::Version
62
61
  version: '6.0'
63
62
  - !ruby/object:Gem::Dependency
@@ -66,14 +65,14 @@ dependencies:
66
65
  requirements:
67
66
  - - "~>"
68
67
  - !ruby/object:Gem::Version
69
- version: '2.1'
68
+ version: '2.0'
70
69
  type: :development
71
70
  prerelease: false
72
71
  version_requirements: !ruby/object:Gem::Requirement
73
72
  requirements:
74
73
  - - "~>"
75
74
  - !ruby/object:Gem::Version
76
- version: '2.1'
75
+ version: '2.0'
77
76
  description: RecycleBin provides soft delete functionality with a user-friendly trash/recycle
78
77
  bin interface for any Rails application. Easily restore deleted records with a simple
79
78
  web interface.
@@ -93,13 +92,20 @@ files:
93
92
  - app/controllers/recycle_bin/application_controller.rb
94
93
  - app/controllers/recycle_bin/trash_controller.rb
95
94
  - app/helpers/recycle_bin/application_helper.rb
96
- - app/views/layouts/recycle_bin/application.html.erb
97
- - app/views/layouts/recycle_bin/recycle_bin/application.html.erb
98
- - app/views/layouts/recycle_bin/recycle_bin/trash/index.html.erb
99
- - app/views/layouts/recycle_bin/recycle_bin/trash/show.html.erb
95
+ - app/views/recycle_bin/layouts/recycle_bin.html.erb
96
+ - app/views/recycle_bin/shared/_error_messages.html.erb
97
+ - app/views/recycle_bin/shared/_flash_messages.html.erb
98
+ - app/views/recycle_bin/trash/_action_history.html.erb
99
+ - app/views/recycle_bin/trash/_associations.html.erb
100
+ - app/views/recycle_bin/trash/_filters.html.erb
101
+ - app/views/recycle_bin/trash/_item.html.erb
102
+ - app/views/recycle_bin/trash/_pagination.html.erb
103
+ - app/views/recycle_bin/trash/_stats.html.erb
100
104
  - app/views/recycle_bin/trash/index.html.erb
101
105
  - app/views/recycle_bin/trash/show.html.erb
102
106
  - config/routes.rb
107
+ - docs/index.html
108
+ - docs/logo.svg
103
109
  - lib/generators/recycle_bin/add_deleted_at/add_deleted_at_generator.rb
104
110
  - lib/generators/recycle_bin/add_deleted_at/templates/add_deleted_at_migration.rb.erb
105
111
  - lib/generators/recycle_bin/install/install_generator.rb
@@ -122,7 +128,6 @@ metadata:
122
128
  bug_tracker_uri: https://github.com/R95-del/recycle_bin/issues
123
129
  documentation_uri: https://github.com/R95-del/recycle_bin/blob/main/README.md
124
130
  wiki_uri: https://github.com/R95-del/recycle_bin/wiki
125
- post_install_message:
126
131
  rdoc_options: []
127
132
  require_paths:
128
133
  - lib
@@ -137,8 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
142
  - !ruby/object:Gem::Version
138
143
  version: '0'
139
144
  requirements: []
140
- rubygems_version: 3.5.18
141
- signing_key:
145
+ rubygems_version: 3.6.6
142
146
  specification_version: 4
143
147
  summary: Soft delete and trash management for Ruby on Rails applications
144
148
  test_files: []
@@ -1,266 +0,0 @@
1
-
2
- <!DOCTYPE html>
3
- <html>
4
- <head>
5
- <title>Recycle Bin</title>
6
- <meta name="viewport" content="width=device-width,initial-scale=1">
7
- <%= csrf_meta_tags %>
8
- <%= csp_meta_tag %>
9
-
10
- <style>
11
- /* Simple, clean CSS for V1 MVP */
12
- * {
13
- box-sizing: border-box;
14
- margin: 0;
15
- padding: 0;
16
- }
17
-
18
- body {
19
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
20
- line-height: 1.6;
21
- color: #333;
22
- background-color: #f8f9fa;
23
- }
24
-
25
- .container {
26
- max-width: 1200px;
27
- margin: 0 auto;
28
- padding: 0 20px;
29
- }
30
-
31
- /* Header */
32
- .header {
33
- background: #fff;
34
- border-bottom: 1px solid #dee2e6;
35
- padding: 1rem 0;
36
- margin-bottom: 2rem;
37
- }
38
-
39
- .header h1 {
40
- color: #495057;
41
- font-size: 1.5rem;
42
- display: flex;
43
- align-items: center;
44
- gap: 0.5rem;
45
- }
46
-
47
- .header .trash-icon {
48
- font-size: 1.8rem;
49
- }
50
-
51
- /* Navigation */
52
- .nav {
53
- margin-top: 1rem;
54
- }
55
-
56
- .nav a {
57
- color: #6c757d;
58
- text-decoration: none;
59
- margin-right: 1.5rem;
60
- padding: 0.5rem 0;
61
- border-bottom: 2px solid transparent;
62
- transition: all 0.2s;
63
- }
64
-
65
- .nav a:hover,
66
- .nav a.active {
67
- color: #495057;
68
- border-bottom-color: #007bff;
69
- }
70
-
71
- /* Cards */
72
- .card {
73
- background: #fff;
74
- border-radius: 8px;
75
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
76
- padding: 1.5rem;
77
- margin-bottom: 1rem;
78
- }
79
-
80
- /* Buttons */
81
- .btn {
82
- display: inline-block;
83
- padding: 0.5rem 1rem;
84
- border: none;
85
- border-radius: 4px;
86
- text-decoration: none;
87
- cursor: pointer;
88
- font-size: 0.875rem;
89
- transition: all 0.2s;
90
- }
91
-
92
- .btn-primary {
93
- background: #007bff;
94
- color: white;
95
- }
96
-
97
- .btn-primary:hover {
98
- background: #0056b3;
99
- }
100
-
101
- .btn-success {
102
- background: #28a745;
103
- color: white;
104
- }
105
-
106
- .btn-success:hover {
107
- background: #1e7e34;
108
- }
109
-
110
- .btn-danger {
111
- background: #dc3545;
112
- color: white;
113
- }
114
-
115
- .btn-danger:hover {
116
- background: #c82333;
117
- }
118
-
119
- .btn-sm {
120
- padding: 0.25rem 0.75rem;
121
- font-size: 0.8rem;
122
- }
123
-
124
- /* Tables */
125
- .table {
126
- width: 100%;
127
- border-collapse: collapse;
128
- background: #fff;
129
- border-radius: 8px;
130
- overflow: hidden;
131
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
132
- }
133
-
134
- .table th,
135
- .table td {
136
- padding: 1rem;
137
- text-align: left;
138
- border-bottom: 1px solid #dee2e6;
139
- }
140
-
141
- .table th {
142
- background: #f8f9fa;
143
- font-weight: 600;
144
- color: #495057;
145
- }
146
-
147
- .table tr:hover {
148
- background: #f8f9fa;
149
- }
150
-
151
- /* Stats */
152
- .stats {
153
- display: grid;
154
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
155
- gap: 1rem;
156
- margin-bottom: 2rem;
157
- }
158
-
159
- .stat-card {
160
- background: #fff;
161
- padding: 1.5rem;
162
- border-radius: 8px;
163
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
164
- text-align: center;
165
- }
166
-
167
- .stat-number {
168
- font-size: 2rem;
169
- font-weight: bold;
170
- color: #007bff;
171
- }
172
-
173
- .stat-label {
174
- color: #6c757d;
175
- font-size: 0.875rem;
176
- margin-top: 0.5rem;
177
- }
178
-
179
- /* Empty state */
180
- .empty-state {
181
- text-align: center;
182
- padding: 4rem 2rem;
183
- color: #6c757d;
184
- }
185
-
186
- .empty-state-icon {
187
- font-size: 4rem;
188
- margin-bottom: 1rem;
189
- }
190
-
191
- /* Responsive */
192
- @media (max-width: 768px) {
193
- .container {
194
- padding: 0 15px;
195
- }
196
-
197
- .table {
198
- font-size: 0.875rem;
199
- }
200
-
201
- .table th,
202
- .table td {
203
- padding: 0.75rem 0.5rem;
204
- }
205
-
206
- .btn {
207
- font-size: 0.8rem;
208
- padding: 0.4rem 0.8rem;
209
- }
210
- }
211
-
212
- /* Alerts */
213
- .alert {
214
- padding: 0.75rem 1rem;
215
- margin-bottom: 1rem;
216
- border-radius: 4px;
217
- }
218
-
219
- .alert-success {
220
- background: #d4edda;
221
- color: #155724;
222
- border: 1px solid #c3e6cb;
223
- }
224
-
225
- .alert-danger {
226
- background: #f8d7da;
227
- color: #721c24;
228
- border: 1px solid #f5c6cb;
229
- }
230
-
231
- .alert-info {
232
- background: #d1ecf1;
233
- color: #0c5460;
234
- border: 1px solid #bee5eb;
235
- }
236
- </style>
237
- </head>
238
-
239
- <body>
240
- <div class="header">
241
- <div class="container">
242
- <h1>
243
- <span class="trash-icon">🗑️</span>
244
- Recycle Bin
245
- </h1>
246
- <nav class="nav">
247
- <%= link_to "All Items", recycle_bin.trash_index_path,
248
- class: ("active" if current_page?(recycle_bin.trash_index_path)) %>
249
- <%= link_to "Statistics", "#", class: "disabled" %>
250
- </nav>
251
- </div>
252
- </div>
253
-
254
- <div class="container">
255
- <% if notice %>
256
- <div class="alert alert-success"><%= notice %></div>
257
- <% end %>
258
-
259
- <% if alert %>
260
- <div class="alert alert-danger"><%= alert %></div>
261
- <% end %>
262
-
263
- <%= yield %>
264
- </div>
265
- </body>
266
- </html>