effective_trash 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -0
- data/app/controllers/admin/trash_controller.rb +1 -1
- data/app/controllers/effective/trash_controller.rb +1 -2
- data/app/helpers/effective_trash_helper.rb +15 -13
- data/app/models/concerns/acts_as_trashable.rb +1 -8
- data/app/models/effective/datatables/trash.rb +2 -2
- data/app/models/effective/trash.rb +18 -3
- data/db/trash/trash_archived_booleans.rb +28 -0
- data/lib/effective_trash/version.rb +1 -1
- data/lib/effective_trash.rb +13 -0
- data/lib/generators/effective_trash/trash_archived_booleans_generator.rb +25 -0
- metadata +18 -3
- data/lib/effective_trash/active_record_serializer.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d48b6ae4a747e8d6d2567827966736aff26b6b53
|
4
|
+
data.tar.gz: e4eb0078becab79191be39d8bb2b0d94159e43f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ff0bc35b9b92526df5bd06991f872f57a5a831779524774490e353160f9cc06c7ed6e46077e4d284d3af8e5a3ace5f5a47d814f540eec77c52a40998b5f9dd1
|
7
|
+
data.tar.gz: 208dfa88a8522b943c9135e138a5040ce58916bc2ac21e11bb6609145e20e047244562847fa833e9690896a271a63d2778831d62159fd5f9c265e7e1f1538dfd
|
data/README.md
CHANGED
@@ -81,6 +81,23 @@ can :admin, :effective_trash
|
|
81
81
|
|
82
82
|
The user must be permitted to to `:update` an `Effective::Trash` in order to restore the trashed item.
|
83
83
|
|
84
|
+
## Upgrade database from archived booleans
|
85
|
+
|
86
|
+
Use the following generator, to produce a database migration:
|
87
|
+
|
88
|
+
```
|
89
|
+
rails generate effective_trash:trash_archived_booleans
|
90
|
+
```
|
91
|
+
|
92
|
+
The migration will trash all `archived?` objects, delete them, and replace the archived boolean on the corresponding database table.
|
93
|
+
|
94
|
+
This upgrades from this gem author's previous archived implementation, which was:
|
95
|
+
|
96
|
+
- Use an `archived` boolean on each model.
|
97
|
+
- Call the model by a scope (or default scope, yuck) somehting like `Post.where(archived: false)`.
|
98
|
+
|
99
|
+
Don't do that.
|
100
|
+
|
84
101
|
## License
|
85
102
|
|
86
103
|
MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
|
@@ -18,7 +18,7 @@ module Admin
|
|
18
18
|
|
19
19
|
def show
|
20
20
|
@trash = Effective::Trash.all.find(params[:id])
|
21
|
-
@page_title = "Trash item - #{@trash.
|
21
|
+
@page_title = "Trash item - #{@trash.trashed_to_s}"
|
22
22
|
|
23
23
|
EffectiveTrash.authorized?(self, :show, @trash)
|
24
24
|
EffectiveTrash.authorized?(self, :admin, :effective_trash)
|
@@ -10,21 +10,23 @@ module EffectiveTrashHelper
|
|
10
10
|
# Any other options are sent to the table tag
|
11
11
|
def tableize_hash(hash, options = {})
|
12
12
|
if hash.present? && hash.kind_of?(Hash)
|
13
|
-
content_tag(:table, options) do
|
14
|
-
|
15
|
-
|
16
|
-
content_tag(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
content_tag(:table, class: options[:class]) do
|
14
|
+
content_tag(:tbody) do
|
15
|
+
hash.map do |k, v|
|
16
|
+
content_tag(:tr) do
|
17
|
+
content_tag((options[:th] ? :th : :td), k) +
|
18
|
+
content_tag(:td) do
|
19
|
+
if v.kind_of?(Hash)
|
20
|
+
tableize_hash(v, options.merge({class: 'table table-hover', th: (options.key?(:sub_th) ? options[:sub_th] : options[:th])}))
|
21
|
+
elsif v.kind_of?(Array)
|
22
|
+
'[' + v.join(', ') + ']'
|
23
|
+
else
|
24
|
+
v.to_s
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
|
-
end
|
27
|
-
end
|
28
|
+
end.join('').html_safe
|
29
|
+
end
|
28
30
|
end.html_safe
|
29
31
|
else
|
30
32
|
hash.to_s.html_safe
|
@@ -17,14 +17,7 @@ module ActsAsTrashable
|
|
17
17
|
has_one :trash, as: :trashed, class_name: Effective::Trash
|
18
18
|
|
19
19
|
before_destroy do
|
20
|
-
trash
|
21
|
-
trashed: self,
|
22
|
-
user: EffectiveTrash.current_user,
|
23
|
-
trashed_to_s: to_s,
|
24
|
-
trashed_extra: (trashed_extra if respond_to?(:trashed_extra)),
|
25
|
-
|
26
|
-
details: EffectiveTrash::ActiveRecordSerializer.new(self, acts_as_trashable_options).attributes
|
27
|
-
).save!
|
20
|
+
EffectiveTrash.trash!(self)
|
28
21
|
end
|
29
22
|
|
30
23
|
# Parse Options
|
@@ -11,14 +11,14 @@ if defined?(EffectiveDatatables)
|
|
11
11
|
table_column :id, visible: false
|
12
12
|
|
13
13
|
unless attributes[:user_id] || attributes[:user] || (attributes[:user] == false)
|
14
|
-
table_column :user, label: 'Destroyed by'
|
14
|
+
table_column :user, label: 'Destroyed by'
|
15
15
|
end
|
16
16
|
|
17
17
|
table_column :trashed_type, label: 'Type'
|
18
18
|
table_column :trashed_id, label: 'Original Id', visible: false
|
19
19
|
table_column :trashed_to_s, label: 'Item'
|
20
20
|
|
21
|
-
table_column :details, visible:
|
21
|
+
table_column :details, visible: false, sortable: false do |trash|
|
22
22
|
tableize_hash(trash.details, th: true, sub_th: false, width: '100%')
|
23
23
|
end
|
24
24
|
|
@@ -19,7 +19,7 @@ module Effective
|
|
19
19
|
default_scope -> { order(updated_at: :desc) }
|
20
20
|
|
21
21
|
def to_s
|
22
|
-
[trashed_type, trashed_id].join(' ').presence || 'New Trash item'
|
22
|
+
trashed_to_s.presence || [trashed_type, trashed_id].join(' ').presence || 'New Trash item'
|
23
23
|
end
|
24
24
|
|
25
25
|
def details
|
@@ -28,9 +28,24 @@ module Effective
|
|
28
28
|
|
29
29
|
# So this is a Trash item
|
30
30
|
# When we delete ourselves, we restore this trash item first
|
31
|
-
def
|
31
|
+
def restore!
|
32
32
|
raise 'no attributes to restore from' unless details.kind_of?(Hash) && details[:attributes].present?
|
33
|
-
|
33
|
+
|
34
|
+
resource = Effective::Resource.new(trashed_type)
|
35
|
+
object = trashed_type.constantize.new(details[:attributes])
|
36
|
+
|
37
|
+
resource.nested_resources.each do |association|
|
38
|
+
if details[association.name].present? && object.respond_to?("#{association.name}_attributes=")
|
39
|
+
nested_attributes = details[association.name].inject({}) do |h, (index, nested)|
|
40
|
+
h[index] = nested[:attributes].except('id', association.inverse_of.foreign_key); h
|
41
|
+
end
|
42
|
+
|
43
|
+
object.send("#{association.name}_attributes=", nested_attributes)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
object.save!(validate: false)
|
48
|
+
destroy!
|
34
49
|
end
|
35
50
|
|
36
51
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class TrashArchivedBooleans < ActiveRecord::Migration[4.2]
|
2
|
+
def self.up
|
3
|
+
Rails.application.eager_load!
|
4
|
+
klasses = ActiveRecord::Base.descendants
|
5
|
+
.reject { |klass| klass.abstract_class? }
|
6
|
+
.select { |klass| (klass.new.respond_to?(:archived?) rescue false) }
|
7
|
+
|
8
|
+
klasses.each do |klass|
|
9
|
+
puts "Trashing #{klass.table_name}"
|
10
|
+
|
11
|
+
ids = [] # to delete
|
12
|
+
|
13
|
+
klass.unscoped.where(archived: true).find_each do |resource|
|
14
|
+
print '.'
|
15
|
+
EffectiveTrash.trash!(resource)
|
16
|
+
ids << resource.id
|
17
|
+
end
|
18
|
+
|
19
|
+
klass.where(id: ids).delete_all
|
20
|
+
end
|
21
|
+
|
22
|
+
klasses.each { |klass| remove_column(klass.table_name, :archived) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.down
|
26
|
+
raise 'TODO: this could be written'
|
27
|
+
end
|
28
|
+
end
|
data/lib/effective_trash.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'haml-rails'
|
2
|
+
require 'effective_resources'
|
2
3
|
require 'effective_trash/engine'
|
3
4
|
require 'effective_trash/version'
|
4
5
|
|
@@ -31,4 +32,16 @@ module EffectiveTrash
|
|
31
32
|
@effective_trash_current_user
|
32
33
|
end
|
33
34
|
|
35
|
+
# Trash it - Does not delete the original object.
|
36
|
+
# This is run in a before_destroy, or through a script.
|
37
|
+
def self.trash!(obj)
|
38
|
+
trash = Effective::Trash.new(
|
39
|
+
trashed: obj,
|
40
|
+
user: EffectiveTrash.current_user,
|
41
|
+
trashed_to_s: obj.to_s,
|
42
|
+
trashed_extra: (trashed_extra if obj.respond_to?(:trashed_extra)),
|
43
|
+
details: Effective::Resource.new(obj).instance_attributes
|
44
|
+
).save!
|
45
|
+
end
|
46
|
+
|
34
47
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# bundle exec rails generate effective_trash:trash_archived_booleans
|
2
|
+
|
3
|
+
module EffectiveTrash
|
4
|
+
module Generators
|
5
|
+
class TrashArchivedBooleansGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
desc 'Upgrade all tables from boolean archived implementation. Trash all previously archived records'
|
9
|
+
|
10
|
+
source_root File.expand_path('../../template', __FILE__)
|
11
|
+
|
12
|
+
def self.next_migration_number(dirname)
|
13
|
+
if not ActiveRecord::Base.timestamped_migrations
|
14
|
+
Time.new.utc.strftime('%Y%m%d%H%M%S')
|
15
|
+
else
|
16
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_migration_file
|
21
|
+
migration_template ('../' * 3) + 'db/trash/trash_archived_booleans.rb', 'db/migrate/trash_archived_booleans.rb'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_trash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 2.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: effective_resources
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: coffee-rails
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,12 +119,13 @@ files:
|
|
105
119
|
- config/effective_trash.rb
|
106
120
|
- config/routes.rb
|
107
121
|
- db/migrate/01_create_effective_trash.rb.erb
|
122
|
+
- db/trash/trash_archived_booleans.rb
|
108
123
|
- lib/effective_trash.rb
|
109
|
-
- lib/effective_trash/active_record_serializer.rb
|
110
124
|
- lib/effective_trash/engine.rb
|
111
125
|
- lib/effective_trash/set_current_user.rb
|
112
126
|
- lib/effective_trash/version.rb
|
113
127
|
- lib/generators/effective_trash/install_generator.rb
|
128
|
+
- lib/generators/effective_trash/trash_archived_booleans_generator.rb
|
114
129
|
homepage: https://github.com/code-and-effect/effective_trash
|
115
130
|
licenses:
|
116
131
|
- MIT
|
@@ -1,61 +0,0 @@
|
|
1
|
-
module EffectiveTrash
|
2
|
-
class ActiveRecordSerializer
|
3
|
-
attr_accessor :resource, :options
|
4
|
-
|
5
|
-
def initialize(resource, options = {})
|
6
|
-
raise ArgumentError.new('options must be a Hash') unless options.kind_of?(Hash)
|
7
|
-
|
8
|
-
@resource = resource
|
9
|
-
@options = options
|
10
|
-
end
|
11
|
-
|
12
|
-
def attributes
|
13
|
-
attributes = { attributes: resource.attributes }
|
14
|
-
|
15
|
-
# Collect to_s representations of all belongs_to associations
|
16
|
-
(resource.class.try(:reflect_on_all_associations, :belongs_to) || []).each do |association|
|
17
|
-
attributes[association.name] = resource.send(association.name).to_s.presence
|
18
|
-
end
|
19
|
-
|
20
|
-
# Collect to_s representations for all has_one associations
|
21
|
-
(resource.class.try(:reflect_on_all_associations, :has_one) || []).each do |association|
|
22
|
-
next if association.name == :trash && resource.respond_to?(:acts_as_trashable_options) # We skip our own association
|
23
|
-
attributes[association.name] = resource.send(association.name).to_s.presence
|
24
|
-
end
|
25
|
-
|
26
|
-
# Collects attributes for all accepts_as_nested_parameters has_many associations
|
27
|
-
(resource.class.try(:reflect_on_all_autosave_associations) || []).each do |association|
|
28
|
-
attributes[association.name] = {}
|
29
|
-
|
30
|
-
Array(resource.send(association.name)).each_with_index do |child, index|
|
31
|
-
attributes[association.name][index+1] = ActiveRecordLogger.new(child, options).attributes
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
attributes.delete_if { |k, v| v.blank? }
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
# TODO: Make this work better with nested objects
|
41
|
-
def applicable(attributes)
|
42
|
-
atts = if options[:only].present?
|
43
|
-
attributes.slice(*options[:only])
|
44
|
-
elsif options[:except].present?
|
45
|
-
attributes.except(*options[:except])
|
46
|
-
else
|
47
|
-
attributes.except(:updated_at, :created_at)
|
48
|
-
end
|
49
|
-
|
50
|
-
(options[:additionally] || []).each do |attribute|
|
51
|
-
value = (resource.send(attribute) rescue :effective_trash_nope)
|
52
|
-
next if attributes[attribute].present? || value == :effective_trash_nope
|
53
|
-
|
54
|
-
atts[attribute] = value
|
55
|
-
end
|
56
|
-
|
57
|
-
atts
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|