|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | 3 | module Sqlpkg |
| 4 | + DIR = ".sqlpkg" |
| 5 | + FILE_PATTERN = "*.{dylib,so,dll}" |
| 6 | + |
| 7 | + # Helper methods |
| 8 | + module H |
| 9 | + extend self |
| 10 | + |
| 11 | + # Don't depend on ActiveSupport just to convert a constant name to a path segment. |
| 12 | + # Handles acronyms and camelcase constants, e.g. UUID => uuid and SomeName => some_name |
| 13 | + def constant_to_path(constant_name) |
| 14 | + constant_name.to_s |
| 15 | + .gsub("::", "/") |
| 16 | + .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') |
| 17 | + .gsub(/([a-z\d])([A-Z])/, '\1_\2') |
| 18 | + .tr("-", "_") |
| 19 | + .downcase |
| 20 | + end |
| 21 | + end |
| 22 | + |
| 23 | + # The directory where `sqlpkg` stores installed extensions |
| 24 | + # => "./.sqlpkg" |
| 25 | + def self.file_dir |
| 26 | + File.join(__dir__, DIR) |
| 27 | + end |
| 28 | + |
| 29 | + # List of file paths for all installed extensions |
| 30 | + # => ["./.sqlpkg/asg017/ulid/ulid0.dylib", "./.sqlpkg/nalgeon/uuid/uuid.dylib"] |
| 31 | + def self.paths |
| 32 | + Dir.glob File.join(file_dir, "**", FILE_PATTERN) |
| 33 | + end |
| 34 | + |
| 35 | + # Dynamically resolve extension constants for installed extensions. |
| 36 | + # Allows this gem to work with the new sqlite3[https://sparklemotion.github.io/sqlite3-ruby/] |
| 37 | + # database configuration option `extensions:`, which allows an application to load extensions |
| 38 | + # when using `sqlite3` >= v2.4.0. The array members may be filesystem paths or the names of |
| 39 | + # modules that respond to `.to_path`. So, we dynamically build constants that respond to `.to_path`. |
| 40 | + # Allow either direct access `Sqlpkg::UUID` or owner-scoped access `Sqlpkg::Nalgeon::UUID`. |
| 41 | + def self.const_missing(const) |
| 42 | + path_segment = H.constant_to_path(const) |
| 43 | + is_owner = false |
| 44 | + path = paths.find do |it| |
| 45 | + _path, pkg_path = it.split("#{DIR}#{File::SEPARATOR}") |
| 46 | + owner, name, _file = pkg_path.split(File::SEPARATOR) |
| 47 | + |
| 48 | + if path_segment == owner |
| 49 | + is_owner = true |
| 50 | + else |
| 51 | + path_segment == name |
| 52 | + end |
| 53 | + end |
| 54 | + |
| 55 | + return super unless path |
| 56 | + |
| 57 | + if is_owner |
| 58 | + # Dynamically generated module for the extension owner. |
| 59 | + # This module has `.file_dir`, `.paths`, and `.const_missing` defined. |
| 60 | + # Any dynamically generated classes _must be_ installed extensions |
| 61 | + # for this owner. |
| 62 | + const_set(const, Module.new do |
| 63 | + # The directory where `sqlpkg` stores installed extensions for this owner |
| 64 | + # => "./.sqlpkg/nalgeon" |
| 65 | + def self.file_dir |
| 66 | + File.join(__dir__, ".#{H.constant_to_path(self.name)}") |
| 67 | + end |
| 68 | + |
| 69 | + # List of file paths for all installed extensions belonging to this owner |
| 70 | + # => ["./.sqlpkg/nalgeon/uuid/uuid.dylib"] |
| 71 | + def self.paths |
| 72 | + Dir.glob File.join(file_dir, "**", FILE_PATTERN) |
| 73 | + end |
| 74 | + |
| 75 | + def self.const_missing(const) |
| 76 | + path_segment = H.constant_to_path(const) |
| 77 | + path = paths.find do |it| |
| 78 | + _path, pkg_path = it.split("#{DIR}#{File::SEPARATOR}") |
| 79 | + _owner, name, _file = pkg_path.split(File::SEPARATOR) |
| 80 | + |
| 81 | + path_segment == name |
| 82 | + end |
| 83 | + |
| 84 | + return super unless path |
| 85 | + |
| 86 | + # Dynamically generated module for the extension. |
| 87 | + # This class only has `.to_path` defined. |
| 88 | + const_set(const, Class.new do |
| 89 | + define_singleton_method :to_path do |
| 90 | + path |
| 91 | + end |
| 92 | + end) |
| 93 | + end |
| 94 | + end) |
| 95 | + else |
| 96 | + # Dynamically generated module for the extension. |
| 97 | + # This class only has `.to_path` defined. |
| 98 | + const_set(const, Class.new do |
| 99 | + define_singleton_method :to_path do |
| 100 | + path |
| 101 | + end |
| 102 | + end) |
| 103 | + end |
| 104 | + end |
4 | 105 | end |
5 | 106 |
|
6 | 107 | require_relative "sqlpkg/version" |
|
0 commit comments