Skip to content

Commit c6d2381

Browse files
committed
Add support for Sequel
1 parent f83e7a4 commit c6d2381

File tree

7 files changed

+455
-0
lines changed

7 files changed

+455
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
99
### Added
1010

1111
- The ability to configure the random generator for the gem via `KSUID.configure`. This allows you to set up random generation to the specifications you need, whether that is for speed or for security.
12+
- A plugin for Sequel to support KSUID fields. You can include the plugin via `plugin :ksuid` within a `Sequel::Model` class.
1213

1314
### Changed
1415

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@ group :ci do
3232
end
3333

3434
group :test do
35+
gem 'jdbc-sqlite3', platforms: %i[jruby]
3536
gem 'rspec', '~> 3.6'
37+
gem 'sequel'
38+
gem 'sqlite3', platforms: %i[mri mingw x64_mingw]
3639
end

config.reek

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
ManualDispatch:
33
exclude:
44
- "KSUID::Configuration#assert_generator_is_callable"
5+
- "Sequel::Plugins::Ksuid::InstanceMethods#set_ksuid"
56

67
UncommunicativeModuleName:
78
exclude:

lib/ksuid.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require_relative 'ksuid/configuration'
44
require_relative 'ksuid/type'
55
require_relative 'ksuid/version'
6+
require_relative 'sequel/plugins/ksuid' if defined?(Sequel)
67

78
# The K-Sortable Unique IDentifier (KSUID)
89
#

lib/sequel/plugins/ksuid.rb

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# frozen_string_literal: true
2+
3+
module Sequel # :nodoc:
4+
module Plugins # :nodoc:
5+
# Adds KSUID support to the Sequel ORM
6+
#
7+
# @api public
8+
#
9+
# @example Creates a model with a standard, string-based KSUID
10+
# connection_string = 'sqlite:/'
11+
# connection_string = 'jdbc:sqlite::memory:' if RUBY_ENGINE == 'jruby'
12+
# DB = Sequel.connect(connection_string)
13+
#
14+
# DB.create_table!(:events) do
15+
# Integer :id
16+
# String :ksuid
17+
# end
18+
#
19+
# class Event < Sequel::Model(:events)
20+
# plugin :ksuid
21+
# end
22+
#
23+
# @example Creates a model with a customized KSUID field
24+
# connection_string = 'sqlite:/'
25+
# connection_string = 'jdbc:sqlite::memory:' if RUBY_ENGINE == 'jruby'
26+
# DB = Sequel.connect(connection_string)
27+
#
28+
# DB.create_table!(:events) do
29+
# Integer :id
30+
# String :correlation_id
31+
# end
32+
#
33+
# class Event < Sequel::Model(:events)
34+
# plugin :ksuid, field: :correlation_id
35+
# end
36+
#
37+
# @example Creates a model that always overwrites the KSUID on save
38+
# connection_string = 'sqlite:/'
39+
# connection_string = 'jdbc:sqlite::memory:' if RUBY_ENGINE == 'jruby'
40+
# DB = Sequel.connect(connection_string)
41+
#
42+
# DB.create_table!(:events) do
43+
# Integer :id
44+
# String :ksuid
45+
# end
46+
#
47+
# class Event < Sequel::Model(:events)
48+
# plugin :ksuid, force: true
49+
# end
50+
#
51+
# @example Creates a model with a binary-encoded KSUID
52+
# connection_string = 'sqlite:/'
53+
# connection_string = 'jdbc:sqlite::memory:' if RUBY_ENGINE == 'jruby'
54+
# DB = Sequel.connect(connection_string)
55+
#
56+
# DB.create_table!(:events) do
57+
# Integer :id
58+
# blob :ksuid
59+
# end
60+
#
61+
# class Event < Sequel::Model(:events)
62+
# plugin :ksuid, binary: true
63+
# end
64+
module Ksuid
65+
# Configures the plugin by setting available options
66+
#
67+
# @api private
68+
#
69+
# @param model [Sequel::Model] the model to configure
70+
# @param options [Hash] the hash of available options
71+
# @option options [Boolean] :binary encode the KSUID as a binary string
72+
# @option options [Boolean] :field the field to use as a KSUID
73+
# @option options [Boolean] :force overwrite the field on save
74+
# @option options [Boolean] :wrap wraps the KSUID into a KSUID type
75+
# @return [void]
76+
def self.configure(model, options = OPTS)
77+
model.instance_exec do
78+
extract_configuration(options)
79+
define_ksuid_accessor
80+
end
81+
end
82+
83+
# Class methods that are extended onto an enabling model class
84+
#
85+
# @api private
86+
module ClassMethods
87+
# The field that is enabled with KSUID handling
88+
#
89+
# @api private
90+
#
91+
# @return [Symbol]
92+
attr_reader :ksuid_field
93+
94+
# Checks whether the KSUID should be binary encoded
95+
#
96+
# @api private
97+
#
98+
# @return [Boolean]
99+
def ksuid_binary?
100+
@ksuid_binary
101+
end
102+
103+
# Defines an accessor for the KSUID that converts it into a KSUID
104+
#
105+
# @api private
106+
#
107+
# @return [void]
108+
def define_ksuid_accessor
109+
return unless @ksuid_wrap
110+
111+
define_ksuid_getter
112+
define_ksuid_setter
113+
end
114+
115+
# Defines a getter for the KSUID that converts it into a KSUID
116+
#
117+
# @api private
118+
#
119+
# @return [void]
120+
def define_ksuid_getter
121+
define_method(@ksuid_field) do
122+
KSUID.call(super())
123+
end
124+
end
125+
126+
# Defines a setter for the KSUID that converts the value properly
127+
#
128+
# @api private
129+
#
130+
# @return [void]
131+
def define_ksuid_setter
132+
define_method("#{@ksuid_field}=") do |ksuid|
133+
ksuid = KSUID.call(ksuid)
134+
135+
if self.class.ksuid_binary?
136+
super(ksuid.to_bytes)
137+
else
138+
super(ksuid.to_s)
139+
end
140+
end
141+
end
142+
143+
# Extracts all configuration options from the configure step
144+
#
145+
# @api private
146+
#
147+
# @return [void]
148+
def extract_configuration(options)
149+
@ksuid_binary = options.fetch(:binary, false)
150+
@ksuid_field = options.fetch(:field, :ksuid)
151+
@ksuid_overwrite = options.fetch(:force, false)
152+
@ksuid_wrap = options.fetch(:wrap, false)
153+
end
154+
155+
# Checks whether the KSUID should be overwritten upon save
156+
#
157+
# @api private
158+
#
159+
# @return [Boolean]
160+
def ksuid_overwrite?
161+
@ksuid_overwrite
162+
end
163+
164+
# Checks whether the model should wrap its KSUID field in a type
165+
#
166+
# @api private
167+
#
168+
# @return [Boolean]
169+
def ksuid_wrap?
170+
@ksuid_wrap
171+
end
172+
173+
Plugins.inherited_instance_variables(
174+
self,
175+
:@ksuid_binary => nil,
176+
:@ksuid_field => nil,
177+
:@ksuid_overwrite => nil,
178+
:@ksuid_wrap => nil
179+
)
180+
end
181+
182+
# Instance methods that are included in an enabling model class
183+
#
184+
# @api private
185+
module InstanceMethods
186+
# Generates a KSUID for the field before validation
187+
#
188+
# @api private
189+
#
190+
# @return [void]
191+
def before_validation
192+
set_ksuid if new?
193+
super
194+
end
195+
196+
private
197+
198+
# A hook method for generating a new KSUID
199+
#
200+
# @api private
201+
#
202+
# @return [String] a binary or base 62-encoded string
203+
def create_ksuid
204+
ksuid = KSUID.new
205+
206+
if self.class.ksuid_binary?
207+
ksuid.to_bytes
208+
else
209+
ksuid.to_s
210+
end
211+
end
212+
213+
# Initializes the KSUID field when it is not set, or overwrites it if enabled
214+
#
215+
# Note: The disabled Rubocop rule is to allow the method to follow
216+
# Sequel conventions.
217+
#
218+
# @api private
219+
#
220+
# @param ksuid [String] the normal string or byte string of the KSUID
221+
# @return [void]
222+
# rubocop:disable Naming/AccessorMethodName
223+
def set_ksuid(ksuid = create_ksuid)
224+
field = model.ksuid_field
225+
setter = :"#{field}="
226+
227+
return unless respond_to?(field) &&
228+
respond_to?(setter) &&
229+
(model.ksuid_overwrite? || !get_column_value(field))
230+
231+
set_column_value(setter, ksuid)
232+
end
233+
end
234+
end
235+
end
236+
end

spec/doctest_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# frozen_string_literal: true
22

3+
require 'sequel'
4+
require 'sqlite3' unless RUBY_ENGINE == 'jruby'
35
require 'ksuid'

0 commit comments

Comments
 (0)