ancestry 3.0.2 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8088b41fa07084ef6a761d52c2d33b051c24eae3
4
- data.tar.gz: ed5f0c0678e1d238adb4b68ed6d6239204c397a1
3
+ metadata.gz: c1c70fafe20e537ed6cb98a60e671dc01fd540ac
4
+ data.tar.gz: 583d3382374a761c553d99329f71be36ac423222
5
5
  SHA512:
6
- metadata.gz: c97d7541dbba13b6a768edab18a21c2916fbb9ea0f61674f4831a0e56f0eed83bf3b1a18bd743b1f2e0eb26b6b78ca21c69271052e3fb1042d8cf194c74802ca
7
- data.tar.gz: 7274762db4aee75b633c14fb158b68da2e4e525c4c5e5557c9c164aa6a2dc8d68c7cdc96f443112a327b0a21b76a3acc57cb09c7653ec5b24a4a839266b7313b
6
+ metadata.gz: 55df9269223d805cd09f8fb87b5e0a25cad36aaad892e3527217cd086e15c6105e5552c0d7cd108645fe9a947712986a0885252f4b71a861eec1525be374f0b7
7
+ data.tar.gz: 3f8ea178c2f3cc918ddd1fb79f8af4e3b8ed63704cd764bed42d8fb79da3b5316ab7160871eade25f8af9aac3bb004a298abc59f17e5b9759085c1ef8eb3c41d
data/README.md CHANGED
@@ -12,13 +12,20 @@ gems, integrity checking, integrity restoration, arrangement of
12
12
  (sub)tree into hashes and different strategies for dealing with orphaned
13
13
  records.
14
14
 
15
+ NOTE:
16
+
17
+ - Ancestry 3.x supports Rails 5.0 and earlier.
18
+ - Ancestry 4.0 only supports rails 5.0 and higher
19
+
15
20
  # Installation
16
21
 
17
22
  To apply Ancestry to any `ActiveRecord` model, follow these simple steps:
18
23
 
24
+
19
25
  ## Install
20
26
 
21
27
  * Add to Gemfile:
28
+
22
29
  ```ruby
23
30
  # Gemfile
24
31
 
@@ -26,6 +33,7 @@ gem 'ancestry'
26
33
  ```
27
34
 
28
35
  * Install required gems:
36
+
29
37
  ```bash
30
38
  $ bundle install
31
39
  ```
@@ -33,11 +41,13 @@ $ bundle install
33
41
 
34
42
  ## Add ancestry column to your table
35
43
  * Create migration:
44
+
36
45
  ```bash
37
46
  $ rails g migration add_ancestry_to_[table] ancestry:string:index
38
47
  ```
39
48
 
40
49
  * Migrate your database:
50
+
41
51
  ```bash
42
52
  $ rake db:migrate
43
53
  ```
@@ -24,7 +24,7 @@ EOF
24
24
 
25
25
  s.authors = ['Stefan Kroes', 'Keenan Brock']
26
26
  s.email = 'keenan@thebrocks.net'
27
- s.homepage = 'http://github.com/stefankroes/ancestry'
27
+ s.homepage = 'https://github.com/stefankroes/ancestry'
28
28
  s.license = 'MIT'
29
29
 
30
30
  s.files = [
@@ -2,7 +2,7 @@ module Ancestry
2
2
  module ClassMethods
3
3
  # Fetch tree node if necessary
4
4
  def to_node object
5
- if object.is_a?(self.ancestry_base_class) then object else find(object) end
5
+ if object.is_a?(self.ancestry_base_class) then object else unscoped_where{|scope| scope.find object} end
6
6
  end
7
7
 
8
8
  # Scope on relative depth options
@@ -206,5 +206,24 @@ module Ancestry
206
206
  yield self.ancestry_base_class.unscope(:where)
207
207
  end
208
208
  end
209
+
210
+ ANCESTRY_UNCAST_TYPES = [:string, :uuid, :text].freeze
211
+ if ActiveSupport::VERSION::STRING < "4.0"
212
+ def primary_key_is_an_integer?
213
+ if defined?(@primary_key_is_an_integer)
214
+ @primary_key_is_an_integer
215
+ else
216
+ @primary_key_is_an_integer = !ANCESTRY_UNCAST_TYPES.include?(columns_hash[primary_key.to_s].type)
217
+ end
218
+ end
219
+ else
220
+ def primary_key_is_an_integer?
221
+ if defined?(@primary_key_is_an_integer)
222
+ @primary_key_is_an_integer
223
+ else
224
+ @primary_key_is_an_integer = !ANCESTRY_UNCAST_TYPES.include?(type_for_attribute(primary_key))
225
+ end
226
+ end
227
+ end
209
228
  end
210
229
  end
@@ -46,7 +46,10 @@ module Ancestry
46
46
  scope :siblings_of, lambda { |object| where(sibling_conditions(object)) }
47
47
  scope :ordered_by_ancestry, Proc.new { |order|
48
48
  if %w(mysql mysql2 sqlite sqlite3 postgresql).include?(connection.adapter_name.downcase) && ActiveRecord::VERSION::MAJOR >= 5
49
- reorder(Arel.sql("coalesce(#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(ancestry_column)}, '')"), order)
49
+ reorder(
50
+ Arel::Nodes::Ascending.new(Arel::Nodes::NamedFunction.new('COALESCE', [arel_table[ancestry_column], Arel.sql("''")])),
51
+ order
52
+ )
50
53
  else
51
54
  reorder(Arel.sql("(CASE WHEN #{connection.quote_table_name(table_name)}.#{connection.quote_column_name(ancestry_column)} IS NULL THEN 0 ELSE 1 END), #{connection.quote_table_name(table_name)}.#{connection.quote_column_name(ancestry_column)}"), order)
52
55
  end
@@ -1,11 +1,14 @@
1
1
  module Ancestry
2
2
  module InstanceMethods
3
+ BEFORE_LAST_SAVE_SUFFIX = ActiveRecord::VERSION::STRING >= '5.1.0' ? '_before_last_save' : '_was'
4
+ IN_DATABASE_SUFFIX = ActiveRecord::VERSION::STRING >= '5.1.0' ? '_in_database' : '_was'
5
+
3
6
  # Validate that the ancestors don't include itself
4
7
  def ancestry_exclude_self
5
8
  errors.add(:base, "#{self.class.name.humanize} cannot be a descendant of itself.") if ancestor_ids.include? self.id
6
9
  end
7
10
 
8
- # Update descendants with new ancestry
11
+ # Update descendants with new ancestry (before save)
9
12
  def update_descendants_with_new_ancestry
10
13
  # If enabled and node is existing and ancestry was updated and the new ancestry is sane ...
11
14
  if !ancestry_callbacks_disabled? && !new_record? && ancestry_changed? && sane_ancestry?
@@ -16,7 +19,9 @@ module Ancestry
16
19
  descendant.update_attribute(
17
20
  self.ancestry_base_class.ancestry_column,
18
21
  descendant.read_attribute(descendant.class.ancestry_column).gsub(
22
+ # child_ancestry_was
19
23
  /^#{self.child_ancestry}/,
24
+ # future child_ancestry
20
25
  if ancestors? then "#{read_attribute self.class.ancestry_column }/#{id}" else id.to_s end
21
26
  )
22
27
  )
@@ -25,7 +30,7 @@ module Ancestry
25
30
  end
26
31
  end
27
32
 
28
- # Apply orphan strategy
33
+ # Apply orphan strategy (before destroy - no changes)
29
34
  def apply_orphan_strategy
30
35
  if !ancestry_callbacks_disabled? && !new_record?
31
36
  case self.ancestry_base_class.orphan_strategy
@@ -35,6 +40,7 @@ module Ancestry
35
40
  new_ancestry = if descendant.ancestry == child_ancestry
36
41
  nil
37
42
  else
43
+ # child_ancestry did not change so child_ancestry_was will work here
38
44
  descendant.ancestry.gsub(/^#{child_ancestry}\//, '')
39
45
  end
40
46
  descendant.update_attribute descendant.class.ancestry_column, new_ancestry
@@ -61,7 +67,7 @@ module Ancestry
61
67
  end
62
68
  end
63
69
 
64
- # Touch each of this record's ancestors
70
+ # Touch each of this record's ancestors (after save)
65
71
  def touch_ancestors_callback
66
72
  if !ancestry_callbacks_disabled? && self.ancestry_base_class.touch_ancestors
67
73
  # Touch each of the old *and* new ancestors
@@ -73,12 +79,17 @@ module Ancestry
73
79
  end
74
80
  end
75
81
 
76
- # The ancestry value for this record's children
82
+ # The ancestry value for this record's children (before save)
83
+ # This is technically child_ancestry_was
77
84
  def child_ancestry
78
85
  # New records cannot have children
79
86
  raise Ancestry::AncestryException.new('No child ancestry for new record. Save record before performing tree operations.') if new_record?
80
87
 
81
- if self.send("#{self.ancestry_base_class.ancestry_column}_was").blank? then id.to_s else "#{self.send "#{self.ancestry_base_class.ancestry_column}_was"}/#{id}" end
88
+ if self.send("#{self.ancestry_base_class.ancestry_column}#{IN_DATABASE_SUFFIX}").blank?
89
+ id.to_s
90
+ else
91
+ "#{self.send "#{self.ancestry_base_class.ancestry_column}#{IN_DATABASE_SUFFIX}"}/#{id}"
92
+ end
82
93
  end
83
94
 
84
95
  # Ancestors
@@ -93,10 +104,6 @@ module Ancestry
93
104
  changed.include?(self.ancestry_base_class.ancestry_column.to_s)
94
105
  end
95
106
 
96
- def parse_ancestry_column obj
97
- obj.to_s.split('/').map { |id| cast_primary_key(id) }
98
- end
99
-
100
107
  def ancestor_ids
101
108
  parse_ancestry_column(read_attribute(self.ancestry_base_class.ancestry_column))
102
109
  end
@@ -109,14 +116,20 @@ module Ancestry
109
116
  self.ancestry_base_class.scope_depth(depth_options, depth).ordered_by_ancestry.where ancestor_conditions
110
117
  end
111
118
 
119
+ # deprecate
112
120
  def ancestor_was_conditions
113
- {primary_key_with_table => ancestor_ids_was}
121
+ {primary_key_with_table => ancestor_ids_before_last_save}
114
122
  end
115
123
 
124
+ # deprecated - probably don't want to use anymore
116
125
  def ancestor_ids_was
117
126
  parse_ancestry_column(send("#{self.ancestry_base_class.ancestry_column}_was"))
118
127
  end
119
128
 
129
+ def ancestor_ids_before_last_save
130
+ parse_ancestry_column(send("#{self.ancestry_base_class.ancestry_column}#{BEFORE_LAST_SAVE_SUFFIX}"))
131
+ end
132
+
120
133
  def path_ids
121
134
  ancestor_ids + [id]
122
135
  end
@@ -143,6 +156,8 @@ module Ancestry
143
156
 
144
157
  # Parent
145
158
 
159
+ # currently parent= does not work in after save callbacks
160
+ # assuming that parent hasn't changed
146
161
  def parent= parent
147
162
  write_attribute(self.ancestry_base_class.ancestry_column, if parent.nil? then nil else parent.child_ancestry end)
148
163
  end
@@ -288,16 +303,10 @@ module Ancestry
288
303
 
289
304
  private
290
305
 
291
- def cast_primary_key(key)
292
- if [:string, :uuid, :text].include? primary_key_type
293
- key
294
- else
295
- key.to_i
296
- end
297
- end
298
-
299
- def primary_key_type
300
- @primary_key_type ||= column_for_attribute(self.class.primary_key).type
306
+ def parse_ancestry_column obj
307
+ obj_ids = obj.to_s.split('/')
308
+ obj_ids.map!(&:to_i) if self.class.primary_key_is_an_integer?
309
+ obj_ids
301
310
  end
302
311
 
303
312
  def unscoped_descendants
@@ -306,9 +315,10 @@ module Ancestry
306
315
  end
307
316
  end
308
317
 
318
+ # works with after save context (hence before_last_save)
309
319
  def unscoped_current_and_previous_ancestors
310
320
  unscoped_where do |scope|
311
- scope.where id: (ancestor_ids + ancestor_ids_was).uniq
321
+ scope.where id: (ancestor_ids + ancestor_ids_before_last_save).uniq
312
322
  end
313
323
  end
314
324
 
@@ -41,7 +41,7 @@ module Ancestry
41
41
  def subtree_conditions(object)
42
42
  t = arel_table
43
43
  node = to_node(object)
44
- descendant_conditions(object).or(t[primary_key].eq(node.id))
44
+ descendant_conditions(node).or(t[primary_key].eq(node.id))
45
45
  end
46
46
 
47
47
  def sibling_conditions(object)
@@ -1,3 +1,3 @@
1
1
  module Ancestry
2
- VERSION = "3.0.2"
2
+ VERSION = "3.0.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ancestry
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Kroes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-04-23 00:00:00.000000000 Z
12
+ date: 2018-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -134,7 +134,7 @@ files:
134
134
  - lib/ancestry/instance_methods.rb
135
135
  - lib/ancestry/materialized_path.rb
136
136
  - lib/ancestry/version.rb
137
- homepage: http://github.com/stefankroes/ancestry
137
+ homepage: https://github.com/stefankroes/ancestry
138
138
  licenses:
139
139
  - MIT
140
140
  metadata:
@@ -158,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
158
  version: '0'
159
159
  requirements: []
160
160
  rubyforge_project:
161
- rubygems_version: 2.6.13
161
+ rubygems_version: 2.6.14.1
162
162
  signing_key:
163
163
  specification_version: 4
164
164
  summary: Organize ActiveRecord model into a tree structure