enm 0.1.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 +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/Cargo.lock +1030 -0
- data/Cargo.toml +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +29 -0
- data/ext/enm/Cargo.toml +22 -0
- data/ext/enm/build.rs +5 -0
- data/ext/enm/extconf.rb +6 -0
- data/ext/enm/src/lib.rs +543 -0
- data/lib/enm/database.rb +12 -0
- data/lib/enm/enum.rb +90 -0
- data/lib/enm/namespace.rb +176 -0
- data/lib/enm/types.rb +28 -0
- data/lib/enm/version.rb +5 -0
- data/lib/enm.rb +106 -0
- data/mise.toml +2 -0
- metadata +106 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ENM
|
|
4
|
+
# A namespace allows grouping multiple databases together for lookup,
|
|
5
|
+
# to be exposed in an atomic constant through {ENM.current}.
|
|
6
|
+
#
|
|
7
|
+
# It is not meant to be instantiated directly, instead, use {ENM.configure_current}
|
|
8
|
+
# to set it up for the gem. More advanced use cases can use {.build} for their own
|
|
9
|
+
# use cases, but the interface is not guaranteed to be stable.
|
|
10
|
+
class Namespace
|
|
11
|
+
include Dry::Initializer[undefined: false].define -> do
|
|
12
|
+
option :databases, ENM::Types::DatabaseMap
|
|
13
|
+
option :default, ENM::Types::DefaultDatabase, default: proc { databases.first[1] if databases.one? }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(...)
|
|
17
|
+
super
|
|
18
|
+
|
|
19
|
+
freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param [String, Symbol] name
|
|
23
|
+
# @return [ENM::Database, nil]
|
|
24
|
+
def [](name) = @databases[name.to_s]
|
|
25
|
+
|
|
26
|
+
# @param [String, Symbol] name
|
|
27
|
+
# @param [String, Symbol, nil] schema
|
|
28
|
+
# @param [String, Symbol, nil] on the name of the database to get the enum from.
|
|
29
|
+
# If `nil`, the default database will be used.
|
|
30
|
+
# @param [String, Symbol, #to_s, #to_sym, nil] default whether to use a
|
|
31
|
+
# certain value as dry-type's default, since it must be defined _before_
|
|
32
|
+
# the enum call.
|
|
33
|
+
# @param [:string, :symbol] mode the underlying type of the enum values.
|
|
34
|
+
# @return [Dry::Types::Type]
|
|
35
|
+
def dry(name, schema: nil, on: nil, default: nil, mode: :string)
|
|
36
|
+
enum = get(name, schema:, on:)
|
|
37
|
+
|
|
38
|
+
enum.dry(default:, mode:)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def has_default? = !@default.nil?
|
|
42
|
+
|
|
43
|
+
# @see ENM::Database#get
|
|
44
|
+
# @param [String, Symbol] name
|
|
45
|
+
# @param [String, Symbol, nil] schema
|
|
46
|
+
# @param [String, Symbol, nil] on the name of the database to get the enum from.
|
|
47
|
+
# If `nil`, the default database will be used.
|
|
48
|
+
# @return [ENM::Enum]
|
|
49
|
+
def get(name, schema: nil, on: nil)
|
|
50
|
+
database = database_for(on)
|
|
51
|
+
|
|
52
|
+
database.get(name, schema:)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# @param [#to_s, nil] name
|
|
58
|
+
# @raise [ENM::NoDefaultDatabaseError] if `name` is `nil` and no default database is configured
|
|
59
|
+
# @raise [ENM::DatabaseNotFoundError] if `name` is not
|
|
60
|
+
# @return [ENM::Database]
|
|
61
|
+
def database_for(name)
|
|
62
|
+
if name.nil?
|
|
63
|
+
if has_default?
|
|
64
|
+
@default
|
|
65
|
+
else
|
|
66
|
+
raise ENM::NoDefaultDatabaseError, "No default database configured for namespace"
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
@databases.fetch(name.to_s) do
|
|
70
|
+
raise ENM::DatabaseNotFoundError, "Database #{name} not found in namespace"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class << self
|
|
76
|
+
private_class_method :new
|
|
77
|
+
|
|
78
|
+
# @yieldparam [ENM::Namespace::Builder] builder
|
|
79
|
+
# @yieldreturn [void]
|
|
80
|
+
# @return [ENM::Namespace]
|
|
81
|
+
def build
|
|
82
|
+
builder = Builder.new
|
|
83
|
+
|
|
84
|
+
yield(builder)
|
|
85
|
+
|
|
86
|
+
new(**builder.to_h)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A builder for {ENM::Namespace}.
|
|
91
|
+
# @see ENM::Namespace.build
|
|
92
|
+
# @api private
|
|
93
|
+
class Builder
|
|
94
|
+
def initialize
|
|
95
|
+
@databases = {}
|
|
96
|
+
@default = nil
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Add a {ENM::Database database} to the namespace from a RON string.
|
|
100
|
+
#
|
|
101
|
+
# @api public
|
|
102
|
+
# @see #from_ron_file!
|
|
103
|
+
# @param [String] ron
|
|
104
|
+
# @param [Integer] version
|
|
105
|
+
# @param [Boolean] default
|
|
106
|
+
# @return [void]
|
|
107
|
+
def from_ron!(ron, version: ENM::RON_VERSION, default: false)
|
|
108
|
+
add!(Database.from_ron(version, ron), default:)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Add a {ENM::Database database} to the namespace from a RON file.
|
|
112
|
+
#
|
|
113
|
+
# @api public
|
|
114
|
+
# @see #from_ron!
|
|
115
|
+
# @param [String] path
|
|
116
|
+
# @param [Integer] version
|
|
117
|
+
# @param [Boolean] default
|
|
118
|
+
# @return [void]
|
|
119
|
+
def from_ron_file!(path, version: ENM::RON_VERSION, default: false)
|
|
120
|
+
add!(Database.from_ron_file(version, path), default:)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Add a {ENM::Database database} to the namespace from a SQL string.
|
|
124
|
+
#
|
|
125
|
+
# @api public
|
|
126
|
+
# @see #from_sql_file!
|
|
127
|
+
# @param [String, Symbol] name
|
|
128
|
+
# @param [String] sql
|
|
129
|
+
# @param [Boolean] default
|
|
130
|
+
# @return [void]
|
|
131
|
+
def from_sql!(name, sql, default: false)
|
|
132
|
+
add!(Database.from_sql(name, sql), default:)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Add a {ENM::Database database} to the namespace from a SQL string.
|
|
136
|
+
#
|
|
137
|
+
# @api public
|
|
138
|
+
# @see #from_sql!
|
|
139
|
+
# @param [String, Symbol] name
|
|
140
|
+
# @param [String] path
|
|
141
|
+
# @param [Boolean] default
|
|
142
|
+
# @return [void]
|
|
143
|
+
def from_sql_file!(name, path, default: false)
|
|
144
|
+
add!(Database.from_sql_file(name, path), default:)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Set the default database for the namespace.
|
|
148
|
+
#
|
|
149
|
+
# @api public
|
|
150
|
+
# @param [String, Symbol] name
|
|
151
|
+
# @raise [ENM::DatabaseNotFoundError] if no database with the given name is found in the namespace
|
|
152
|
+
# @return [void]
|
|
153
|
+
def default!(name)
|
|
154
|
+
@default = @databases.fetch(name.to_s) do
|
|
155
|
+
raise ENM::DatabaseNotFoundError, "Default database #{name} not found in namespace"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @api private
|
|
160
|
+
# @return [{ Symbol => Object }]
|
|
161
|
+
def to_h
|
|
162
|
+
{ databases: @databases, default: @default }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
# @param [ENM::Database] database
|
|
168
|
+
# @param [Boolean] default
|
|
169
|
+
# @return [void]
|
|
170
|
+
def add!(database, default: false)
|
|
171
|
+
@databases[database.name.to_s] = database
|
|
172
|
+
@default = database if default
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/lib/enm/types.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ENM
|
|
4
|
+
# @api private
|
|
5
|
+
module Types
|
|
6
|
+
# @!parse [ruby]
|
|
7
|
+
# include Dry::Types
|
|
8
|
+
__send__(:include, Dry.Types)
|
|
9
|
+
|
|
10
|
+
# @return [Dry::Types::Type(ENM::Database)]
|
|
11
|
+
Database = Instance(ENM::Database)
|
|
12
|
+
|
|
13
|
+
# @return [Dry::Types::Type(Hash{String => ENM::Database})]
|
|
14
|
+
DatabaseMap = Hash.map(String, Database)
|
|
15
|
+
|
|
16
|
+
# @return [Dry::Types::Type(ENM::Database, nil)]
|
|
17
|
+
DefaultDatabase = Database.optional
|
|
18
|
+
|
|
19
|
+
# @return [Dry::Types::Type(#to_s)]
|
|
20
|
+
DefaultValue = Coercible::String.constrained(filled: true)
|
|
21
|
+
|
|
22
|
+
# @return [Dry::Types::Type(ENM::Enum)]
|
|
23
|
+
Enum = Instance(ENM::Enum)
|
|
24
|
+
|
|
25
|
+
# @return [Dry::Types::Type(:string, :symbol)]
|
|
26
|
+
DryMode = Coercible::Symbol.default(:string).enum(:string, :symbol)
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/enm/version.rb
ADDED
data/lib/enm.rb
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/types"
|
|
5
|
+
require "pathname"
|
|
6
|
+
|
|
7
|
+
require_relative "enm/version"
|
|
8
|
+
require "enm/enm"
|
|
9
|
+
require "enm/types"
|
|
10
|
+
require "enm/database"
|
|
11
|
+
require "enm/enum"
|
|
12
|
+
require "enm/namespace"
|
|
13
|
+
|
|
14
|
+
# A parser and introspecter for PostgreSQL enums, made to be used
|
|
15
|
+
# within Rails or other Ruby applications.
|
|
16
|
+
module ENM
|
|
17
|
+
# @abstract The base error class for `ENM`.
|
|
18
|
+
class Error < StandardError; end
|
|
19
|
+
|
|
20
|
+
class AlreadyConfiguredError < Error; end
|
|
21
|
+
|
|
22
|
+
class DatabaseNotFoundError < Error; end
|
|
23
|
+
|
|
24
|
+
class DuplicatedTypeError < Error; end
|
|
25
|
+
|
|
26
|
+
class DuplicatedValueError < Error; end
|
|
27
|
+
|
|
28
|
+
class EnumConflictError < Error; end
|
|
29
|
+
|
|
30
|
+
class EnumNotFoundError < Error; end
|
|
31
|
+
|
|
32
|
+
# An error that is raised when trying to specify a default value that
|
|
33
|
+
# is not present in the enum.
|
|
34
|
+
class InvalidDefaultError < Error; end
|
|
35
|
+
|
|
36
|
+
# An error that is raised when there is some sort of IO error during
|
|
37
|
+
# file reading / writing operations.
|
|
38
|
+
class IOError < Error; end
|
|
39
|
+
|
|
40
|
+
# An error that is raised when trying to access the current namespace when
|
|
41
|
+
# it has yet to be initialized.
|
|
42
|
+
class NoCurrentNamespaceError < Error; end
|
|
43
|
+
|
|
44
|
+
# An error that is raised when trying to look up an enum without
|
|
45
|
+
# specifying the database, and no default database has been set.
|
|
46
|
+
class NoDefaultDatabaseError < Error; end
|
|
47
|
+
|
|
48
|
+
# An error that is raised when there is an issue during parsing of SQL input.
|
|
49
|
+
class ParseError < Error; end
|
|
50
|
+
|
|
51
|
+
# An error that is raised when there is an issue with RON serialization or deserialization.
|
|
52
|
+
class SerializationError < Error; end
|
|
53
|
+
|
|
54
|
+
# An error that is raised when specifying an unknown version during RON deserialization.
|
|
55
|
+
class UnknownVersionError < Error; end
|
|
56
|
+
|
|
57
|
+
class << self
|
|
58
|
+
# Configure {.current} namespace for the gem.
|
|
59
|
+
#
|
|
60
|
+
# @param [Boolean] force
|
|
61
|
+
# @raise [ENM::AlreadyConfiguredError] if a current namespace is already configured and `force` is not set to `true`
|
|
62
|
+
# @yieldparam [ENM::Namespace::Builder] builder
|
|
63
|
+
# @yieldreturn [void]
|
|
64
|
+
# @return [void]
|
|
65
|
+
def configure_current(force: false, &block)
|
|
66
|
+
namespace = Namespace.build(&block)
|
|
67
|
+
|
|
68
|
+
raise AlreadyConfiguredError, "Current namespace is already configured" if current? && !force
|
|
69
|
+
|
|
70
|
+
Thread.current.thread_variable_set(:enm_current_namespace, namespace)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @!attribute [r] current
|
|
74
|
+
# @raise [ENM::NoCurrentNamespaceError] if no current namespace is set
|
|
75
|
+
# @return [ENM::Namespace]
|
|
76
|
+
def current
|
|
77
|
+
Thread.current.thread_variable_get(:enm_current_namespace) || raise(ENM::NoCurrentNamespaceError, "No current namespace set")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Whether a current namespace is set.
|
|
81
|
+
def current? = Thread.current.thread_variable?(:enm_current_namespace)
|
|
82
|
+
|
|
83
|
+
# @see ENM::Namespace#[]
|
|
84
|
+
# @param [String, Symbol] name
|
|
85
|
+
# @return [ENM::Database, nil]
|
|
86
|
+
def [](name) = current[name]
|
|
87
|
+
|
|
88
|
+
# @see ENM::Namespace#dry
|
|
89
|
+
# @return [Dry::Types::Type]
|
|
90
|
+
def dry(...) = current.dry(...)
|
|
91
|
+
|
|
92
|
+
# @see ENM::Namespace#get
|
|
93
|
+
# @return [ENM::Enum]
|
|
94
|
+
def get(...) = current.get(...)
|
|
95
|
+
|
|
96
|
+
# Reset the current namespace, caches, etc.
|
|
97
|
+
#
|
|
98
|
+
# @api private
|
|
99
|
+
# @return [void]
|
|
100
|
+
def reset!
|
|
101
|
+
Thread.current.thread_variable_set(:enm_current_namespace, nil)
|
|
102
|
+
|
|
103
|
+
ENM::Enum.cache.clear
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
data/mise.toml
ADDED
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: enm
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Alexa Grey
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: concurrent-ruby
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: dry-initializer
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: dry-types
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.8'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.8'
|
|
54
|
+
description: Parse and serialize PostgreSQL enum sets in Ruby, powered by Rust.
|
|
55
|
+
email:
|
|
56
|
+
- devel@mouse.vc
|
|
57
|
+
executables: []
|
|
58
|
+
extensions:
|
|
59
|
+
- ext/enm/extconf.rb
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- CHANGELOG.md
|
|
63
|
+
- CODE_OF_CONDUCT.md
|
|
64
|
+
- Cargo.lock
|
|
65
|
+
- Cargo.toml
|
|
66
|
+
- LICENSE.txt
|
|
67
|
+
- README.md
|
|
68
|
+
- Rakefile
|
|
69
|
+
- ext/enm/Cargo.toml
|
|
70
|
+
- ext/enm/build.rs
|
|
71
|
+
- ext/enm/extconf.rb
|
|
72
|
+
- ext/enm/src/lib.rs
|
|
73
|
+
- lib/enm.rb
|
|
74
|
+
- lib/enm/database.rb
|
|
75
|
+
- lib/enm/enum.rb
|
|
76
|
+
- lib/enm/namespace.rb
|
|
77
|
+
- lib/enm/types.rb
|
|
78
|
+
- lib/enm/version.rb
|
|
79
|
+
- mise.toml
|
|
80
|
+
homepage: https://github.com/scryptmouse/enm
|
|
81
|
+
licenses:
|
|
82
|
+
- MIT
|
|
83
|
+
metadata:
|
|
84
|
+
allowed_push_host: https://rubygems.org
|
|
85
|
+
homepage_uri: https://github.com/scryptmouse/enm
|
|
86
|
+
source_code_uri: https://github.com/scryptmouse/enm
|
|
87
|
+
changelog_uri: https://github.com/scryptmouse/enm/blob/main/CHANGELOG.md
|
|
88
|
+
rubygems_mfa_required: 'true'
|
|
89
|
+
rdoc_options: []
|
|
90
|
+
require_paths:
|
|
91
|
+
- lib
|
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 3.4.0
|
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - ">="
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
requirements: []
|
|
103
|
+
rubygems_version: 4.0.8
|
|
104
|
+
specification_version: 4
|
|
105
|
+
summary: Parse and serialize PostgreSQL enum sets in Ruby, powered by Rust.
|
|
106
|
+
test_files: []
|