friendly_id 5.2.3 → 5.4.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.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/stale.yml +17 -0
  4. data/.github/workflows/test.yml +60 -0
  5. data/Changelog.md +39 -1
  6. data/Gemfile +3 -0
  7. data/README.md +54 -163
  8. data/Rakefile +2 -2
  9. data/UPGRADING.md +115 -0
  10. data/certs/parndt.pem +25 -0
  11. data/friendly_id.gemspec +7 -3
  12. data/gemfiles/Gemfile.rails-5.0.rb +2 -2
  13. data/gemfiles/{Gemfile.rails-4.2.rb → Gemfile.rails-5.1.rb} +4 -5
  14. data/gemfiles/{Gemfile.rails-4.1.rb → Gemfile.rails-5.2.rb} +5 -7
  15. data/gemfiles/{Gemfile.rails-4.0.rb → Gemfile.rails-6.0.rb} +5 -8
  16. data/lib/friendly_id/base.rb +4 -8
  17. data/lib/friendly_id/candidates.rb +0 -2
  18. data/lib/friendly_id/configuration.rb +3 -2
  19. data/lib/friendly_id/finder_methods.rb +18 -7
  20. data/lib/friendly_id/finders.rb +1 -1
  21. data/lib/friendly_id/history.rb +21 -12
  22. data/lib/friendly_id/initializer.rb +11 -0
  23. data/lib/friendly_id/migration.rb +9 -3
  24. data/lib/friendly_id/object_utils.rb +9 -2
  25. data/lib/friendly_id/scoped.rb +8 -1
  26. data/lib/friendly_id/sequentially_slugged.rb +12 -2
  27. data/lib/friendly_id/slug.rb +4 -0
  28. data/lib/friendly_id/slugged.rb +2 -2
  29. data/lib/friendly_id/version.rb +1 -1
  30. data/test/databases.yml +6 -4
  31. data/test/finders_test.rb +24 -0
  32. data/test/helper.rb +13 -3
  33. data/test/history_test.rb +86 -7
  34. data/test/numeric_slug_test.rb +31 -0
  35. data/test/object_utils_test.rb +2 -0
  36. data/test/schema.rb +19 -2
  37. data/test/scoped_test.rb +13 -0
  38. data/test/sequentially_slugged_test.rb +59 -0
  39. data/test/shared.rb +2 -2
  40. data/test/simple_i18n_test.rb +2 -2
  41. data/test/slugged_test.rb +168 -4
  42. data.tar.gz.sig +2 -0
  43. metadata +44 -15
  44. metadata.gz.sig +0 -0
  45. data/.travis.yml +0 -40
data/test/history_test.rb CHANGED
@@ -65,8 +65,7 @@ class HistoryTest < TestCaseClass
65
65
  test "should not be read only when found by slug" do
66
66
  with_instance_of(model_class) do |record|
67
67
  refute model_class.friendly.find(record.friendly_id).readonly?
68
- assert record.update_attribute :name, 'foo'
69
- assert record.update_attributes name: 'foo'
68
+ assert record.update name: 'foo'
70
69
  end
71
70
  end
72
71
 
@@ -93,6 +92,28 @@ class HistoryTest < TestCaseClass
93
92
  end
94
93
  end
95
94
 
95
+ test 'should maintain history even if current slug is not the most recent one' do
96
+ with_instance_of(model_class) do |record|
97
+ record.name = 'current'
98
+ assert record.save
99
+
100
+ # this feels like a hack. only thing i can get to work with the HistoryTestWithSti
101
+ # test cases. (Editorialist vs Journalist.)
102
+ sluggable_type = FriendlyId::Slug.first.sluggable_type
103
+ # create several slugs for record
104
+ # current slug does not have max id
105
+ FriendlyId::Slug.delete_all
106
+ FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: 'current')
107
+ FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: 'outdated')
108
+
109
+ record.reload
110
+ record.slug = nil
111
+ assert record.save
112
+
113
+ assert_equal 2, FriendlyId::Slug.count
114
+ end
115
+ end
116
+
96
117
  test "should not create new slugs that match old slugs" do
97
118
  transaction do
98
119
  first_record = model_class.create! :name => "foo"
@@ -109,10 +130,10 @@ class HistoryTest < TestCaseClass
109
130
  first_record = model_class.create! :name => "foo"
110
131
  second_record = model_class.create! :name => 'another'
111
132
 
112
- second_record.update_attributes :name => 'foo', :slug => nil
133
+ second_record.update :name => 'foo', :slug => nil
113
134
  assert_match(/foo-.*/, second_record.slug)
114
135
 
115
- first_record.update_attributes :name => 'another', :slug => nil
136
+ first_record.update :name => 'another', :slug => nil
116
137
  assert_match(/another-.*/, first_record.slug)
117
138
  end
118
139
  end
@@ -172,7 +193,7 @@ class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
172
193
  end
173
194
  end
174
195
 
175
- class DependentDestroyTest < HistoryTest
196
+ class DependentDestroyTest < TestCaseClass
176
197
 
177
198
  include FriendlyId::Test
178
199
 
@@ -211,6 +232,37 @@ class DependentDestroyTest < HistoryTest
211
232
  end
212
233
  end
213
234
 
235
+ if ActiveRecord::VERSION::STRING >= '5.0'
236
+ class HistoryTestWithParanoidDeletes < HistoryTest
237
+ class ParanoidRecord < ActiveRecord::Base
238
+ extend FriendlyId
239
+ friendly_id :name, :use => :history, :dependent => false
240
+
241
+ default_scope { where(deleted_at: nil) }
242
+ end
243
+
244
+ def model_class
245
+ ParanoidRecord
246
+ end
247
+
248
+ test 'slug should have a sluggable even when soft deleted by a library' do
249
+ transaction do
250
+ assert FriendlyId::Slug.find_by_slug('paranoid').nil?
251
+ record = model_class.create(name: 'paranoid')
252
+ assert FriendlyId::Slug.find_by_slug('paranoid').present?
253
+
254
+ record.update deleted_at: Time.now
255
+
256
+ orphan_slug = FriendlyId::Slug.find_by_slug('paranoid')
257
+ assert orphan_slug.present?, 'Orphaned slug should exist'
258
+
259
+ assert orphan_slug.valid?, "Errors: #{orphan_slug.errors.full_messages}"
260
+ assert orphan_slug.sluggable.present?, 'Orphaned slug should still find corresponding paranoid sluggable'
261
+ end
262
+ end
263
+ end
264
+ end
265
+
214
266
  class HistoryTestWithSti < HistoryTest
215
267
  class Journalist < ActiveRecord::Base
216
268
  extend FriendlyId
@@ -248,7 +300,7 @@ class HistoryTestWithFriendlyFinders < HistoryTest
248
300
  begin
249
301
  assert model_class.find(old_friendly_id)
250
302
  assert model_class.exists?(old_friendly_id), "should exist? by old id for #{model_class.name}"
251
- rescue ActiveRecord::RecordNotFound => e
303
+ rescue ActiveRecord::RecordNotFound
252
304
  flunk "Could not find record by old id for #{model_class.name}"
253
305
  end
254
306
  end
@@ -346,6 +398,33 @@ class ScopedHistoryTest < TestCaseClass
346
398
  end
347
399
  end
348
400
 
401
+ test "should record history when scope changes" do
402
+ transaction do
403
+ city1 = City.create!
404
+ city2 = City.create!
405
+ with_instance_of(Restaurant) do |record|
406
+ record.name = "x"
407
+ record.slug = nil
408
+
409
+ record.city = city1
410
+ record.save!
411
+ assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
412
+ assert_equal("x", record.slugs.reload.first.slug)
413
+
414
+ record.city = city2
415
+ record.save!
416
+ assert_equal("city_id:#{city2.id}", record.slugs.reload.first.scope)
417
+
418
+ record.name = "y"
419
+ record.slug = nil
420
+ record.city = city1
421
+ record.save!
422
+ assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
423
+ assert_equal("y", record.slugs.reload.first.slug)
424
+ end
425
+ end
426
+ end
427
+
349
428
  test "should allow equal slugs in different scopes" do
350
429
  transaction do
351
430
  city = City.create!
@@ -356,4 +435,4 @@ class ScopedHistoryTest < TestCaseClass
356
435
  assert_equal record.slug, second_record.slug
357
436
  end
358
437
  end
359
- end
438
+ end
@@ -0,0 +1,31 @@
1
+ require 'helper'
2
+
3
+ class NumericSlugTest < TestCaseClass
4
+ include FriendlyId::Test
5
+ include FriendlyId::Test::Shared::Core
6
+
7
+ def model_class
8
+ Article
9
+ end
10
+
11
+ test "should generate numeric slugs" do
12
+ transaction do
13
+ record = model_class.create! :name => "123"
14
+ assert_equal "123", record.slug
15
+ end
16
+ end
17
+
18
+ test "should find by numeric slug" do
19
+ transaction do
20
+ record = model_class.create! :name => "123"
21
+ assert_equal model_class.friendly.find("123").id, record.id
22
+ end
23
+ end
24
+
25
+ test "should exist? by numeric slug" do
26
+ transaction do
27
+ record = model_class.create! :name => "123"
28
+ assert model_class.friendly.exists?("123")
29
+ end
30
+ end
31
+ end
@@ -19,6 +19,8 @@ class ObjectUtilsTest < TestCaseClass
19
19
  end
20
20
 
21
21
  test "ActiveRecord::Base instances should be unfriendly_ids" do
22
+ FriendlyId.mark_as_unfriendly(ActiveRecord::Base)
23
+
22
24
  model_class = Class.new(ActiveRecord::Base) do
23
25
  self.table_name = "authors"
24
26
  end
data/test/schema.rb CHANGED
@@ -2,7 +2,14 @@ require "friendly_id/migration"
2
2
 
3
3
  module FriendlyId
4
4
  module Test
5
- class Schema < ActiveRecord::Migration
5
+ migration_class =
6
+ if ActiveRecord::VERSION::MAJOR >= 5
7
+ ActiveRecord::Migration[4.2]
8
+ else
9
+ ActiveRecord::Migration
10
+ end
11
+
12
+ class Schema < migration_class
6
13
  class << self
7
14
  def down
8
15
  CreateFriendlyIdSlugs.down
@@ -41,6 +48,12 @@ module FriendlyId
41
48
  add_column table_name, :slug, :string
42
49
  end
43
50
 
51
+ paranoid_tables.each do |table_name|
52
+ add_column table_name, :slug, :string
53
+ add_column table_name, :deleted_at, :datetime
54
+ add_index table_name, :deleted_at
55
+ end
56
+
44
57
  # This will be used to test scopes
45
58
  add_column :novels, :novelist_id, :integer
46
59
  add_column :novels, :publisher_id, :integer
@@ -78,6 +91,10 @@ module FriendlyId
78
91
  %w[journalists articles novelists novels manuals cities]
79
92
  end
80
93
 
94
+ def paranoid_tables
95
+ ["paranoid_records"]
96
+ end
97
+
81
98
  def tables_with_uuid_primary_key
82
99
  ["menu_items"]
83
100
  end
@@ -91,7 +108,7 @@ module FriendlyId
91
108
  end
92
109
 
93
110
  def tables
94
- simple_tables + slugged_tables + scoped_tables
111
+ simple_tables + slugged_tables + scoped_tables + paranoid_tables
95
112
  end
96
113
  end
97
114
  end
data/test/scoped_test.rb CHANGED
@@ -81,4 +81,17 @@ class ScopedTest < TestCaseClass
81
81
  end
82
82
  end
83
83
 
84
+ test "should generate new slug when scope changes" do
85
+ transaction do
86
+ novelist = Novelist.create! :name => "a"
87
+ publisher = Publisher.create! :name => "b"
88
+ novel1 = Novel.create! :name => "c", :novelist => novelist, :publisher => publisher
89
+ novel2 = Novel.create! :name => "c", :novelist => novelist, :publisher => Publisher.create(:name => "d")
90
+ assert_equal novel1.friendly_id, novel2.friendly_id
91
+ novel2.publisher = publisher
92
+ novel2.save!
93
+ assert novel2.friendly_id != novel1.friendly_id
94
+ end
95
+ end
96
+
84
97
  end
@@ -137,4 +137,63 @@ class SequentiallySluggedTestWithHistory < TestCaseClass
137
137
  assert_equal 'test-name-2', record2.slug
138
138
  end
139
139
  end
140
+
141
+ test "should work with regeneration with history when 2 slugs already exists and the second is changed" do
142
+ transaction do
143
+ record1 = model_class.create! :name => "Test name"
144
+ record2 = model_class.create! :name => "Test name"
145
+ record3 = model_class.create! :name => "Another test name"
146
+ assert_equal 'test-name', record1.slug
147
+ assert_equal 'test-name-2', record2.slug
148
+ assert_equal 'another-test-name', record3.slug
149
+
150
+ record2.name = "One more test name"
151
+ record2.slug = nil
152
+ record2.save!
153
+ assert_equal 'one-more-test-name', record2.slug
154
+
155
+ record3.name = "Test name"
156
+ record3.slug = nil
157
+ record3.save!
158
+ assert_equal 'test-name-3', record3.slug
159
+ end
160
+ end
161
+ end
162
+
163
+ class City < ActiveRecord::Base
164
+ has_many :restaurants
165
+ end
166
+
167
+ class Restaurant < ActiveRecord::Base
168
+ extend FriendlyId
169
+ belongs_to :city
170
+ friendly_id :name, :use => [:sequentially_slugged, :scoped, :history], :scope => :city
171
+ end
172
+
173
+ class SequentiallySluggedTestWithScopedHistory < TestCaseClass
174
+ include FriendlyId::Test
175
+ include FriendlyId::Test::Shared::Core
176
+
177
+ def model_class
178
+ Restaurant
179
+ end
180
+
181
+ test "should work with regeneration with scoped history" do
182
+ transaction do
183
+ city1 = City.create!
184
+ city2 = City.create!
185
+ record1 = model_class.create! :name => "Test name", :city => city1
186
+ record2 = model_class.create! :name => "Test name", :city => city1
187
+
188
+ assert_equal 'test-name', record1.slug
189
+ assert_equal 'test-name-2', record2.slug
190
+
191
+ record2.name = 'Another test name'
192
+ record2.slug = nil
193
+ record2.save!
194
+
195
+ record3 = model_class.create! :name => "Test name", :city => city1
196
+ assert_equal 'test-name-3', record3.slug
197
+ end
198
+ end
140
199
  end
data/test/shared.rb CHANGED
@@ -62,7 +62,7 @@ module FriendlyId
62
62
  my_model_class = Class.new(model_class)
63
63
  self.class.const_set("Foo", my_model_class)
64
64
  with_instance_of my_model_class do |record|
65
- record.update_attributes my_model_class.friendly_id_config.slug_column => nil
65
+ record.update my_model_class.friendly_id_config.slug_column => nil
66
66
  record = my_model_class.friendly.find(record.id)
67
67
  record.class.validate Proc.new {errors.add(:name, "FAIL")}
68
68
  record.save
@@ -132,7 +132,7 @@ module FriendlyId
132
132
  test "updating record's other values should not change the friendly_id" do
133
133
  with_instance_of model_class do |record|
134
134
  old = record.friendly_id
135
- record.update_attributes! :active => false
135
+ record.update! active: false
136
136
  assert model_class.friendly.find old
137
137
  end
138
138
  end
@@ -91,14 +91,14 @@ class SimpleI18nTest < TestCaseClass
91
91
  class RegressionTest < TestCaseClass
92
92
  include FriendlyId::Test
93
93
 
94
- test "should not overwrite other locale's slugs on update_attributes" do
94
+ test "should not overwrite other locale's slugs on update" do
95
95
  transaction do
96
96
  journalist = Journalist.create!(:name => "John Smith")
97
97
  journalist.set_friendly_id("Juan Fulano", :es)
98
98
  journalist.save!
99
99
  assert_equal "john-smith", journalist.to_param
100
100
  journalist.slug = nil
101
- journalist.update_attributes :name => "Johnny Smith"
101
+ journalist.update :name => "Johnny Smith"
102
102
  assert_equal "johnny-smith", journalist.to_param
103
103
  I18n.with_locale(:es) do
104
104
  assert_equal "juan-fulano", journalist.to_param
data/test/slugged_test.rb CHANGED
@@ -92,7 +92,7 @@ class SluggedTest < TestCaseClass
92
92
  end
93
93
  end
94
94
 
95
- test "should not set slug on create if unrelated validations fail" do
95
+ test "should set slug on create if unrelated validations fail" do
96
96
  klass = Class.new model_class do
97
97
  validates_presence_of :active
98
98
  friendly_id :name, :use => :slugged
@@ -106,11 +106,30 @@ class SluggedTest < TestCaseClass
106
106
  instance = klass.new :name => 'foo'
107
107
  refute instance.save
108
108
  refute instance.valid?
109
+ assert_equal 'foo', instance.slug
110
+ end
111
+ end
112
+
113
+ test "should not set slug on create if slug validation fails" do
114
+ klass = Class.new model_class do
115
+ validates_presence_of :active
116
+ validates_length_of :slug, :minimum => 2
117
+ friendly_id :name, :use => :slugged
118
+
119
+ def self.name
120
+ "Journalist"
121
+ end
122
+ end
123
+
124
+ transaction do
125
+ instance = klass.new :name => 'x'
126
+ refute instance.save
127
+ refute instance.valid?
109
128
  assert_nil instance.slug
110
129
  end
111
130
  end
112
131
 
113
- test "should not set slug on create if unrelated validations fail with custom slug_column" do
132
+ test "should set slug on create if unrelated validations fail with custom slug_column" do
114
133
  klass = Class.new(ActiveRecord::Base) do
115
134
  self.table_name = 'authors'
116
135
  extend FriendlyId
@@ -126,11 +145,31 @@ class SluggedTest < TestCaseClass
126
145
  instance = klass.new :name => 'foo'
127
146
  refute instance.save
128
147
  refute instance.valid?
148
+ assert_equal 'foo', instance.subdomain
149
+ end
150
+ end
151
+
152
+ test "should not set slug on create if custom slug column validations fail" do
153
+ klass = Class.new(ActiveRecord::Base) do
154
+ self.table_name = 'authors'
155
+ extend FriendlyId
156
+ validates_length_of :subdomain, :minimum => 2
157
+ friendly_id :name, :use => :slugged, :slug_column => :subdomain
158
+
159
+ def self.name
160
+ "Author"
161
+ end
162
+ end
163
+
164
+ transaction do
165
+ instance = klass.new :name => 'x'
166
+ refute instance.save
167
+ refute instance.valid?
129
168
  assert_nil instance.subdomain
130
169
  end
131
170
  end
132
171
 
133
- test "should not update slug on save if unrelated validations fail" do
172
+ test "should keep new slug on save if unrelated validations fail" do
134
173
  klass = Class.new model_class do
135
174
  validates_presence_of :active
136
175
  friendly_id :name, :use => :slugged
@@ -149,10 +188,31 @@ class SluggedTest < TestCaseClass
149
188
  instance.active = nil
150
189
  refute instance.save
151
190
  refute instance.valid?
152
- assert_equal 'foo', instance.slug
191
+ assert_equal 'foobar', instance.slug
153
192
  end
154
193
  end
155
194
 
195
+ test "should not update slug on save if slug validations fail" do
196
+ klass = Class.new model_class do
197
+ validates_length_of :slug, :minimum => 2
198
+ friendly_id :name, :use => :slugged
199
+
200
+ def self.name
201
+ "Journalist"
202
+ end
203
+ end
204
+
205
+ transaction do
206
+ instance = klass.new :name => 'foo', :active => true
207
+ assert instance.save
208
+ assert instance.valid?
209
+ instance.name = 'x'
210
+ instance.slug = nil
211
+ instance.active = nil
212
+ refute instance.save
213
+ assert_equal 'foo', instance.slug
214
+ end
215
+ end
156
216
  end
157
217
 
158
218
  class SlugGeneratorTest < TestCaseClass
@@ -419,6 +479,110 @@ class FailedValidationAfterUpdateRegressionTest < TestCaseClass
419
479
 
420
480
  end
421
481
 
482
+ class ToParamTest < TestCaseClass
483
+
484
+ include FriendlyId::Test
485
+
486
+ class Journalist < ActiveRecord::Base
487
+ extend FriendlyId
488
+ validates_presence_of :active
489
+ validates_length_of :slug, :minimum => 2
490
+ friendly_id :name, :use => :slugged
491
+
492
+ attr_accessor :to_param_in_callback
493
+
494
+ after_save do
495
+ self.to_param_in_callback = to_param
496
+ end
497
+ end
498
+
499
+ test "to_param should return nil if record is unpersisted" do
500
+ assert_nil Journalist.new.to_param
501
+ end
502
+
503
+ test "to_param should return original slug if record failed validation" do
504
+ journalist = Journalist.new :name => 'Clark Kent', :active => nil
505
+ refute journalist.save
506
+ assert_equal 'clark-kent', journalist.to_param
507
+ end
508
+
509
+ test "to_param should clear slug attributes if slug attribute fails validation" do
510
+ journalist = Journalist.new :name => 'x', :active => true
511
+ refute journalist.save
512
+ assert_nil journalist.to_param
513
+ end
514
+
515
+ test "to_param should clear slug attribute if slug attribute fails validation and unrelated validation fails" do
516
+ journalist = Journalist.new :name => 'x', :active => nil
517
+ refute journalist.save
518
+ assert_nil journalist.to_param
519
+ end
520
+
521
+ test "to_param should use slugged attribute if record saved successfully" do
522
+ transaction do
523
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
524
+ assert journalist.save
525
+ assert_equal 'clark-kent', journalist.to_param
526
+ end
527
+ end
528
+
529
+ test "to_param should use new slug if existing record changes but fails to save" do
530
+ transaction do
531
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
532
+ assert journalist.save
533
+ journalist.name = 'Superman'
534
+ journalist.slug = nil
535
+ journalist.active = nil
536
+ refute journalist.save
537
+ assert_equal 'superman', journalist.to_param
538
+ end
539
+ end
540
+
541
+ test "to_param should use original slug if new slug attribute is not valid" do
542
+ transaction do
543
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
544
+ assert journalist.save
545
+ journalist.name = 'x'
546
+ journalist.slug = nil
547
+ journalist.active = nil
548
+ refute journalist.save
549
+ assert_equal 'clark-kent', journalist.to_param
550
+ end
551
+ end
552
+
553
+ test "to_param should use new slug if existing record changes successfully" do
554
+ transaction do
555
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
556
+ assert journalist.save
557
+ journalist.name = 'Superman'
558
+ journalist.slug = nil
559
+ assert journalist.save
560
+ assert_equal 'superman', journalist.to_param
561
+ end
562
+ end
563
+
564
+ test "to_param should use new slug within callbacks if new record is saved successfully" do
565
+ transaction do
566
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
567
+ assert journalist.save
568
+ assert_equal 'clark-kent', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
569
+ end
570
+ end
571
+
572
+ test "to_param should use new slug within callbacks if existing record changes successfully" do
573
+ transaction do
574
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
575
+ assert journalist.save
576
+ assert journalist.valid?
577
+ journalist.name = 'Superman'
578
+ journalist.slug = nil
579
+ assert journalist.save, "save should be successful"
580
+ assert_equal 'superman', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
581
+ end
582
+ end
583
+
584
+ end
585
+
422
586
  class ConfigurableRoutesTest < TestCaseClass
423
587
  include FriendlyId::Test
424
588
 
data.tar.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ �Hi�/��P:L�񔢣$�v���ҿ�Z��c�����B�C�݁�n��^���KӤ��&%�s[��oϗ*�(0&��g��⸡�3<p�X��]����Z�'�&.3f�Ȫ�,�#-�S�YF�Ԛ�_� L�N�6�W �E�dl��N �biS��f�+�� �[
2
+ eY~��˔�� O� �y~B��K8v$"O'�:�