schnecke 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 144b6900260d2c6d5d9f293cee58c0ceb25ec663ff00c77bf1d2d72f9d79fc78
4
- data.tar.gz: 6332f95d24fcc612fd1be9147f2e816c55a9484c4ac2582e9be21691902272e9
3
+ metadata.gz: bdf704d77a580357657db281c7fd5021045fcd5c2b7cca9f32f6f468cf33d895
4
+ data.tar.gz: 995fc538958ecf0ae7b1ce67cb81498e4223b3538782cc036a9763eefa1c0e4f
5
5
  SHA512:
6
- metadata.gz: 906f07f820ffe12443424f5c0e1427a5b942537911a6026f6b9644b3acdd40a817227642f86b7d01123bac0beeaed31eebf522b6e7442206cf3b16c6e4b0444e
7
- data.tar.gz: 61f35e4e5322dd7a7fc967c093697150e56d1d2c17b8f15890bf262ffdeec9306ebb7790748e06691b082ec9ac006bd163a427b4e963363fa0affbbdb8ff2e9b
6
+ metadata.gz: fd373216be05945333cc1ddd6850892151549db1e887cc64ce462ae4f98ee4e86b698e34d591b71f6fd667f5cb65ae8a3634a175a9186f88ee3c97035fcc06af
7
+ data.tar.gz: d62fc13e0f3151f338314d79e7664267812b2de7f5a70ef6e1ba5fbb24ae550a569bf7266858906ee8f7502387aa76bf7687b9f8cf3c73a6a65a0a584213604f
data/.rubocop.yml CHANGED
@@ -44,6 +44,8 @@ Metrics/MethodLength:
44
44
 
45
45
  Metrics/ModuleLength:
46
46
  Max: 100
47
+ Exclude:
48
+ - 'lib/schnecke/schnecke.rb'
47
49
 
48
50
  Minitest/MultipleAssertions:
49
51
  Max: 10
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- schnecke (0.2.0)
4
+ schnecke (0.3.0)
5
5
  activerecord (> 4.2.0)
6
6
  activesupport (> 4.2.0)
7
7
 
data/README.md CHANGED
@@ -22,10 +22,10 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- Given a class `A` which has the attribute `name` and has a `slug` column defined, we can do the following:
25
+ Given a class `SomeObject` which has the attribute `name` and has a `slug` column defined, we can do the following:
26
26
 
27
27
  ```ruby
28
- class A
28
+ class SomeObject
29
29
  include Schnecke
30
30
  slug :name
31
31
  end
@@ -34,18 +34,37 @@ end
34
34
  This will take the value in `name` and automatically set the slug based on it. If the slug needs to be based on multiple attributes of a model, simply pass in the array of attributes as follows:
35
35
 
36
36
  ```ruby
37
- class A
37
+ class SomeObject
38
38
  include Schnecke
39
39
  slug [:first_name, :last_name]
40
40
  end
41
41
  ```
42
42
 
43
+ Under the hood, this library adds a `before_validate` callback that automatically runs a method called `assign_slug`. You are welcome to call this method explicity if you so desire.
44
+
45
+ ```ruby
46
+ obj = SomeObject.new(name: 'Hello World!')
47
+ obj.assign_slug
48
+ ```
49
+
50
+ It is important to note that if the attribute used to hold the slug (`slug` by default, see next section) already contains a value, the slug assignment **WILL NOT HAPPEN**. This means, if you manually assign the slug by explicitly setting the slug value yourself, it will not be modified. If you would like the slug to be overwrriten you can explicitly call the `reassign_slug` method.
51
+
52
+ ```ruby
53
+ obj = SomeObject.new(name: 'Hello World!', slug: 'hi')
54
+
55
+ # This will do nothing as the slug was already set
56
+ obj.assign_slug
57
+
58
+ # This will cause the slug to be assigned
59
+ obj.reassign_slug
60
+ ```
61
+
43
62
  ### Slug column
44
63
 
45
64
  By default it is assumed that the generated slug will be assigned to the `slug` attribute of the model. If one needs to place the slug in a different columm, this can be done by defining the `column` attribute:
46
65
 
47
66
  ```ruby
48
- class A
67
+ class SomeObject
49
68
  include Schnecke
50
69
  slug :name, column: :some_other_column
51
70
  end
@@ -59,14 +78,14 @@ By default the maxium length of a slug is 32 characters *NOT INCLUDING* any pote
59
78
 
60
79
 
61
80
  ```ruby
62
- class A
81
+ class SomeObject
63
82
  include Schnecke
64
83
  slug :name, limit_length: 15
65
84
  end
66
85
  ```
67
86
 
68
87
  ```ruby
69
- class A
88
+ class SomeObject
70
89
  include Schnecke
71
90
  slug :name, limit_length: false
72
91
  end
@@ -74,21 +93,21 @@ end
74
93
 
75
94
  ### Slug Uniquness
76
95
 
77
- By default slugs are unique to the object that defines the slug. For example if we have the 2 objects, `A` and `B` as defined as below, then the slugs will be unique for all slugs for all type `A` objcets and all type `B` objects.
96
+ By default slugs are unique to the object that defines the slug. For example if we have the 2 objects, `SomeObject` and `SomeOtherObject` as defined as below, then the slugs will be unique for all slugs for all type `SomeObject` objcets and all type `SomeOtherObject` objects.
78
97
 
79
98
  ```ruby
80
- class A
99
+ class SomeObject
81
100
  include Schnecke
82
101
  slug :name
83
102
  end
84
103
 
85
- class B
104
+ class SomeOtherObject
86
105
  include Schnecke
87
106
  slug :name
88
107
  end
89
108
  ```
90
109
 
91
- This means that the slug `foo` can exists 2 times; once for any object of type `A` and once for any object of type `B`. Currently there is no way to create globally unique slugs. If this is something that is required, then something like [`friendly_id`](https://github.com/norman/friendly_id) might be more appropriate for your use case.
110
+ This means that the slug `foo` can exists 2 times; once for any object of type `SomeObject` and once for any object of type `SomeOtherObject`. Currently there is no way to create globally unique slugs. If this is something that is required, then something like [`friendly_id`](https://github.com/norman/friendly_id) might be more appropriate for your use case.
92
111
 
93
112
  ### Handling non-unique slugs
94
113
 
@@ -98,7 +117,7 @@ It is important to note that the maximum length of a slug does not include the a
98
117
 
99
118
  ### Defining a custom uniqueness scope
100
119
 
101
- There are times when we want slugs not be unique for all objects of type `A`, but rather for a smaller scope. For example, let's say we have a system with multiple `Accounts`, each containing `Record`s. If we want the slug for the `Record` to be unique only within the scope of an `account` we can do by providing the uniqueness scope when setting up the slug.
120
+ There are times when we want slugs not be unique for all objects of type `SomeObject`, but rather for a smaller scope. For example, let's say we have a system with multiple `Accounts`, each containing `Record`s. If we want the slug for the `Record` to be unique only within the scope of an `account` we can do by providing the uniqueness scope when setting up the slug.
102
121
 
103
122
  ```ruby
104
123
  class Record
@@ -121,12 +140,33 @@ class Tag
121
140
  end
122
141
  ```
123
142
 
143
+ ### Callbacks
144
+
145
+ Two callbacks, `before_assign_slug` and `after_assign_slug`, are provided so that you can run arbitrary code before and after the slug assignment process. Both of these callbacks will always run regardless of whether or not a slug is to be assigned. The only time `after_assign_slug` is not run is if there is an exception raised during the assignment process.
146
+
147
+ Note, since `reassign_slug` is just a forced assignment of a slug, both callbacks will run as well.
148
+
149
+ ```ruby
150
+ class SomeObject
151
+ include Schnecke
152
+ slug :name
153
+
154
+ def before_assign_slug(opts={})
155
+ puts 'Hello world! I get run before the slug assignment process'
156
+ end
157
+
158
+ def after_assign_slug(opts={})
159
+ puts 'Goodbye world! I get run after the slug assignment process'
160
+ end
161
+ end
162
+ ```
163
+
124
164
  ### Advanced Usage
125
165
 
126
166
  If you need to change how the slug is generated, how duplicates are handled, etc., you can overwrite the methods in your class. For example to change how slugs are generated you can overwrite the `slugify` method.
127
167
 
128
168
  ```ruby
129
- class A
169
+ class SomeObject
130
170
  include Schnecke
131
171
  slug :name
132
172
 
@@ -140,7 +180,7 @@ end
140
180
  Note, by default the library will validate to ensure that the slug only contains lowercase alpphanumeric letters and '-' or '_'. If your new method changes the alloweable set of characters you can either disable this validation, or pass in your own validation pattern.
141
181
 
142
182
  ```ruby
143
- class A
183
+ class SomeObject
144
184
  include Schnecke
145
185
  slug :name, require_format: false
146
186
 
@@ -149,7 +189,7 @@ class A
149
189
  end
150
190
  end
151
191
 
152
- class B
192
+ class SomeOtherObject
153
193
  include Schnecke
154
194
  slug :name, require_format: /\A[a-z]+\z/
155
195
 
@@ -64,7 +64,47 @@ module Schnecke
64
64
  # Note, a slug will not be assigned if one already exists. If one needs to
65
65
  # force the assignment of a slug, pass `force: true`
66
66
  def assign_slug(opts = {})
67
- validate_source
67
+ before_assign_slug(opts)
68
+ perform_slug_assign(opts)
69
+ after_assign_slug(opts)
70
+ end
71
+
72
+ # Reassign the slug
73
+ #
74
+ # Unlike assign_slug, this will cause a slug to be created even if one
75
+ # already exists.
76
+ def reassign_slug(opts = {})
77
+ opts[:force] = true
78
+ assign_slug(opts)
79
+ end
80
+
81
+ # Callback that is handled before the slug assignment process.
82
+ #
83
+ # When this is called, no validations, or decisions about whether or not
84
+ # a slug should be created have been made. As such, this will always run
85
+ # regardless of whether or not the slug assignment process proceeds or not.
86
+ def before_assign_slug(opts = {})
87
+ # Left blank, but can be implemented by user
88
+ end
89
+
90
+ # Callback that is handled after the slug is assigned
91
+ #
92
+ # Unless an error is raised during the slug assignment process, this method
93
+ # will always be called regardless of whether or not the slug was assigned
94
+ def after_assign_slug(opts = {})
95
+ # Left blank, but can be implemented by user
96
+ end
97
+
98
+ protected
99
+
100
+ # Assign the slug
101
+ #
102
+ # This is automatically called before model validation.
103
+ #
104
+ # Note, a slug will not be assigned if one already exists. If one needs to
105
+ # force the assignment of a slug, pass `force: true`
106
+ def perform_slug_assign(opts = {})
107
+ validate_slug_source
68
108
  validate_slug_column
69
109
 
70
110
  return if !should_create_slug? && !opts[:force]
@@ -79,7 +119,7 @@ module Schnecke
79
119
  end
80
120
 
81
121
  # Make sure it is not too long
82
- candidate_slug = truncate(candidate_slug)
122
+ candidate_slug = truncate_slug(candidate_slug)
83
123
 
84
124
  # If there is a duplicate, create a unique one
85
125
  if slug_exists?(candidate_slug)
@@ -89,16 +129,6 @@ module Schnecke
89
129
  self[schnecke_config[:slug_column]] = candidate_slug
90
130
  end
91
131
 
92
- # Reassign the slug
93
- #
94
- # Unlike assign_slug, this will cause a slug to be created even if one
95
- # already exists.
96
- def reassign_slug
97
- assign_slug(force: true)
98
- end
99
-
100
- protected
101
-
102
132
  # Slugify a string
103
133
  #
104
134
  # This will take a string and convert it to a slug by removing punctuation
@@ -106,9 +136,9 @@ module Schnecke
106
136
  #
107
137
  # This can be overriden if a different slug generation method is needed
108
138
  def slugify(str)
109
- return if str.blank?
139
+ return str if str.blank?
110
140
 
111
- str.gsub!(/[\p{Pc}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]/, '')
141
+ str = str.gsub(/[\p{Pc}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]/, '')
112
142
  str.parameterize
113
143
  end
114
144
 
@@ -131,7 +161,7 @@ module Schnecke
131
161
  #
132
162
  # This can be overriden if a different behavior is desired
133
163
  def slugify_duplicate(slug)
134
- return if slug.blank?
164
+ return slug if slug.blank?
135
165
 
136
166
  seq = 2
137
167
  new_slug = slug_concat([slug, seq])
@@ -156,7 +186,7 @@ module Schnecke
156
186
 
157
187
  private
158
188
 
159
- def validate_source
189
+ def validate_slug_source
160
190
  source = arrayify(schnecke_config[:slug_source])
161
191
  source.each do |attr|
162
192
  unless respond_to?(attr)
@@ -185,9 +215,9 @@ module Schnecke
185
215
  parts.join(schnecke_config[:slug_separator])
186
216
  end
187
217
 
188
- def truncate(slug)
189
- return if slug.blank?
190
- return if schnecke_config[:limit_length].blank?
218
+ def truncate_slug(slug)
219
+ return slug if slug.blank?
220
+ return slug if schnecke_config[:limit_length].blank?
191
221
 
192
222
  slug[0, schnecke_config[:limit_length]]
193
223
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Schnecke
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schnecke
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick R. Schmid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-01 00:00:00.000000000 Z
11
+ date: 2022-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler