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.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +17 -0
  5. data/Gemfile.lock +117 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.rdoc +43 -0
  8. data/Rakefile +15 -0
  9. data/bin/specify_cli +248 -0
  10. data/lib/specify.rb +45 -0
  11. data/lib/specify/branch_parser.rb +85 -0
  12. data/lib/specify/cli.rb +11 -0
  13. data/lib/specify/cli/database_setup.rb +46 -0
  14. data/lib/specify/cli/stubs.rb +63 -0
  15. data/lib/specify/cli/viewset.rb +21 -0
  16. data/lib/specify/configuration.rb +12 -0
  17. data/lib/specify/configuration/config.rb +120 -0
  18. data/lib/specify/configuration/db_config.rb +162 -0
  19. data/lib/specify/configuration/host_config.rb +37 -0
  20. data/lib/specify/database.rb +140 -0
  21. data/lib/specify/models.rb +43 -0
  22. data/lib/specify/models/accession.rb +33 -0
  23. data/lib/specify/models/agent.rb +138 -0
  24. data/lib/specify/models/app_resource_data.rb +32 -0
  25. data/lib/specify/models/app_resource_dir.rb +43 -0
  26. data/lib/specify/models/auto_numbering_scheme.rb +94 -0
  27. data/lib/specify/models/collecting_event.rb +38 -0
  28. data/lib/specify/models/collection.rb +67 -0
  29. data/lib/specify/models/collection_object.rb +127 -0
  30. data/lib/specify/models/createable.rb +21 -0
  31. data/lib/specify/models/determination.rb +63 -0
  32. data/lib/specify/models/discipline.rb +61 -0
  33. data/lib/specify/models/division.rb +26 -0
  34. data/lib/specify/models/geography.rb +5 -0
  35. data/lib/specify/models/geography/administrative_division.rb +32 -0
  36. data/lib/specify/models/geography/geographic_name.rb +66 -0
  37. data/lib/specify/models/geography/geography.rb +23 -0
  38. data/lib/specify/models/institution.rb +13 -0
  39. data/lib/specify/models/locality.rb +50 -0
  40. data/lib/specify/models/preparation.rb +53 -0
  41. data/lib/specify/models/preparation_type.rb +30 -0
  42. data/lib/specify/models/record_set.rb +55 -0
  43. data/lib/specify/models/record_set_item.rb +29 -0
  44. data/lib/specify/models/taxonomy.rb +6 -0
  45. data/lib/specify/models/taxonomy/common_name.rb +14 -0
  46. data/lib/specify/models/taxonomy/rank.rb +31 -0
  47. data/lib/specify/models/taxonomy/taxon.rb +54 -0
  48. data/lib/specify/models/taxonomy/taxonomy.rb +21 -0
  49. data/lib/specify/models/tree_queryable.rb +55 -0
  50. data/lib/specify/models/updateable.rb +20 -0
  51. data/lib/specify/models/user.rb +104 -0
  52. data/lib/specify/models/view_set_object.rb +32 -0
  53. data/lib/specify/number_format.rb +60 -0
  54. data/lib/specify/services.rb +18 -0
  55. data/lib/specify/services/service.rb +51 -0
  56. data/lib/specify/services/stub_generator.rb +291 -0
  57. data/lib/specify/services/view_loader.rb +177 -0
  58. data/lib/specify/session.rb +77 -0
  59. data/lib/specify/user_type.rb +61 -0
  60. data/lib/specify/version.rb +19 -0
  61. data/man/specify_cli-database.1 +60 -0
  62. data/man/specify_cli-database.1.html +137 -0
  63. data/man/specify_cli-database.1.ronn +53 -0
  64. data/man/specify_cli-repository.1 +55 -0
  65. data/man/specify_cli-repository.1.html +128 -0
  66. data/man/specify_cli-repository.1.ronn +42 -0
  67. data/man/specify_cli-stubs.1 +177 -0
  68. data/man/specify_cli-stubs.1.html +239 -0
  69. data/man/specify_cli-stubs.1.ronn +147 -0
  70. data/man/specify_cli-viewset.1 +92 -0
  71. data/man/specify_cli-viewset.1.html +154 -0
  72. data/man/specify_cli-viewset.1.ronn +72 -0
  73. data/man/specify_cli.1 +213 -0
  74. data/man/specify_cli.1.html +252 -0
  75. data/man/specify_cli.1.ronn +157 -0
  76. data/spec/branch_parser_spec.rb +94 -0
  77. data/spec/cli/stubs_spec.rb +44 -0
  78. data/spec/configuration/config_spec.rb +269 -0
  79. data/spec/configuration/db_config_spec.rb +299 -0
  80. data/spec/configuration/host_config_spec.rb +64 -0
  81. data/spec/database_spec.rb +83 -0
  82. data/spec/examples.txt +217 -0
  83. data/spec/helpers.rb +15 -0
  84. data/spec/models/app_resource_data_spec.rb +38 -0
  85. data/spec/models/app_resource_dir_spec.rb +8 -0
  86. data/spec/models/auto_numbering_scheme_spec.rb +78 -0
  87. data/spec/models/collection_object_spec.rb +92 -0
  88. data/spec/models/collection_spec.rb +32 -0
  89. data/spec/models/discipline_spec.rb +31 -0
  90. data/spec/models/record_set_spec.rb +18 -0
  91. data/spec/models/user_spec.rb +182 -0
  92. data/spec/models/view_set_object_spec.rb +70 -0
  93. data/spec/number_format_spec.rb +43 -0
  94. data/spec/services/stub_generator_spec.rb +635 -0
  95. data/spec/services/view_loader_spec.rb +436 -0
  96. data/spec/session_spec.rb +105 -0
  97. data/spec/spec_helper.rb +116 -0
  98. data/spec/support/db.yml +12 -0
  99. data/spec/support/stub.yaml +17 -0
  100. data/spec/support/stub_locality.yaml +19 -0
  101. data/spec/support/viewsets/paleo.views.xml +30 -0
  102. data/spec/support/viewsets/paleo.xml +30 -0
  103. data/spec/user_type_spec.rb +79 -0
  104. data/specify_cli.gemspec +27 -0
  105. data/specify_cli.rdoc +1 -0
  106. 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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'cli/database_setup'
4
+ require_relative 'cli/stubs'
5
+ require_relative 'cli/viewset'
6
+
7
+ module Specify
8
+ # The CLI module contains methods used by the command line interface.
9
+ module CLI
10
+ end
11
+ end
@@ -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