swifter_enum 1.1.0 → 1.1.1

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +226 -70
  3. data/lib/swifter_enum/version.rb +1 -1
  4. metadata +7 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b447021b43c633d5d52bdaa9d10175e121eab3ebdefcd8400fb12421c0b127af
4
- data.tar.gz: 3d960b5fb93cf81659d049ceba6c40bf397f2f3d051c05e136579738a9f35d2b
3
+ metadata.gz: 256304e01311b60f1cf26bc7ad5d52fb7ba625bbc6c3b4216e6af129042d3516
4
+ data.tar.gz: fd82b318f1aa43a43c87eeebb965a36515b40f23f1a4a92928ed7d2e374052d5
5
5
  SHA512:
6
- metadata.gz: 1a4c829b0768b63f2de100eaa8439e066db41b1d8ea6fcf62691121d15c6c16f6b7c70395bd1fd08d34074e05a39fd26326d1dab4f60fdb8d26610047c91d8ea
7
- data.tar.gz: 200af325588c59f48568cda24b3125aa420fd6354d66a3c589b58aa879bb37f337fbc09f770d6de321bde9359091affe0130ce201c15215c7ad74bc838aad458
6
+ metadata.gz: 3e22b72fbbdc5a9090bc18189ea10542c08ad17fc4236067c304274c8bd5100090adf7e2eb5f94b264f5f9f1fe8b84a64c9e9bff849a24249b60d689ab53b443
7
+ data.tar.gz: 67cfb099c8c99eb3d754802841f49a144f6fcfba54cd2855dd6b3f645b0f16946870fe9f0bc1509f5ba06cedffbb92460755bf35a629156b10414a0fd3be6a21
data/README.md CHANGED
@@ -1,53 +1,195 @@
1
1
  # SwifterEnum
2
2
 
3
- SwifterEnum is a Ruby gem for creating enumerated types (enums) in Ruby on Rails applications.
4
-
5
- It is inspired by Swift's enums, and allows you to keep logic related to your enum directly in your enum class.
6
-
7
- so - after defining
8
-
9
- class Video < ApplicationRecord
10
- swifter_enum :camera, CameraEnum
3
+ SwifterEnum brings Swift-style enums to Ruby on Rails, allowing you to encapsulate behavior within your enum types rather than scattering it throughout your application.
4
+
5
+ ## Why SwifterEnum?
6
+
7
+ In Swift, enums are first-class types that can have methods, computed properties, and associated values. SwifterEnum brings this powerful pattern to Rails by replacing simple string/integer enums with proper objects that can carry their own behavior.
8
+
9
+ ### Key Benefits
10
+
11
+ **1. Encapsulated Behavior** - Your enum knows how to handle itself:
12
+
13
+ ```ruby
14
+ # Define your enum with its behavior
15
+ class PaymentStatusEnum < SwifterEnum::Base
16
+ set_values ({
17
+ pending: 0,
18
+ processing: 10,
19
+ completed: 20,
20
+ failed: 30,
21
+ refunded: 40
22
+ })
23
+
24
+ def completed?
25
+ [:completed, :refunded].include?(value)
26
+ end
27
+
28
+ def can_refund?
29
+ value == :completed
30
+ end
31
+
32
+ def icon
33
+ case value
34
+ when :pending then "clock"
35
+ when :processing then "spinner"
36
+ when :completed then "check-circle"
37
+ when :failed then "x-circle"
38
+ when :refunded then "rotate-left"
11
39
  end
12
-
13
- you can then define and access methods on your enum like
14
-
15
- `video.camera.icon`
16
-
17
- This avoids helper methods which distribute your enum logic around your application.
18
-
19
- **Before**
20
-
21
- helper method somewhere in the app
22
-
23
- #app/helpers/controller_helper.rb
24
-
25
- def icon_for(camera:)
26
- ...
40
+ end
41
+
42
+ def color
43
+ case value
44
+ when :pending then "gray"
45
+ when :processing then "blue"
46
+ when :completed then "green"
47
+ when :failed then "red"
48
+ when :refunded then "orange"
27
49
  end
28
-
29
- called with
30
-
31
- icon_for(camera:my_model.camera)
32
-
33
- **After**
34
-
35
- logic encapsluated within the enum class
36
-
37
- #app/models/swifter_enum/camera_enum.rb
38
- class CameraEnum < SwifterEnum::Base
39
- set_values ({ videographer: 0, handcam: 1 })
40
-
41
- def icon
42
- ...
43
- end
50
+ end
51
+ end
52
+
53
+ # Use it naturally in your models
54
+ order.payment_status.completed? # => true
55
+ order.payment_status.can_refund? # => true
56
+ order.payment_status.icon # => "check-circle"
57
+ order.payment_status.color # => "green"
58
+ ```
59
+
60
+ **2. Type Safety** - Catch invalid values at runtime:
61
+
62
+ ```ruby
63
+ # Safe access with bracket notation - raises error for invalid values
64
+ status = PaymentStatusEnum[:completed] # ✓ Returns enum instance
65
+ status = PaymentStatusEnum[:invalid] # ✗ Raises ArgumentError
66
+
67
+ # Validation in models
68
+ class Order < ApplicationRecord
69
+ swifter_enum :payment_status, PaymentStatusEnum
70
+ validates :payment_status, swifter_enum: true
71
+ end
72
+ ```
73
+
74
+ **3. Smart Enum Objects with Flexible Equality** - Returns enum instances that work naturally with symbols and strings:
75
+
76
+ ```ruby
77
+ order.payment_status # => #<PaymentStatusEnum @value=:completed>
78
+
79
+ # Flexible equality checking - all of these work:
80
+ order.payment_status == :completed # => true (symbol comparison)
81
+ order.payment_status == "completed" # => true (string comparison)
82
+ order.payment_status == other.payment_status # => true (enum instance comparison)
83
+
84
+ # Use in case statements naturally
85
+ case order.payment_status
86
+ when :pending then "Waiting for payment"
87
+ when :completed then "All done!"
88
+ when :failed then "Something went wrong"
89
+ end
90
+
91
+ # Or in arrays
92
+ [:completed, :refunded].include?(order.payment_status) # => true
93
+ ```
94
+
95
+ **4. Seamless Rails Integration** - All standard Rails enum features continue to work:
96
+
97
+ ```ruby
98
+ # Familiar Rails enum syntax
99
+ class Order < ApplicationRecord
100
+ swifter_enum :payment_status, PaymentStatusEnum
101
+ swifter_enum :priority, PriorityEnum
102
+ end
103
+
104
+ # Standard Rails enum features work unchanged:
105
+ order.payment_status = :processing # Set with symbol, string, or enum instance
106
+ order.payment_status = "processing" # String works too
107
+ order.payment_status = PaymentStatusEnum[:processing] # Or enum instance
108
+ order.completed! # Bang method sets and saves
109
+ order.completed? # => true (query method)
110
+ order.not_completed? # => false (negative query)
111
+
112
+ Order.completed # Scope returning all completed orders
113
+ Order.not_completed # Scope returning all non-completed orders
114
+ ```
115
+
116
+ **5. Progressive Migration** - Adopt incrementally without breaking existing code:
117
+
118
+ ```ruby
119
+ order.payment_status # => #<PaymentStatusEnum @value=:completed>
120
+
121
+ # Both setters work
122
+ order.payment_status = :pending # Symbol
123
+ order.payment_status = PaymentStatusEnum[:pending] # Enum instance
124
+
125
+ # Raw methods provide an 'escape hatch' to standard Rails enum handling
126
+ order.payment_status_raw # => "completed" (original Rails enum value)
127
+ Order.payment_status_raws # => {"pending"=>0, "processing"=>10, ...}
128
+ ```
129
+
130
+ ## Real-World Example
131
+
132
+ Consider a subscription system where enum behavior naturally belongs with the enum itself:
133
+
134
+ ```ruby
135
+ class SubscriptionTierEnum < SwifterEnum::Base
136
+ set_values ({
137
+ free: 0,
138
+ basic: 10,
139
+ pro: 20,
140
+ enterprise: 30
141
+ })
142
+
143
+ def price
144
+ case value
145
+ when :free then 0
146
+ when :basic then 9.99
147
+ when :pro then 29.99
148
+ when :enterprise then 99.99
149
+ end
150
+ end
151
+
152
+ def features
153
+ case value
154
+ when :free then ["5 projects", "Basic support"]
155
+ when :basic then ["20 projects", "Email support", "API access"]
156
+ when :pro then ["Unlimited projects", "Priority support", "Advanced API"]
157
+ when :enterprise then ["Everything in Pro", "SSO", "Dedicated support", "SLA"]
44
158
  end
159
+ end
160
+
161
+ def can_upgrade_to?(other_tier)
162
+ return false unless other_tier.is_a?(self.class)
163
+ self.class.values[other_tier.value] > self.class.values[value]
164
+ end
165
+
166
+ def badge_color
167
+ case value
168
+ when :free then "gray"
169
+ when :basic then "blue"
170
+ when :pro then "purple"
171
+ when :enterprise then "gold"
172
+ end
173
+ end
174
+ end
45
175
 
46
- called with
176
+ # Clean, expressive code in your views and controllers
177
+ current_user.subscription_tier.price # => 29.99
178
+ current_user.subscription_tier.features # => ["Unlimited projects", ...]
179
+ current_user.subscription_tier.can_upgrade_to?(SubscriptionTierEnum[:enterprise]) # => true
47
180
 
48
- my_model.camera.icon
181
+ # In your views
182
+ <span class="badge badge-<%= current_user.subscription_tier.badge_color %>">
183
+ <%= current_user.subscription_tier.t %>
184
+ </span>
185
+ <ul>
186
+ <% current_user.subscription_tier.features.each do |feature| %>
187
+ <li><%= feature %></li>
188
+ <% end %>
189
+ </ul>
190
+ ```
49
191
 
50
- I was prompted to create this gem by reading about enum approaches in [the RailsNotes Newsletter](https://railsnotes.beehiiv.com/p/issue-17-enums-value-objects-field-guide-enum-sort-in-order-of). Like any good programmer, none of those solutions *quite* met my requirements. Hopefully it will be useful. I welcome feedback, fixes and pull requests.
192
+ This approach eliminates helper methods, reduces case statements scattered across your codebase, and keeps related logic together where it belongs.
51
193
 
52
194
 
53
195
  ## Installation
@@ -58,47 +200,61 @@ Add this line to your application's Gemfile:
58
200
 
59
201
  ## Usage
60
202
 
61
- ### Overview
62
-
203
+ ### Basic Setup
63
204
 
64
- SwifterEnums act like a normal Rails enum - except that instead of returning string values, they return an instance of your selected class.
205
+ Define your enum class inheriting from `SwifterEnum::Base`:
65
206
 
66
- They also have various affordances so that in many cases, you can treat them as if they return symbol values.
207
+ ```ruby
208
+ class CameraEnum < SwifterEnum::Base
209
+ # Using integers for database storage (most common)
210
+ set_values ({ videographer: 0, handcam: 1 })
67
211
 
68
- We have a Video ActiveModel with an enum defined by
212
+ # Or use strings for database storage (see "Using string values to store Enum" section)
213
+ # set_values [:videographer, :handcam]
69
214
 
70
- class Video < ApplicationRecord
71
- swifter_enum :camera, CameraEnum
215
+ def icon
216
+ case @value
217
+ when :videographer then "icons/video-camera"
218
+ when :handcam then "icons/hand-stop"
72
219
  end
220
+ end
221
+ end
222
+ ```
73
223
 
74
- CameraEnum is a class like the following
224
+ Use it in your model:
75
225
 
76
- class CameraEnum < SwifterEnum::Base
77
- set_values ({ videographer: 0, handcam: 1 })
226
+ ```ruby
227
+ class Video < ApplicationRecord
228
+ swifter_enum :camera, CameraEnum
78
229
 
79
- def icon
80
- case @value
81
- when :videographer
82
- "icons/video-camera"
83
- when :handcam
84
- "icons/hand-stop"
85
- end
86
- end
87
- end
230
+ # Optional validation
231
+ validates :camera, swifter_enum: true
232
+ end
233
+ ```
234
+
235
+ ### Working with Enum Values
88
236
 
89
- This provides a richer approach to enums:
237
+ ```ruby
238
+ video = Video.first
90
239
 
91
- v = Video.first
92
- v.camera => #<CameraEnum:0x0000000134c7c290 @value=:handcam>
93
- v.camera.value => :handcam
240
+ # Getter returns an enum instance (not a string/symbol)
241
+ video.camera # => #<CameraEnum:0x000... @value=:handcam>
242
+ video.camera.value # => :handcam (access underlying symbol)
94
243
 
95
- #you can set values directly
96
- v.camera = :videographer
97
- v.camera => #<CameraEnum:0x000000013385f078 @value=:videographer>
244
+ # Multiple ways to set values
245
+ video.camera = :videographer # Symbol
246
+ video.camera = "videographer" # String
247
+ video.camera = CameraEnum[:videographer] # Enum instance
248
+ video.camera = other_video.camera # Copy from another instance
98
249
 
99
- #the purpose of this gem is that you can now define and access methods on the CameraEnum
100
- v.camera.icon => "icons/video-camera"
250
+ # Call your custom methods
251
+ video.camera.icon # => "icons/video-camera"
101
252
 
253
+ # Works naturally with conditionals
254
+ if video.camera == :handcam
255
+ # Do something
256
+ end
257
+ ```
102
258
 
103
259
  ### Safe Value Access with Bracket Notation
104
260
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwifterEnum
4
- VERSION = "1.1.0"
4
+ VERSION = "1.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swifter_enum
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Jonson
@@ -125,10 +125,11 @@ dependencies:
125
125
  - - "~>"
126
126
  - !ruby/object:Gem::Version
127
127
  version: '2.4'
128
- description: Simple enum for active record that takes inspiration from Swift's enums
129
- to allow you to encapsulate enum logic within an enum class. This is easy to drop-in
130
- as a replacement for regular rails enums, with minimal changes required. Once you
131
- switch, then you can easily extend your enums with methods.
128
+ description: 'SwifterEnum transforms Rails enums from simple values into powerful
129
+ objects with methods, computed properties, and type safety. Your enums become smart:
130
+ payment_status.can_refund?, subscription.price, status.icon - all while maintaining
131
+ 100% Rails enum compatibility. Drop-in replacement that eliminates scattered helper
132
+ methods and case statements throughout your codebase.'
132
133
  email:
133
134
  - rob@hobbyistsoftware.com
134
135
  executables: []
@@ -178,5 +179,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
179
  requirements: []
179
180
  rubygems_version: 3.6.8
180
181
  specification_version: 4
181
- summary: Active Record enum that uses a class, so you can add methods.
182
+ summary: Swift-style enums for Rails that encapsulate behavior within your enum types
182
183
  test_files: []