Class: Arfi::Commands::FIdx

Inherits:
Thor
  • Object
show all
Defined in:
lib/arfi/commands/f_idx.rb

Overview

Arfi::Commands::FIdx module contains commands for manipulating functional index in Rails project.

Constant Summary collapse

ADAPTERS =
%i[postgresql mysql].freeze

Instance Method Summary collapse

Instance Method Details

#build_from_file(index_name) ⇒ String (private)

Arfi::Commands::FIdx#build_from_file -> String

Helper method to build the SQL function. Used with flag ‘–template`.

Parameters:

  • index_name (String)

    Name of the index.

Returns:

  • (String)

    SQL function body.

See Also:



161
162
163
164
165
# File 'lib/arfi/commands/f_idx.rb', line 161

def build_from_file(index_name)
  # steep:ignore:start
  RubyVM::InstructionSequence.compile("index_name = '#{index_name}'; #{File.read(options[:template])}").eval
  # steep:ignore:end
end

#build_sql_function(index_name) ⇒ String (private)

Arfi::Commands::FIdx#build_sql_function -> String

Helper method to build the SQL function.

Parameters:

  • index_name (String)

    Name of the index.

Returns:

  • (String)

    SQL function body.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/arfi/commands/f_idx.rb', line 112

def build_sql_function(index_name) # rubocop:disable Metrics/MethodLength
  return build_from_file(index_name) if options[:template] # steep:ignore NoMethod

  unless options[:adapter] # steep:ignore NoMethod
    return <<~SQL
      CREATE OR REPLACE FUNCTION #{index_name}() RETURNS TEXT[]
          LANGUAGE SQL
          IMMUTABLE AS
      $$
          -- Function body here
      $$
    SQL
  end

  case options[:adapter] # steep:ignore NoMethod
  when 'postgresql'
    <<~SQL
      CREATE OR REPLACE FUNCTION #{index_name}() RETURNS TEXT[]
          LANGUAGE SQL
          IMMUTABLE AS
      $$
          -- Function body here
      $$
    SQL
  when 'mysql'
    <<~SQL
      CREATE FUNCTION #{index_name} ()
      RETURNS return_type
      BEGIN
        -- Function body here
      END;
    SQL
  else
    # steep:ignore:start
    raise "Unknown adapter: #{options[:adapter]}. Supported adapters: #{ADAPTERS.join(', ')}"
    # steep:ignore:end
  end
end

#create(index_name) ⇒ void

This method returns an undefined value.

Arfi::Commands::FIdx#create -> void

This command is used to create the functional index.

ARFI also supports the use of custom templates for SQL functions, but now there are some restrictions and rules according to which it is necessary to describe the function. First, the function must be written in a Ruby-compatible syntax: the file name is not so important, but the name for the function name must be interpolated with the index_name variable name, and the function itself must be placed in the HEREDOC statement. Below is an example file.

To use a custom template, add the –template flag.

Examples:

bundle exec arfi f_idx create some_function
# ./template/my_custom_template
<<~SQL
CREATE OR REPLACE FUNCTION #{index_name}() RETURNS TEXT[]
  LANGUAGE SQL
  IMMUTABLE AS
$$
  -- Function body here
$$
SQL
bundle exec arfi f_idx create some_function --template ./template/my_custom_template

Parameters:

  • index_name (String)

    Name of the index.

Raises:

See Also:



57
58
59
60
61
# File 'lib/arfi/commands/f_idx.rb', line 57

def create(index_name)
  validate_schema_format!
  content = build_sql_function(index_name)
  create_function_file(index_name, content)
end

#create_function_file(index_name, content) ⇒ void (private)

This method returns an undefined value.

Arfi::Commands::FIdx#create_function_file -> void

Helper method to create the index file.

Parameters:

  • index_name (String)

    Name of the index.

  • content (String)

    SQL function body.



176
177
178
179
180
181
182
183
# File 'lib/arfi/commands/f_idx.rb', line 176

def create_function_file(index_name, content)
  existing_files = Dir.glob("#{functions_dir}/#{index_name}*.sql")

  return write_file(index_name, content, 1) if existing_files.empty?

  latest_version = extract_latest_version(existing_files)
  write_file(index_name, content, latest_version.succ)
end

#destroy(index_name) ⇒ void

This method returns an undefined value.

Arfi::Commands::FIdx#destroy -> void

This command is used to delete the functional index.

Examples:

bundle exec arfi f_idx destroy some_function [revision index (just an integer, 1 is by default)]

Parameters:

  • index_name (String)

    Name of the index.

Raises:



81
82
83
84
85
86
87
88
# File 'lib/arfi/commands/f_idx.rb', line 81

def destroy(index_name)
  validate_schema_format!

  revision = Integer(options[:revision] || '01') # steep:ignore NoMethod
  revision = "0#{revision}"
  FileUtils.rm("#{functions_dir}/#{index_name}_v#{revision}.sql")
  puts "Deleted: #{functions_dir}/#{index_name}_v#{revision}.sql"
end

#extract_latest_version(files) ⇒ String (private)

Arfi::Commands::FIdx#extract_latest_version -> Integer

Helper method to extract the latest version of the index.

Parameters:

  • files (Array<String>)

    List of files.

Returns:

  • (String)

    Latest version of the index.



193
194
195
196
197
198
199
# File 'lib/arfi/commands/f_idx.rb', line 193

def extract_latest_version(files)
  version_numbers = files.map do |file|
    File.basename(file)[/\w+_v(\d+)\.sql/, 1]
  end.compact

  version_numbers.max
end

#functions_dirPathname (private)

Arfi::Commands::FIdx#functions_dir -> Pathname

Helper method to get path to ‘db/functions` directory.

Returns:

  • (Pathname)

    Path to ‘db/functions` directory



225
226
227
228
229
230
231
232
233
234
235
# File 'lib/arfi/commands/f_idx.rb', line 225

def functions_dir
  # steep:ignore:start
  if options[:adapter]
    raise Arfi::Errors::AdapterNotSupported unless ADAPTERS.include?(options[:adapter].to_sym)

    Rails.root.join("db/functions/#{options[:adapter]}")
    # steep:ignore:end
  else
    Rails.root.join('db/functions')
  end
end

#validate_schema_format!nil (private)

Arfi::Commands::FIdx#validate_schema_format! -> void

Helper method to validate the schema format.

Returns:

  • (nil)

    if the schema format is valid.

Raises:



100
101
102
# File 'lib/arfi/commands/f_idx.rb', line 100

def validate_schema_format!
  raise Arfi::Errors::InvalidSchemaFormat unless ActiveRecord.schema_format == :ruby # steep:ignore NoMethod
end

#write_file(index_name, content, version) ⇒ void (private)

This method returns an undefined value.

Arfi::Commands::FIdx#write_file -> void

Helper method to write the index file.

Parameters:

  • index_name (String)

    Name of the index.

  • content (String)

    SQL function body.

  • version (String|Integer)

    Version of the index.



211
212
213
214
215
216
# File 'lib/arfi/commands/f_idx.rb', line 211

def write_file(index_name, content, version)
  version_str = format('%02d', version)
  path = "#{functions_dir}/#{index_name}_v#{version_str}.sql"
  File.write(path, content.to_s)
  puts "Created: #{path}"
end