acts_as_sourceable 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ .DS_Store
@@ -0,0 +1,3 @@
1
+ == ActsAsSourceable
2
+
3
+ Allows the RRN to perform garbage project on categories that are no longer referenced.
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'acts_as_sourceable'
3
+ s.version = '1.0.4'
4
+ s.date = %q{2010-09-16}
5
+ s.email = 'technical@rrnpilot.org'
6
+ s.homepage = 'http://github.com/rrn/acts_as_sourceable'
7
+ s.summary = 'perform garbage collection on categories that are no longer referenced'
8
+ s.description = 'Allows the RRN to perform garbage collection on categories that are no longer referenced.'
9
+ s.authors = ['Nicholas Jakobsen', 'Ryan Wallace']
10
+ s.require_paths = ["lib"]
11
+ s.files = `git ls-files`.split("\n")
12
+ end
@@ -0,0 +1,4 @@
1
+ require 'acts_as_sourceable/acts_as_sourceable'
2
+ require 'acts_as_sourceable/sourceable_institution'
3
+
4
+ ActiveRecord::Base.extend ActsAsSourceable::ActMethod
@@ -0,0 +1,122 @@
1
+ module ActsAsSourceable
2
+ module ActMethod
3
+ def acts_as_sourceable(options = {})
4
+ # Include Class and Instance Methods
5
+ ActiveRecord::Relation.send(:include, ActsAsSourceable::ActiveRelationMethods)
6
+ extend ActsAsSourceable::ClassMethods
7
+ include ActsAsSourceable::InstanceMethods
8
+
9
+ has_many :sourceable_institutions, :as => :sourceable, :dependent => :destroy
10
+ has_many :sources, :through => :sourceable_institutions, :source => :holding_institution
11
+
12
+ # Delegate the relation methods to the relation
13
+ class << self
14
+ delegate :update_sources, :unsource, :to => :scoped
15
+ end
16
+
17
+ class_attribute :sourceable_cache_column, :sourceable_used_by, :sourceable_sourced_by
18
+ self.sourceable_cache_column = options[:cache_column]
19
+ self.sourceable_used_by = options[:used_by]
20
+ self.sourceable_sourced_by = options[:sourced_by]
21
+
22
+ # If a cache column is provided, use that to determine which records are sourced and unsourced
23
+ # Elsif the records can be derived, we need to check the flattened item tables for any references
24
+ # Else we check the sourceable_institutions to see if the record has a recorded source
25
+ if sourceable_cache_column
26
+ scope :sourced, where(sourceable_cache_column => true)
27
+ scope :unsourced, where(sourceable_cache_column => false)
28
+ else
29
+ scope :sourced, joins(:sourceable_institutions).group("#{table_name}.id")
30
+ scope :unsourced, joins("LEFT OUTER JOIN sourceable_institutions ON sourceable_id = #{quoted_table_name}.id and sourceable_type = '#{self.name}'").where("sourceable_id IS NULL")
31
+ end
32
+
33
+ # Create a scope that returns record that is not used by the associations in sourceable_used_by
34
+ if sourceable_used_by
35
+ scope :unused, where(Array(sourceable_used_by).collect {|usage_association| "id NOT IN (" + select("#{table_name}.id").joins(usage_association).group("#{table_name}.id").to_sql + ")"}.join(' AND '))
36
+ scope :orphaned, unsourced.unused
37
+ else
38
+ scope :orphaned, unsourced
39
+ end
40
+ end
41
+ end
42
+
43
+ module ActiveRelationMethods
44
+ def update_sources
45
+ scoping { @klass.find_each(&:update_sources) }
46
+ end
47
+
48
+ def unsource
49
+ scoping { @klass.update_all("#{sourceable_cache_column} = false", @klass.sourceable_cache_column => true) } if @klass.sourceable_cache_column
50
+ scoping { SourceableInstitution.where("sourceable_type = ? AND sourceable_id IN (#{@klass.select(:id).to_sql})", @klass.name).delete_all }
51
+ end
52
+ end
53
+
54
+ module ClassMethods
55
+ def acts_like_sourceable?
56
+ true
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ def acts_like_sourceable?
62
+ true
63
+ end
64
+
65
+ # Automatically update the sources for this model
66
+ # If the model gets its sources from another model, collect the sources of that model and record them as your own
67
+ # Else, this model must belong to a holding institution, so that is the source
68
+ def update_sources
69
+ if sourceable_sourced_by
70
+ if self.class.reflect_on_association(sourceable_sourced_by.to_sym).collection?
71
+ source_id_sql = send(sourceable_sourced_by).joins(:sourceable_institutions).select("sourceable_institutions.holding_institution_id").to_sql
72
+ sources = HoldingInstitution.where("id IN (#{source_id_sql})")
73
+ set_sources(sources)
74
+ else
75
+ set_sources(send(sourceable_sourced_by).sources)
76
+ end
77
+ else
78
+ set_sources(holding_institution)
79
+ end
80
+ end
81
+
82
+ def sourced?
83
+ if sourceable_cache_column
84
+ self[sourceable_cache_column]
85
+ else
86
+ self.class.sourced.exists?(self)
87
+ end
88
+ end
89
+
90
+ def unsourced?
91
+ !sourced?
92
+ end
93
+
94
+ # NOTE: We do a much more verbose method of assigning sources than the obvious self.sources = Array(holding_institutions)
95
+ # because HoldingInstitutions are present in the production database, and assigning sources causes rails to use the
96
+ # production database (as opposed to the conversion database) to check for existing sources. This is obviously bad
97
+ # because the sources in the production database do not reflect those that are in the conversion database since we
98
+ # unsource many things during conversion.
99
+ def set_sources(holding_institutions)
100
+ holding_institution_ids = Array(holding_institutions).collect(&:id)
101
+ existing_source_ids = sourceable_institutions.pluck('holding_institution_id')
102
+
103
+ # Delete those that have been removed
104
+ condition = holding_institution_ids.any? ? "holding_institution_id NOT IN (?)" : nil # Can't use "NOT IN (?)" for an empty array because the result is always false
105
+ SourceableInstitution.where(:sourceable_type => self.class.name, :sourceable_id => self.id).delete_all(condition)
106
+
107
+ # Add those that are not present
108
+ holding_institution_ids.each do |holding_institution_id|
109
+ self.sourceable_institutions << SourceableInstitution.new(:holding_institution_id => holding_institution_id) unless existing_source_ids.include?(holding_institution_id)
110
+ end
111
+
112
+ set_sourceable_cache_column(holding_institution_ids.any?)
113
+ end
114
+
115
+ private
116
+
117
+ def set_sourceable_cache_column(value)
118
+ # Update via sql because we don't need callbacks and validations called
119
+ self.class.update_all({sourceable_cache_column => value}, :id => id) if sourceable_cache_column
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,16 @@
1
+ class SourceableInstitution < ActiveRecord::Base
2
+ belongs_to :sourceable, :polymorphic => true
3
+ belongs_to :holding_institution
4
+
5
+ validates_presence_of :sourceable_type, :sourceable_id, :holding_institution_id
6
+
7
+ # Removes sourceable institutions that no longer belong to a record or holding institution
8
+ def self.garbage_collect
9
+ ActiveRecord::Base.connection.select_values(SourceableInstitution.select("DISTINCT sourceable_type").to_sql).each do |sourceable_type|
10
+ sourceable_table_name = sourceable_type.constantize.table_name
11
+ sourceable_id_sql = SourceableInstitution.select("sourceable_institutions.id").where(:sourceable_type => sourceable_type).joins("LEFT OUTER JOIN #{sourceable_table_name} ON #{sourceable_table_name}.id = sourceable_institutions.sourceable_id").where("#{sourceable_table_name}.id IS NULL").to_sql
12
+ SourceableInstitution.delete_all("id IN (#{sourceable_id_sql})")
13
+ end
14
+ SourceableInstitution.delete_all(["holding_institution_id NOT IN (?)", HoldingInstitution.all.collect(&:id)])
15
+ end
16
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_sourceable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -18,7 +18,13 @@ email: technical@rrnpilot.org
18
18
  executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
- files: []
21
+ files:
22
+ - .gitignore
23
+ - README.rdoc
24
+ - acts_as_sourceable.gemspec
25
+ - lib/acts_as_sourceable.rb
26
+ - lib/acts_as_sourceable/acts_as_sourceable.rb
27
+ - lib/acts_as_sourceable/sourceable_institution.rb
22
28
  homepage: http://github.com/rrn/acts_as_sourceable
23
29
  licenses: []
24
30
  post_install_message: