specify_cli 0.0.5
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/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +117 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +43 -0
- data/Rakefile +15 -0
- data/bin/specify_cli +248 -0
- data/lib/specify.rb +45 -0
- data/lib/specify/branch_parser.rb +85 -0
- data/lib/specify/cli.rb +11 -0
- data/lib/specify/cli/database_setup.rb +46 -0
- data/lib/specify/cli/stubs.rb +63 -0
- data/lib/specify/cli/viewset.rb +21 -0
- data/lib/specify/configuration.rb +12 -0
- data/lib/specify/configuration/config.rb +120 -0
- data/lib/specify/configuration/db_config.rb +162 -0
- data/lib/specify/configuration/host_config.rb +37 -0
- data/lib/specify/database.rb +140 -0
- data/lib/specify/models.rb +43 -0
- data/lib/specify/models/accession.rb +33 -0
- data/lib/specify/models/agent.rb +138 -0
- data/lib/specify/models/app_resource_data.rb +32 -0
- data/lib/specify/models/app_resource_dir.rb +43 -0
- data/lib/specify/models/auto_numbering_scheme.rb +94 -0
- data/lib/specify/models/collecting_event.rb +38 -0
- data/lib/specify/models/collection.rb +67 -0
- data/lib/specify/models/collection_object.rb +127 -0
- data/lib/specify/models/createable.rb +21 -0
- data/lib/specify/models/determination.rb +63 -0
- data/lib/specify/models/discipline.rb +61 -0
- data/lib/specify/models/division.rb +26 -0
- data/lib/specify/models/geography.rb +5 -0
- data/lib/specify/models/geography/administrative_division.rb +32 -0
- data/lib/specify/models/geography/geographic_name.rb +66 -0
- data/lib/specify/models/geography/geography.rb +23 -0
- data/lib/specify/models/institution.rb +13 -0
- data/lib/specify/models/locality.rb +50 -0
- data/lib/specify/models/preparation.rb +53 -0
- data/lib/specify/models/preparation_type.rb +30 -0
- data/lib/specify/models/record_set.rb +55 -0
- data/lib/specify/models/record_set_item.rb +29 -0
- data/lib/specify/models/taxonomy.rb +6 -0
- data/lib/specify/models/taxonomy/common_name.rb +14 -0
- data/lib/specify/models/taxonomy/rank.rb +31 -0
- data/lib/specify/models/taxonomy/taxon.rb +54 -0
- data/lib/specify/models/taxonomy/taxonomy.rb +21 -0
- data/lib/specify/models/tree_queryable.rb +55 -0
- data/lib/specify/models/updateable.rb +20 -0
- data/lib/specify/models/user.rb +104 -0
- data/lib/specify/models/view_set_object.rb +32 -0
- data/lib/specify/number_format.rb +60 -0
- data/lib/specify/services.rb +18 -0
- data/lib/specify/services/service.rb +51 -0
- data/lib/specify/services/stub_generator.rb +291 -0
- data/lib/specify/services/view_loader.rb +177 -0
- data/lib/specify/session.rb +77 -0
- data/lib/specify/user_type.rb +61 -0
- data/lib/specify/version.rb +19 -0
- data/man/specify_cli-database.1 +60 -0
- data/man/specify_cli-database.1.html +137 -0
- data/man/specify_cli-database.1.ronn +53 -0
- data/man/specify_cli-repository.1 +55 -0
- data/man/specify_cli-repository.1.html +128 -0
- data/man/specify_cli-repository.1.ronn +42 -0
- data/man/specify_cli-stubs.1 +177 -0
- data/man/specify_cli-stubs.1.html +239 -0
- data/man/specify_cli-stubs.1.ronn +147 -0
- data/man/specify_cli-viewset.1 +92 -0
- data/man/specify_cli-viewset.1.html +154 -0
- data/man/specify_cli-viewset.1.ronn +72 -0
- data/man/specify_cli.1 +213 -0
- data/man/specify_cli.1.html +252 -0
- data/man/specify_cli.1.ronn +157 -0
- data/spec/branch_parser_spec.rb +94 -0
- data/spec/cli/stubs_spec.rb +44 -0
- data/spec/configuration/config_spec.rb +269 -0
- data/spec/configuration/db_config_spec.rb +299 -0
- data/spec/configuration/host_config_spec.rb +64 -0
- data/spec/database_spec.rb +83 -0
- data/spec/examples.txt +217 -0
- data/spec/helpers.rb +15 -0
- data/spec/models/app_resource_data_spec.rb +38 -0
- data/spec/models/app_resource_dir_spec.rb +8 -0
- data/spec/models/auto_numbering_scheme_spec.rb +78 -0
- data/spec/models/collection_object_spec.rb +92 -0
- data/spec/models/collection_spec.rb +32 -0
- data/spec/models/discipline_spec.rb +31 -0
- data/spec/models/record_set_spec.rb +18 -0
- data/spec/models/user_spec.rb +182 -0
- data/spec/models/view_set_object_spec.rb +70 -0
- data/spec/number_format_spec.rb +43 -0
- data/spec/services/stub_generator_spec.rb +635 -0
- data/spec/services/view_loader_spec.rb +436 -0
- data/spec/session_spec.rb +105 -0
- data/spec/spec_helper.rb +116 -0
- data/spec/support/db.yml +12 -0
- data/spec/support/stub.yaml +17 -0
- data/spec/support/stub_locality.yaml +19 -0
- data/spec/support/viewsets/paleo.views.xml +30 -0
- data/spec/support/viewsets/paleo.xml +30 -0
- data/spec/user_type_spec.rb +79 -0
- data/specify_cli.gemspec +27 -0
- data/specify_cli.rdoc +1 -0
- metadata +246 -0
data/lib/specify.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# require 'specify/version.rb'
|
|
4
|
+
|
|
5
|
+
require 'date'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'io/console'
|
|
8
|
+
require 'mysql2'
|
|
9
|
+
require 'open3'
|
|
10
|
+
require 'pathname'
|
|
11
|
+
require 'psych'
|
|
12
|
+
require 'readline'
|
|
13
|
+
require 'securerandom'
|
|
14
|
+
require 'sequel'
|
|
15
|
+
|
|
16
|
+
require_relative 'specify/branch_parser'
|
|
17
|
+
require_relative 'specify/cli'
|
|
18
|
+
require_relative 'specify/configuration'
|
|
19
|
+
require_relative 'specify/database'
|
|
20
|
+
require_relative 'specify/number_format'
|
|
21
|
+
require_relative 'specify/session'
|
|
22
|
+
require_relative 'specify/user_type'
|
|
23
|
+
require_relative 'specify/services'
|
|
24
|
+
|
|
25
|
+
# FIXME: causes warnings, but is also required for call from bash script
|
|
26
|
+
# raises name error for VERSION and DESCRIPTION constants otherwise
|
|
27
|
+
require_relative 'specify/version'
|
|
28
|
+
|
|
29
|
+
# A module that provides functionaliy to manage Specify app resources.
|
|
30
|
+
module Specify
|
|
31
|
+
GIT_CURRENT_BRANCH = 'git rev-parse --abbrev-ref HEAD'
|
|
32
|
+
|
|
33
|
+
BRANCH_ERROR = 'Branch name not parsable: '
|
|
34
|
+
|
|
35
|
+
# FileError is a module that contains errors for file operations.
|
|
36
|
+
module FileError
|
|
37
|
+
VIEWS_FILE = 'Files must be .views.xml files'
|
|
38
|
+
NO_FILE = "File not found"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# LoginError is a module that contains errors for User logins.
|
|
42
|
+
module LoginError
|
|
43
|
+
INCONSISTENT_LOGIN = 'User is already logged in to a different collection'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
# BranchParsers parse the information required to set a
|
|
5
|
+
# Specify::Service::ViewLoade#target for upload of _.views.xml_ files from a
|
|
6
|
+
# string that follows the convention <tt>Database/CollectionName/level</tt>.
|
|
7
|
+
#
|
|
8
|
+
# This can be the name of a git branch of a repository residing in a folder
|
|
9
|
+
# denoting the hostname.
|
|
10
|
+
class BranchParser
|
|
11
|
+
# The name of the collection. Must be an existing
|
|
12
|
+
# Specify::Model::Collection#name.
|
|
13
|
+
attr_reader :collection
|
|
14
|
+
|
|
15
|
+
# A Specify::Configuration::HostConfig.
|
|
16
|
+
attr_reader :config
|
|
17
|
+
|
|
18
|
+
# The name of a _Specify_ database.
|
|
19
|
+
attr_reader :database
|
|
20
|
+
|
|
21
|
+
# The name of a MySQL/MariaDB host.
|
|
22
|
+
attr_reader :host
|
|
23
|
+
|
|
24
|
+
# The name of a _Specify_ user (an existing Specify::Model::User#name).
|
|
25
|
+
attr_reader :user
|
|
26
|
+
|
|
27
|
+
# Creates a new instance of BranchParser for the current Git _HEAD_.
|
|
28
|
+
#
|
|
29
|
+
# +config+: a database configuration YAML file.
|
|
30
|
+
def self.current_branch(config)
|
|
31
|
+
stdout_str, stderr_str, status = Open3.capture3(GIT_CURRENT_BRANCH)
|
|
32
|
+
unless status.exitstatus.zero?
|
|
33
|
+
STDERR.puts "There was an error running #{GIT_CURRENT_BRANCH}"
|
|
34
|
+
STDERR.puts stderr_str
|
|
35
|
+
exit 1
|
|
36
|
+
end
|
|
37
|
+
branch = stdout_str.chomp
|
|
38
|
+
new(Dir.pwd, branch, config)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns a new BranchParser with +view_file_path+ and +name+.
|
|
42
|
+
#
|
|
43
|
+
# +view_file_path+: the directory path of the _.vews.xml_ file (that path
|
|
44
|
+
# must be mapped to a host name in the +config+).
|
|
45
|
+
#
|
|
46
|
+
# +name+: a String with a branch name conforming to the convention
|
|
47
|
+
# <tt>Database/CollectionName/level</tt>.
|
|
48
|
+
#
|
|
49
|
+
# +config+: a database configuration YAML file.
|
|
50
|
+
def initialize(view_file_path, name, config = nil)
|
|
51
|
+
@config = Configuration::HostConfig.new(config)
|
|
52
|
+
@database, collection, @level, @user = *name.split('/')
|
|
53
|
+
raise ArgumentError, BRANCH_ERROR + name unless collection && level
|
|
54
|
+
@host = @config.resolve_host view_file_path
|
|
55
|
+
@collection = normalize_name collection
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns the attributes of +self+ as a hash.
|
|
59
|
+
def to_h
|
|
60
|
+
{ host: host,
|
|
61
|
+
database: database,
|
|
62
|
+
collection: collection,
|
|
63
|
+
level: level }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns the level to a Specify::Service::ViewLoader will upload.
|
|
67
|
+
def level
|
|
68
|
+
case @level
|
|
69
|
+
when 'collection', 'discipline'
|
|
70
|
+
@level.to_sym
|
|
71
|
+
when 'user'
|
|
72
|
+
{ user: @user }
|
|
73
|
+
else
|
|
74
|
+
{ user_type: @level.downcase.to_sym }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def normalize_name(name)
|
|
81
|
+
name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1 \2')
|
|
82
|
+
.gsub(/([a-z\d])([A-Z])/, '\1 \2')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/specify/cli.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
module CLI
|
|
5
|
+
# Asks the user to configure a database.
|
|
6
|
+
def self.configure_database(config)
|
|
7
|
+
STDERR.puts "Configuring new database: #{config.database}"
|
|
8
|
+
config.user_name = require_input 'MySQL user name'
|
|
9
|
+
config.user_password = require_input 'password (blank for prompt)'
|
|
10
|
+
config.session_user = require_input 'Specify user (leave blank to skip)'
|
|
11
|
+
config.save
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Asks the user to configure a host.
|
|
15
|
+
def self.configure_host(config)
|
|
16
|
+
return unless proceed? "host #{config.host} not known"
|
|
17
|
+
config.port = require_input 'port number (leave blank for default)'
|
|
18
|
+
config.save
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Creates a new database configuratin YAML file.
|
|
22
|
+
def self.db_config!(file, global_options)
|
|
23
|
+
return if File.exist?(global_options[:db_config])
|
|
24
|
+
STDERR.puts "Creating new config file #{file}"
|
|
25
|
+
Specify::Configuration::Config.empty file do |config|
|
|
26
|
+
config.add_host global_options[:host], global_options[:port]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Asks the user to proceed. Returns +true+ if the user answers answers with
|
|
31
|
+
# 'Yes'.
|
|
32
|
+
def self.proceed?(message)
|
|
33
|
+
STDERR.puts message
|
|
34
|
+
STDERR.print "Configure? (Y/n)"
|
|
35
|
+
return true if /^[Yy](es)?/.match Readline.readline(': ')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Prompts the user for input with +message+.
|
|
39
|
+
def self.require_input(message)
|
|
40
|
+
STDERR.print message
|
|
41
|
+
answer = Readline.readline(': ')
|
|
42
|
+
return if answer.empty?
|
|
43
|
+
answer
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
module CLI
|
|
5
|
+
# Transforms +arg+ (a String passed as a command line argument) containing
|
|
6
|
+
# hierarchical information (such as geographic or taxonomic) into a
|
|
7
|
+
# structured Hash that can be used to set the
|
|
8
|
+
# Specify::Service::StubGenerator#collecting_data= or
|
|
9
|
+
# Specify::Service::StubGenerator#determination=.
|
|
10
|
+
def self.arg_to_hash(arg)
|
|
11
|
+
return unless arg
|
|
12
|
+
arg.split(';')
|
|
13
|
+
.map { |pair| pair.split(':').map(&:strip) }
|
|
14
|
+
.to_h
|
|
15
|
+
.transform_keys { |key| key == 'locality' ? key.to_sym : key }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Creates stub records with +generator+ (a Specify::Service::StubGenerator).
|
|
19
|
+
#
|
|
20
|
+
# +count+: the number of stub records to be created.
|
|
21
|
+
def self.make_stubs(generator, count)
|
|
22
|
+
STDERR.puts "started creating #{count} records"
|
|
23
|
+
STDERR.puts "cataloger: #{generator.cataloger}"
|
|
24
|
+
generator.database.transaction do
|
|
25
|
+
generator.create count
|
|
26
|
+
STDERR.puts "creating: #{generator.generated.last.catalog_number}"
|
|
27
|
+
end
|
|
28
|
+
STDERR.puts 'done'
|
|
29
|
+
puts "generated #{generator.generated.count} catalog numbers:"
|
|
30
|
+
puts '--------------------------'
|
|
31
|
+
generator.generated.each { |co| puts co.CatalogNumber }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns a Hash for intialization of a Specify::Service::StubGenerator
|
|
35
|
+
# from +global_options+, +args+, and command +options+.
|
|
36
|
+
def self.wrap_args(global_options, args, options)
|
|
37
|
+
params = {}
|
|
38
|
+
stub_generator = {}
|
|
39
|
+
stub_generator[:host] = global_options[:host]
|
|
40
|
+
stub_generator[:database] = global_options[:database]
|
|
41
|
+
stub_generator[:collection] = args.shift
|
|
42
|
+
stub_generator[:specify_user] = global_options[:specify_user]
|
|
43
|
+
stub_generator[:config] = global_options[:db_config]
|
|
44
|
+
params[:stub_generator] = stub_generator
|
|
45
|
+
params.merge stub_parameters(options)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Parses the parameters for stub records to created by a
|
|
49
|
+
# Specify::Service::StubGenerator from the command +options+.
|
|
50
|
+
def self.stub_parameters(options)
|
|
51
|
+
params = { 'dataset_name' => options[:dataset],
|
|
52
|
+
'cataloger' => options[:cataloger],
|
|
53
|
+
'accession' => options[:accession],
|
|
54
|
+
'collecting_data' => arg_to_hash(options[:geography]),
|
|
55
|
+
'default_locality_name' => options[:locality],
|
|
56
|
+
'determination' => arg_to_hash(options[:taxon]) }
|
|
57
|
+
return params unless options[:preptype]
|
|
58
|
+
params['preparation'] = { type: options[:preptype],
|
|
59
|
+
count: options[:prepcount] }
|
|
60
|
+
params.compact
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
module CLI
|
|
5
|
+
# Parses the _level_ for the Specify::Service::ViewLoader to upload
|
|
6
|
+
# _.vioews.xml_ files to from the command +options+.
|
|
7
|
+
def self.level(options)
|
|
8
|
+
if options[:d]
|
|
9
|
+
:discipline
|
|
10
|
+
elsif options[:c]
|
|
11
|
+
:collection
|
|
12
|
+
elsif options[:t]
|
|
13
|
+
{ user_type: options[:t] }
|
|
14
|
+
elsif options[:u]
|
|
15
|
+
{ user: options[:u] }
|
|
16
|
+
else
|
|
17
|
+
raise 'level required (use -d, -c, -t, or -u option)'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'configuration/config'
|
|
4
|
+
require_relative 'configuration/db_config'
|
|
5
|
+
require_relative 'configuration/host_config'
|
|
6
|
+
|
|
7
|
+
module Specify
|
|
8
|
+
# Configuration is a module that contains classes that provide configuration
|
|
9
|
+
# facilities.
|
|
10
|
+
module Configuration
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
module Configuration
|
|
5
|
+
# Configurations wrap a database configuaratin file (_.rc.yaml_ file).
|
|
6
|
+
#
|
|
7
|
+
# Configuration is the superclass of the DBConfig and HostConfig classes.
|
|
8
|
+
class Config
|
|
9
|
+
# A Hash containing the directory-host-mapping parameters for the
|
|
10
|
+
# HostConfig subclass.
|
|
11
|
+
attr_reader :dir_names
|
|
12
|
+
|
|
13
|
+
# A Hash containing the database parameters for the DBConfig subclass.
|
|
14
|
+
attr_reader :hosts
|
|
15
|
+
|
|
16
|
+
# Returns a new empty Config for +file+ that can serve as a template.
|
|
17
|
+
#
|
|
18
|
+
# +file+: the YAML file containg the configuration
|
|
19
|
+
def self.empty(file, &block)
|
|
20
|
+
if File.exist?(file)
|
|
21
|
+
raise "#{file} exists, won't overwrite"
|
|
22
|
+
end
|
|
23
|
+
config = new file, dir_names: {}, hosts: {}, &block
|
|
24
|
+
config.save
|
|
25
|
+
config
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns a new Config for +file+ (a YAML file containg the
|
|
29
|
+
# configuration).
|
|
30
|
+
#
|
|
31
|
+
# <tt>dir_names</tt>: a Hash with directory names as keys, host names as
|
|
32
|
+
# values.
|
|
33
|
+
#
|
|
34
|
+
# +hosts+: a Hash with host configurations. The hash should have the
|
|
35
|
+
# structure:
|
|
36
|
+
# {
|
|
37
|
+
# :hosts => {
|
|
38
|
+
# 'hostname' => {
|
|
39
|
+
# :port => Integer,
|
|
40
|
+
# :databases => {
|
|
41
|
+
# 'database name' => {
|
|
42
|
+
# :db_user => {
|
|
43
|
+
# :name => 'mysql_user_name',
|
|
44
|
+
# :password => 'password'
|
|
45
|
+
# },
|
|
46
|
+
# :sp_user => 'specify_user_name'
|
|
47
|
+
# }
|
|
48
|
+
# }
|
|
49
|
+
# }
|
|
50
|
+
# }
|
|
51
|
+
# }
|
|
52
|
+
# Leave +:password+ out to be prompted.
|
|
53
|
+
def initialize(file = nil, dir_names: nil, hosts: nil)
|
|
54
|
+
@file = Pathname.new(file)
|
|
55
|
+
if dir_names || hosts
|
|
56
|
+
@dir_names = dir_names
|
|
57
|
+
@hosts = hosts
|
|
58
|
+
@params = { dir_names: dir_names, hosts: hosts }
|
|
59
|
+
else
|
|
60
|
+
@params = Psych.load_file(@file)
|
|
61
|
+
@dir_names = @params[:dir_names]
|
|
62
|
+
@hosts = @params[:hosts]
|
|
63
|
+
end
|
|
64
|
+
yield(self) if block_given?
|
|
65
|
+
@saved = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds a configuration for the database with +name+ to the +host+
|
|
69
|
+
# configuration.
|
|
70
|
+
def add_database(name, host:)
|
|
71
|
+
add_host(host) unless hosts[host]
|
|
72
|
+
if hosts.dig host, :databases, name
|
|
73
|
+
raise "Database '#{name}' on '#{host}' already configured"
|
|
74
|
+
end
|
|
75
|
+
db = hosts[host][:databases][name] = db_template
|
|
76
|
+
yield(db) if block_given?
|
|
77
|
+
@saved = false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Adds a configuration for the host with +name+.
|
|
81
|
+
def add_host(name, port = nil)
|
|
82
|
+
raise "Host '#{name}' already configured" if hosts[name]
|
|
83
|
+
hosts[name] = { port: port, databases: {} }
|
|
84
|
+
@saved = false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns a Hash with the contents of the configuration YAML file.
|
|
88
|
+
def params
|
|
89
|
+
{ dir_names: @dir_names, hosts: @hosts }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Saves the current state to the YAML configuration file.
|
|
93
|
+
def save
|
|
94
|
+
File.open(@file, 'w') do |file|
|
|
95
|
+
file.write(Psych.dump(@params))
|
|
96
|
+
end
|
|
97
|
+
@saved = true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns +false+ if the instance has been modified since the last save.
|
|
101
|
+
def saved?
|
|
102
|
+
@saved
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Marks the instance as modified.
|
|
106
|
+
def touch
|
|
107
|
+
@saved = false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def db_template
|
|
113
|
+
{
|
|
114
|
+
db_user: { name: nil, password: nil },
|
|
115
|
+
sp_user: nil
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Specify
|
|
4
|
+
module Configuration
|
|
5
|
+
# DBConfigs are Specify:Database configurations.
|
|
6
|
+
class DBConfig < Config
|
|
7
|
+
# The name of the _Specify_ database.
|
|
8
|
+
attr_reader :database
|
|
9
|
+
|
|
10
|
+
# The name of the MySQL/MariaDB host for the _Specify_ database.
|
|
11
|
+
attr_reader :host
|
|
12
|
+
|
|
13
|
+
# The port for the MySQL/MariaDB server for the _Specify_ database.
|
|
14
|
+
attr_reader :port
|
|
15
|
+
|
|
16
|
+
# The MySQL/MariaDB user for the database. This is typically the _Specify_
|
|
17
|
+
# <em>master user</em>.
|
|
18
|
+
attr_reader :user_name
|
|
19
|
+
|
|
20
|
+
# An existing Specify::Model::User#name; the name of the
|
|
21
|
+
# Specify::Model::User that is logged in to #collection during a Session.
|
|
22
|
+
attr_reader :session_user
|
|
23
|
+
|
|
24
|
+
# Returns a new DBConfig for +database+ on +host+
|
|
25
|
+
#
|
|
26
|
+
# _file_: the YAML file (path) containg the configuration.
|
|
27
|
+
def initialize(host, database, file = nil)
|
|
28
|
+
super(file)
|
|
29
|
+
@host = host
|
|
30
|
+
@database = database
|
|
31
|
+
@port = hosts.dig @host, :port
|
|
32
|
+
@user_name = params&.dig :db_user, :name
|
|
33
|
+
@user_password = params&.dig :db_user, :password
|
|
34
|
+
@session_user = params&.fetch :sp_user, nil
|
|
35
|
+
@saved = known? ? true : false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the connection paramaters for the database as a Hash.
|
|
39
|
+
def connection
|
|
40
|
+
raise "#{database} on #{host} not configured" unless known?
|
|
41
|
+
{ host: host,
|
|
42
|
+
port: port || 3306,
|
|
43
|
+
user: user_name,
|
|
44
|
+
password: @user_password }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns +true+ if #user_name differs from the user +:name+ in the
|
|
48
|
+
# #params of the YAML file.
|
|
49
|
+
def changed_user?
|
|
50
|
+
params[:db_user][:name] != user_name
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns +true+ if the <em>user_password</em> attribute differs from the
|
|
54
|
+
# +:password+ in the #params of the YAML file.
|
|
55
|
+
def changed_password?
|
|
56
|
+
params[:db_user][:password] != @user_password
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns +true+ if #port differs from the +:port+ in the #params of the
|
|
60
|
+
# YAML file.
|
|
61
|
+
def changed_port?
|
|
62
|
+
hosts[host][:port] != port
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns +true+ if the #session_user differs from the <tt>:so_user</tt>
|
|
66
|
+
# in the #params of the YAML file.
|
|
67
|
+
def changed_session_user?
|
|
68
|
+
params[:sp_user] != session_user
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Sets the #database.
|
|
72
|
+
def database=(name)
|
|
73
|
+
@database = name
|
|
74
|
+
touch
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns a Hash with the MySQL/MariaDB user name and password.
|
|
78
|
+
def db_user
|
|
79
|
+
{ name: @user_name, password: @user_password }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Sets the #host
|
|
83
|
+
def host=(name)
|
|
84
|
+
@host = name
|
|
85
|
+
touch
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns +true+ if #host is known (has been configured), +false+
|
|
89
|
+
# otherwise.
|
|
90
|
+
def host?
|
|
91
|
+
hosts[host]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns +true+ if #database is known (has been configured), +false+
|
|
95
|
+
# otherwise.
|
|
96
|
+
def known?
|
|
97
|
+
params ? true : false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns a Hash with the parameters for the #host #database from the
|
|
101
|
+
# configuration YAML file.
|
|
102
|
+
def params
|
|
103
|
+
super.dig:hosts, @host, :databases, @database
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Sets the #port
|
|
107
|
+
def port=(number)
|
|
108
|
+
@port = number&.to_i
|
|
109
|
+
raise ArgumentError, "invalid port number: #{number}" unless port_valid?
|
|
110
|
+
touch
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Saves the current state to the YAML file.
|
|
114
|
+
def save
|
|
115
|
+
return true if saved?
|
|
116
|
+
host? ? update_host : save_new_host
|
|
117
|
+
super
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Sets #session_user.
|
|
121
|
+
def session_user=(name)
|
|
122
|
+
@session_user = name
|
|
123
|
+
touch
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Sets the #user_name.
|
|
127
|
+
def user_name=(name)
|
|
128
|
+
@user_name = name
|
|
129
|
+
touch
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Sets the MySQL/MariaDB <em>user_password</em>.
|
|
133
|
+
def user_password=(password)
|
|
134
|
+
@user_password = password
|
|
135
|
+
touch
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def save_new_host
|
|
141
|
+
add_host host, port
|
|
142
|
+
add_database database, host: host
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def update_database
|
|
146
|
+
params[:db_user][:name] = user_name if changed_user?
|
|
147
|
+
params[:db_user][:password] = @user_password if changed_password?
|
|
148
|
+
params[:sp_user] = session_user if changed_session_user?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def update_host
|
|
152
|
+
hosts[host][:port] = port if changed_port?
|
|
153
|
+
add_database database, host: host unless known?
|
|
154
|
+
update_database
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def port_valid?
|
|
158
|
+
port.nil? || port.to_i.positive?
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|