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.
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module RecycleBin
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+ desc 'Install RecycleBin in your Rails application'
9
+
10
+ def create_initializer
11
+ create_file 'config/initializers/recycle_bin.rb', <<~RUBY
12
+ # RecycleBin Configuration
13
+ RecycleBin.configure do |config|
14
+ # Enable web interface (default: true)
15
+ config.enable_web_interface = true
16
+
17
+ # Items per page in web interface (default: 25)
18
+ # config.items_per_page = 25
19
+
20
+ # Auto-cleanup items after specified time (default: nil - disabled)
21
+ # config.auto_cleanup_after = 30.days
22
+
23
+ # Method to get current user for audit trail#{' '}
24
+ # config.current_user_method = :current_user
25
+
26
+ # Authorization callback - uncomment to restrict access
27
+ # config.authorize_with do |controller|
28
+ # # Example: Only allow admins
29
+ # controller.current_user&.admin?
30
+ # end
31
+ end
32
+ RUBY
33
+ end
34
+
35
+ def add_route
36
+ route "mount RecycleBin::Engine => '/recycle_bin'"
37
+ end
38
+
39
+ def print_installation_instructions
40
+ display_success_header
41
+ display_next_steps
42
+ display_configuration_info
43
+ display_success_footer
44
+ end
45
+
46
+ private
47
+
48
+ def display_success_header
49
+ say "\n#{'=' * 60}", :green
50
+ say '🗑️ RecycleBin installed successfully!', :green
51
+ say '=' * 60, :green
52
+ say ''
53
+ end
54
+
55
+ def display_next_steps
56
+ say 'Next steps:', :yellow
57
+ say '1. Add deleted_at column to your models:'
58
+ say ' rails generate migration AddDeletedAtToUsers deleted_at:datetime:index'
59
+ say ''
60
+ say '2. Include the module in your models:'
61
+ say ' class User < ApplicationRecord'
62
+ say ' include RecycleBin::SoftDeletable'
63
+ say ' end'
64
+ say ''
65
+ say '3. Run migrations:'
66
+ say ' rails db:migrate'
67
+ say ''
68
+ say '4. Visit /recycle_bin to see the web interface'
69
+ say ''
70
+ end
71
+
72
+ def display_configuration_info
73
+ say 'Configuration file created at:'
74
+ say ' config/initializers/recycle_bin.rb'
75
+ end
76
+
77
+ def display_success_footer
78
+ say '=' * 60, :green
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RecycleBin Configuration
4
+ RecycleBin.configure do |config|
5
+ # Enable web interface (default: true)
6
+ config.enable_web_interface = true
7
+
8
+ # Uncomment and customize as needed:
9
+
10
+ # Auto-cleanup items after specified time (default: nil - disabled)
11
+ # config.auto_cleanup_after = 30.days
12
+
13
+ # Method to get current user for audit trail
14
+ # config.current_user_method = :current_user
15
+
16
+ # Items per page in web interface
17
+ # config.items_per_page = 25
18
+
19
+ # Authorization callback
20
+ # config.authorize_with do |controller|
21
+ # # Example: Only allow admins
22
+ # controller.current_user&.admin?
23
+ # end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/engine'
4
+
5
+ module RecycleBin
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace RecycleBin
8
+
9
+ config.generators do |g|
10
+ g.test_framework :rspec
11
+ end
12
+
13
+ # This ensures generators are found
14
+ config.app_generators do |g|
15
+ g.rails = {
16
+ assets: false,
17
+ helper: false
18
+ }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RecycleBin Configuration
4
+ RecycleBin.configure do |config|
5
+ # Enable web interface (default: true)
6
+ config.enable_web_interface = true
7
+
8
+ # Uncomment and customize as needed:
9
+
10
+ # Auto-cleanup items after specified time (default: nil - disabled)
11
+ # config.auto_cleanup_after = 30.days
12
+
13
+ # Method to get current user for audit trail
14
+ # config.current_user_method = :current_user
15
+
16
+ # Items per page in web interface
17
+ # config.items_per_page = 25
18
+
19
+ # Authorization callback
20
+ # config.authorize_with do |controller|
21
+ # # Example: Only allow admins
22
+ # controller.current_user&.admin?
23
+ # end
24
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module RecycleBin
6
+ # SoftDeletable module provides soft delete functionality for ActiveRecord models.
7
+ # Instead of permanently deleting records, it marks them as deleted by setting
8
+ # a deleted_at timestamp, allowing for restoration and trash management.
9
+ module SoftDeletable
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ # Only add ActiveRecord scopes if this is an ActiveRecord model
14
+ if self < ActiveRecord::Base
15
+ # Add a default scope to exclude deleted records
16
+ default_scope { where(deleted_at: nil) }
17
+
18
+ # Scopes for querying deleted/not deleted records
19
+ scope :deleted, -> { unscope(where: :deleted_at).where.not(deleted_at: nil) }
20
+ scope :not_deleted, -> { where(deleted_at: nil) }
21
+ scope :with_deleted, -> { unscope(where: :deleted_at) }
22
+ scope :only_deleted, -> { unscope(where: :deleted_at).where.not(deleted_at: nil) }
23
+ end
24
+ end
25
+
26
+ class_methods do
27
+ # Restore a record by ID (only for ActiveRecord models)
28
+ def restore(id)
29
+ raise NotImplementedError, 'restore method only available for ActiveRecord models' unless self < ActiveRecord::Base
30
+
31
+ with_deleted.find(id).restore
32
+ end
33
+
34
+ # Get all deleted records (only for ActiveRecord models)
35
+ def deleted_records
36
+ raise NotImplementedError, 'deleted_records method only available for ActiveRecord models' unless self < ActiveRecord::Base
37
+
38
+ deleted
39
+ end
40
+
41
+ # Find deleted records safely (bypasses default scope)
42
+ def find_deleted
43
+ unscoped.where.not(deleted_at: nil)
44
+ end
45
+ end
46
+
47
+ # Instance methods
48
+ def soft_delete
49
+ return false if deleted?
50
+
51
+ if respond_to?(:update_column)
52
+ # Use update_column to bypass validations and callbacks
53
+ update_column(:deleted_at, Time.current)
54
+ elsif respond_to?(:deleted_at=)
55
+ # For non-ActiveRecord objects, just set the attribute
56
+ self.deleted_at = Time.current
57
+ save if respond_to?(:save)
58
+ end
59
+ end
60
+
61
+ def restore
62
+ return false unless deleted?
63
+
64
+ if respond_to?(:update_column)
65
+ # Use update_column to bypass validations and callbacks
66
+ update_column(:deleted_at, nil)
67
+ elsif respond_to?(:deleted_at=)
68
+ # For non-ActiveRecord objects, just set the attribute
69
+ self.deleted_at = nil
70
+ save if respond_to?(:save)
71
+ end
72
+ end
73
+
74
+ def deleted?
75
+ respond_to?(:deleted_at) && deleted_at.present?
76
+ end
77
+
78
+ def destroy
79
+ # Override destroy to use soft delete instead
80
+ soft_delete
81
+ end
82
+
83
+ # For permanent deletion (use carefully!) - only for ActiveRecord
84
+ def destroy!
85
+ raise NotImplementedError, 'destroy! method only available for ActiveRecord models' unless respond_to?(:delete)
86
+
87
+ # Use delete to bypass callbacks and directly remove from database
88
+ self.class.unscoped.where(id: id).delete_all
89
+ end
90
+
91
+ # Helper for the trash controller
92
+ def recyclable_title
93
+ # Try common title fields safely
94
+ return try(:title) if respond_to?(:title) && try(:title).present?
95
+ return try(:name) if respond_to?(:name) && try(:name).present?
96
+ return try(:email) if respond_to?(:email) && try(:email).present?
97
+
98
+ "#{self.class.name} ##{id}"
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RecycleBin
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'recycle_bin/version'
4
+ require_relative 'recycle_bin/soft_deletable'
5
+
6
+ # Only require Rails components if Rails is present
7
+ require_relative 'recycle_bin/engine' if defined?(Rails)
8
+
9
+ # RecycleBin provides soft delete functionality for Rails applications.
10
+ # It adds a "recycle bin" feature where deleted records are marked as deleted
11
+ # instead of being permanently removed from the database.
12
+ module RecycleBin
13
+ class Error < StandardError; end
14
+
15
+ class << self
16
+ attr_accessor :configuration
17
+ end
18
+
19
+ def self.configure
20
+ self.configuration ||= Configuration.new
21
+ yield(configuration) if block_given?
22
+ configuration
23
+ end
24
+
25
+ def self.config
26
+ configuration || (self.configuration = Configuration.new)
27
+ end
28
+
29
+ # Simple stats for V1
30
+ def self.stats
31
+ return {} unless defined?(Rails) && Rails.application
32
+
33
+ {
34
+ deleted_items: count_deleted_items,
35
+ models_with_soft_delete: models_with_soft_delete
36
+ }
37
+ end
38
+
39
+ def self.count_deleted_items
40
+ total = 0
41
+ models_with_soft_delete.each do |model_name|
42
+ total += count_deleted_items_for_model(model_name)
43
+ end
44
+ total
45
+ rescue StandardError => e
46
+ log_debug_message("Error counting deleted items: #{e.message}")
47
+ 0
48
+ end
49
+
50
+ def self.count_deleted_items_for_model(model_name)
51
+ model = model_name.constantize
52
+ model.respond_to?(:deleted) ? model.deleted.count : 0
53
+ rescue StandardError => e
54
+ log_debug_message("Error counting deleted items for #{model_name}: #{e.message}")
55
+ 0
56
+ end
57
+
58
+ def self.models_with_soft_delete
59
+ return [] unless rails_application_available?
60
+
61
+ # In test environment, we might not need to eager load
62
+ Rails.application.eager_load! if Rails.application.respond_to?(:eager_load!)
63
+
64
+ find_soft_deletable_models
65
+ end
66
+
67
+ def self.rails_application_available?
68
+ defined?(Rails) && Rails.application
69
+ end
70
+
71
+ def self.find_soft_deletable_models
72
+ return [] unless defined?(ActiveRecord)
73
+
74
+ ActiveRecord::Base.descendants.filter_map do |model|
75
+ model_name_if_soft_deletable(model)
76
+ end.compact
77
+ rescue StandardError => e
78
+ log_debug_message("Error finding soft deletable models: #{e.message}")
79
+ []
80
+ end
81
+
82
+ def self.model_name_if_soft_deletable(model)
83
+ return nil if model.abstract_class? || !model.table_exists?
84
+
85
+ model.name if model_has_soft_delete_capability?(model)
86
+ rescue StandardError => e
87
+ log_debug_message("Skipping model #{model.name}: #{e.message}")
88
+ nil
89
+ end
90
+
91
+ def self.model_has_soft_delete_capability?(model)
92
+ model.column_names.include?('deleted_at') && model.respond_to?(:deleted)
93
+ end
94
+
95
+ # Extract debug logging to reduce duplication
96
+ def self.log_debug_message(message)
97
+ Rails.logger.debug(message) if defined?(Rails) && Rails.logger
98
+ end
99
+
100
+ # Configuration class for RecycleBin
101
+ class Configuration
102
+ attr_accessor :enable_web_interface, :items_per_page, :ui_theme,
103
+ :auto_cleanup_after, :current_user_method
104
+
105
+ def initialize
106
+ @enable_web_interface = true
107
+ @items_per_page = 25
108
+ @ui_theme = 'default'
109
+ @auto_cleanup_after = nil
110
+ @current_user_method = :current_user
111
+ end
112
+
113
+ def authorize_with(&block)
114
+ @authorization_method = block
115
+ end
116
+
117
+ def authorization_method
118
+ @authorization_method || proc { true }
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/recycle_bin/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'recycle_bin'
7
+ spec.version = RecycleBin::VERSION
8
+ spec.authors = ['Rishi Somani', 'Shobhit Jain', 'Raghav Agrawal']
9
+ spec.email = ['somani.rishi81@gmail.com', 'shobhjain09@gmail.com', 'raghavagrawal019@gmail.com']
10
+
11
+ spec.summary = 'Soft delete and trash management for Ruby on Rails applications'
12
+ spec.description = 'RecycleBin provides soft delete functionality with a user-friendly trash/recycle bin interface for any Rails application. Easily restore deleted records with a simple web interface.'
13
+ spec.homepage = 'https://github.com/R95-del/recycle_bin'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 2.7.0'
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ spec.files = Dir.chdir(__dir__) do
19
+ `git ls-files -z`.split("\x0").reject do |f|
20
+ # Exclude the gemspec file itself and development files
21
+ (File.expand_path(f) == __FILE__) ||
22
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github]) ||
23
+ f.match?(/\A\./) || # Exclude dotfiles
24
+ f.end_with?('.gem') || # Exclude any .gem files
25
+ f.include?('Gemfile.lock') # Exclude Gemfile.lock
26
+ end
27
+ end
28
+
29
+ spec.require_paths = ['lib']
30
+
31
+ # Runtime dependencies - Use pessimistic version constraints
32
+ spec.add_runtime_dependency 'rails', '>= 6.0', '< 9.0'
33
+
34
+ # Development dependencies with proper version constraints
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'
38
+
39
+ # Gem metadata with distinct URIs
40
+ spec.metadata['rubygems_mfa_required'] = 'true'
41
+ spec.metadata['homepage_uri'] = spec.homepage
42
+ spec.metadata['source_code_uri'] = 'https://github.com/R95-del/recycle_bin'
43
+ spec.metadata['changelog_uri'] = 'https://github.com/R95-del/recycle_bin/blob/main/CHANGELOG.md'
44
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/R95-del/recycle_bin/issues'
45
+ spec.metadata['documentation_uri'] = 'https://github.com/R95-del/recycle_bin/blob/main/README.md'
46
+ spec.metadata['wiki_uri'] = 'https://github.com/R95-del/recycle_bin/wiki'
47
+ end
@@ -0,0 +1,4 @@
1
+ module RecycleBin
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recycle_bin
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rishi Somani
8
+ - Shobhit Jain
9
+ - Raghav Agrawal
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2025-05-25 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '6.0'
22
+ - - "<"
23
+ - !ruby/object:Gem::Version
24
+ version: '9.0'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: '6.0'
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '9.0'
35
+ - !ruby/object:Gem::Dependency
36
+ name: factory_bot_rails
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '6.2'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '6.2'
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec-rails
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '6.0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '6.0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: sqlite3
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.1'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.1'
77
+ description: RecycleBin provides soft delete functionality with a user-friendly trash/recycle
78
+ bin interface for any Rails application. Easily restore deleted records with a simple
79
+ web interface.
80
+ email:
81
+ - somani.rishi81@gmail.com
82
+ - shobhjain09@gmail.com
83
+ - raghavagrawal019@gmail.com
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - CHANGELOG.md
89
+ - Gemfile
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - app/controllers/recycle_bin/application_controller.rb
94
+ - app/controllers/recycle_bin/trash_controller.rb
95
+ - 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
100
+ - app/views/recycle_bin/trash/index.html.erb
101
+ - app/views/recycle_bin/trash/show.html.erb
102
+ - config/routes.rb
103
+ - lib/generators/recycle_bin/add_deleted_at/add_deleted_at_generator.rb
104
+ - lib/generators/recycle_bin/add_deleted_at/templates/add_deleted_at_migration.rb.erb
105
+ - lib/generators/recycle_bin/install/install_generator.rb
106
+ - lib/generators/recycle_bin/install/templates/recycle_bin.rb
107
+ - lib/recycle_bin.rb
108
+ - lib/recycle_bin/engine.rb
109
+ - lib/recycle_bin/install/templates/recycle_bin.rb
110
+ - lib/recycle_bin/soft_deletable.rb
111
+ - lib/recycle_bin/version.rb
112
+ - recycle_bin.gemspec
113
+ - sig/recycle_bin.rbs
114
+ homepage: https://github.com/R95-del/recycle_bin
115
+ licenses:
116
+ - MIT
117
+ metadata:
118
+ rubygems_mfa_required: 'true'
119
+ homepage_uri: https://github.com/R95-del/recycle_bin
120
+ source_code_uri: https://github.com/R95-del/recycle_bin
121
+ changelog_uri: https://github.com/R95-del/recycle_bin/blob/main/CHANGELOG.md
122
+ bug_tracker_uri: https://github.com/R95-del/recycle_bin/issues
123
+ documentation_uri: https://github.com/R95-del/recycle_bin/blob/main/README.md
124
+ wiki_uri: https://github.com/R95-del/recycle_bin/wiki
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 2.7.0
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubygems_version: 3.5.18
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Soft delete and trash management for Ruby on Rails applications
144
+ test_files: []