cli_application 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/LICENSE.txt +21 -0
- data/README.md +480 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/cli_application/app.rb +267 -0
- data/lib/cli_application/argv.rb +152 -0
- data/lib/cli_application/config.rb +82 -0
- data/lib/cli_application/databases.rb +39 -0
- data/lib/cli_application/includes.rb +21 -0
- data/lib/cli_application/json_struct.rb +48 -0
- data/lib/cli_application/stat.rb +170 -0
- data/lib/cli_application/version.rb +3 -0
- data/lib/cli_application.rb +21 -0
- data/test/cli_example.rb +25 -0
- data/test/examples/1/app.rb +26 -0
- data/test/examples/1/cli_example.rb +14 -0
- data/test/examples/1/stat/app.yml +33 -0
- data/test/examples/2/app.rb +28 -0
- data/test/examples/3/app.rb +25 -0
- data/test/examples/4/app.rb +49 -0
- data/test/examples/5/app.rb +33 -0
- data/test/examples/6/app.rb +31 -0
- data/test/examples/6/offer.rb +5 -0
- data/test/stat/rdebug-ide +21 -0
- data/test/stat/test_app.yml +29 -0
- data/test/test_app.rb +60 -0
- data/test/tovar.rb +5 -0
- metadata +145 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
# CliApplication::App - основной класс - каркас CLI-приложений. Класс обеспечивает контроль
|
2
|
+
# аргументов командной строки, управление конфигами и подключениями к базе данных.
|
3
|
+
|
4
|
+
module CliApplication
|
5
|
+
class App
|
6
|
+
# Ссылка на класс, который содержит аргменты командной строки или значения по умолчанию
|
7
|
+
attr_reader :argv
|
8
|
+
# Код завершения приложения. Может быть использован в Bash-скриптах
|
9
|
+
attr_reader :exitcode
|
10
|
+
# Ссылка на массив, содержащий список директорий в которых исполняется приложение.
|
11
|
+
# Основные: folders[:app] - папка из которой запущено приложение, folders[:class] - папка,
|
12
|
+
# в которой хранится базовый класс проекта.
|
13
|
+
attr_reader :folders
|
14
|
+
# Ссылка на класс конфигурации приложения
|
15
|
+
attr_reader :config
|
16
|
+
# Строка - версия приложения
|
17
|
+
attr_reader :version
|
18
|
+
# Строка - описание приложения
|
19
|
+
attr_reader :description
|
20
|
+
# Строка - краткое описание (назначение) приложения
|
21
|
+
attr_reader :shortdescription
|
22
|
+
# Строка - дата релиза ПО
|
23
|
+
attr_reader :releasedate
|
24
|
+
# Структура, содержащая конфигурации баз данных
|
25
|
+
attr_reader :databases
|
26
|
+
# Строка-шаблон, вывод которой происходит после завершения работы приложения
|
27
|
+
attr_accessor :footer
|
28
|
+
|
29
|
+
# Конструктор экземпляра приложения
|
30
|
+
#
|
31
|
+
# @param [Array] argv аргументы командной строки
|
32
|
+
# @param [String] appfolder директория, из которой запущено приложение
|
33
|
+
# @param [String] classfolder директория, в которой расположен базовый класс проекта
|
34
|
+
# @param [Sym] lang язык работы приложения (реализовано не полностью)
|
35
|
+
def initialize(argv, appfolder, classfolder, lang = :ru)
|
36
|
+
::StTools::Setup.setup(lang)
|
37
|
+
|
38
|
+
@folders = Hash.new
|
39
|
+
@folders[:app] = appfolder
|
40
|
+
@folders[:class] = classfolder
|
41
|
+
|
42
|
+
@argv = ::CliApplication::Argv.new(argv)
|
43
|
+
@stat = ::CliApplication::Stat.new(@folders)
|
44
|
+
@config = ::CliApplication::Config.new(@folders)
|
45
|
+
|
46
|
+
@databases = ::CliApplication::Databases.new(config.cli.databases)
|
47
|
+
|
48
|
+
@footer = nil
|
49
|
+
|
50
|
+
init_app
|
51
|
+
end
|
52
|
+
|
53
|
+
#-------------------------------------------------------------
|
54
|
+
#
|
55
|
+
# Функции для использования внутри функции main
|
56
|
+
#
|
57
|
+
#-------------------------------------------------------------
|
58
|
+
|
59
|
+
# Метод возвращает папку из которой запущено приложение или расположен базовый класс.
|
60
|
+
# Базовый класс обычно располагается в фиксированном месте, например, в папке cli корня проекта. Соответственно,
|
61
|
+
# если вызвать File.dirname(app.folder(:class)), то можно будет узнать корневую папку проекта
|
62
|
+
#
|
63
|
+
# @param [Sym] type тип возвращаемой папки
|
64
|
+
# @option type [Sym] :app папка, из которой запущено приложение (по умолчанию)
|
65
|
+
# @option type [Sym] :class папка, в которой хранится базовый класс
|
66
|
+
# @option type [Sym] :stat папка, в которой хранится статистика по приложению
|
67
|
+
# @return [String] папка, из которой запущено приложение или расположен базовый класс
|
68
|
+
def folder(type = :app)
|
69
|
+
warn "Предупреждение: тип папки '#{type.inspect}' неизвестен (допустимо #{@folders.keys.inspect})" unless @folders.keys.include?(type)
|
70
|
+
@folders[type]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Метод загружает конфиг и делает его доступным через единый интерфейс настроек конфигурации приложения (CliApplication::Config)
|
74
|
+
# При каждом вызове данного метода все конфиги перечитываются заново.
|
75
|
+
#
|
76
|
+
# @param [Sym] type параметр используется для указания местоположения конфига. Если указано :app или :class,
|
77
|
+
# то имя файла с конфигом будет дополнено папкой класса или приложения
|
78
|
+
# @option type [Sym] :app папка, из которой запущено приложение
|
79
|
+
# @option type [Sym] :class папка, в которой хранится базовый класс
|
80
|
+
# @option type [Sym] :absolute указывает на необходимость брать имя файла как задано разработчиком
|
81
|
+
# @return [Nil] нет
|
82
|
+
def add_config(filename, type)
|
83
|
+
@config.add(filename, type)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Метод возвращает имя приложения
|
87
|
+
#
|
88
|
+
# @return [String] имя приложения без параметров командной строки и пути
|
89
|
+
def exename
|
90
|
+
::StTools::System.exename
|
91
|
+
end
|
92
|
+
|
93
|
+
# Метод возвращает число секунд в формате Float с момента запуска приложения. В основном используется для показа
|
94
|
+
# времени выполнения приложения, но может быть вызван в любой момент из любого места приложения.
|
95
|
+
#
|
96
|
+
# @return [Float] число секунд с момента запуска приложения
|
97
|
+
# @example Примеры использования
|
98
|
+
# puts "С момента запуска прошло #{executed_at} сек." #=> "С момента запуска прошло 23.456435 сек."
|
99
|
+
def executed_at
|
100
|
+
@executed_at = (::Time.now - @started_at).to_f
|
101
|
+
end
|
102
|
+
|
103
|
+
# Метод устанавливает код, с которым будет завершена работа приложения.
|
104
|
+
#
|
105
|
+
# @param [Integer] code код завершения приложения, который будет передан в операционную систему (bash)
|
106
|
+
def exitcode=(code)
|
107
|
+
@exitcode = code
|
108
|
+
@stat.exitcode = code
|
109
|
+
end
|
110
|
+
|
111
|
+
# Метод устанавливает текущую версию приложения, которая потом отобразится в файле статистики
|
112
|
+
#
|
113
|
+
# @param [Integer] val строка с версией приложения
|
114
|
+
# @example Примеры использования
|
115
|
+
# app = CliApplication.new(ARGV, __dir__)
|
116
|
+
# app.version = '2.1'
|
117
|
+
def version=(val)
|
118
|
+
@version = val
|
119
|
+
@stat.version = val
|
120
|
+
end
|
121
|
+
|
122
|
+
# Метод устанавливает описание приложения, которое будет выведено при старте скрипта. Данный метод используется
|
123
|
+
# для формирования подсказок пользователю.
|
124
|
+
#
|
125
|
+
# @param [String] val строка с описанием приложения
|
126
|
+
# @example Примеры использования
|
127
|
+
# app = CliApplication.new(ARGV, __dir__)
|
128
|
+
# app.description = 'Данное приложение обеспечивает.... (c) .... и т.д.'
|
129
|
+
def description=(val)
|
130
|
+
@description = val
|
131
|
+
@stat.description = val
|
132
|
+
end
|
133
|
+
|
134
|
+
# Метод устанавливает краткое описание приложения, которое будет выведено при старте скрипта, а также
|
135
|
+
# отображено в файле статистики.
|
136
|
+
#
|
137
|
+
# @param [String] val строка с кратким описанием приложения
|
138
|
+
# @example Примеры использования
|
139
|
+
# app = CliApplication.new(ARGV, __dir__)
|
140
|
+
# app.shortdescription = 'Утилита форматирования диска'
|
141
|
+
def shortdescription=(val)
|
142
|
+
@shortdescription = val
|
143
|
+
@stat.shortdescription = val
|
144
|
+
end
|
145
|
+
|
146
|
+
# Метод устанавливает дату последнего изменения (выпуска) приложения. Используется в справочных целях
|
147
|
+
#
|
148
|
+
# @param [String] val строка датой релиза (выпуска) приложения
|
149
|
+
# @example Примеры использования
|
150
|
+
# app = CliApplication.new(ARGV, __dir__)
|
151
|
+
# app.releasedate = '2015-05-11'
|
152
|
+
def releasedate=(val)
|
153
|
+
@releasedate = val
|
154
|
+
@stat.releasedate = val
|
155
|
+
end
|
156
|
+
|
157
|
+
# Метод предназначен для подключения файлов-моделей ActiveRecords. Архитектура CLI-приложения, учитывающая
|
158
|
+
# совместимость с Rails-проектами, требует загрузки моделей после чтения файлов конфигурации и, соответственно,
|
159
|
+
# иницииации класса приложения. Поэтому объявить require файлов моделей в начале файла не получится, будут
|
160
|
+
# выводится ошибки инициализации базы данных.
|
161
|
+
#
|
162
|
+
# @example Примеры использования
|
163
|
+
# def init_active_records
|
164
|
+
# require 'offers.rb'
|
165
|
+
# require 'params.rb'
|
166
|
+
# require 'categories.rb'
|
167
|
+
# end
|
168
|
+
def init_active_records
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
# Метод инициализации приложения. Может быть переписан с обязательным вызовом функции super
|
173
|
+
#
|
174
|
+
# @example Примеры использования
|
175
|
+
# def init_app
|
176
|
+
# super
|
177
|
+
#
|
178
|
+
# # Код своего приложения
|
179
|
+
# end
|
180
|
+
def init_app
|
181
|
+
@stat.last_started_at = ::Time.zone.now
|
182
|
+
@started_at = ::Time.now
|
183
|
+
@exitcode = 0
|
184
|
+
|
185
|
+
init_active_records
|
186
|
+
end
|
187
|
+
|
188
|
+
# Метод добавления аргумента командной строки. Вызывается при инициализации приложения, служит для определения списка
|
189
|
+
# аргументов командной строки, формирвоания подсказок и установки значения по умолчанию. В классе принят не традиционный
|
190
|
+
# для Linux формат командной строки. Пример вызова: add_city.rb user_id=123 name=Максим city='Верхние Луки'.
|
191
|
+
#
|
192
|
+
# Параметры, добавленные данным методом доступны через переменную argv (см. примеры)
|
193
|
+
#
|
194
|
+
# @param [Sym] action параметр определяет действие, которое надо произвести над параметром командной строки.
|
195
|
+
# @param [String] key название ключа, напрмиер 'user_id', 'name', 'city'.
|
196
|
+
# @param [Object] default значение по умочланию, "подставляемое" при отсутствии заданного пользователем параметра
|
197
|
+
# @param [String] description описание параметра (подсказка)
|
198
|
+
#
|
199
|
+
# @example Примеры использования
|
200
|
+
# app = CliApplication.new(ARGV, __dir__)
|
201
|
+
# app.set_argv(:integer, 'user_id', 0, 'Идентификатор пользователя')
|
202
|
+
# app.set_argv(:string, 'name', 'Без имени', 'Имя пользователя')
|
203
|
+
# app.set_argv(:caps, 'city', 'москВА', 'Город проживания пользователя')
|
204
|
+
#
|
205
|
+
# def main
|
206
|
+
# puts argv.user_id #=> 0
|
207
|
+
# puts argv.name #=> 'Без имени'
|
208
|
+
# puts argv.city #=> 'Москва'
|
209
|
+
# end
|
210
|
+
def set_argv(action, key, default, description)
|
211
|
+
@argv.set_argv(action, key, default, description)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Основной метод, в котором должен быть размещен код приложения
|
215
|
+
# @return [Integer] метод должен возвращать код, который будет транслирован в параметр exitcode
|
216
|
+
def main
|
217
|
+
warn "ПРЕДУПРЕЖДЕНИЕ: необходимо переопределить функцию 'main' в вашем коде"
|
218
|
+
255
|
219
|
+
end
|
220
|
+
|
221
|
+
# При вызове данного метода начнется выполнение кода приложения (будет осуществен вызов функции main)
|
222
|
+
def run
|
223
|
+
self.exitcode = main || 255
|
224
|
+
self.executed_at = (::Time.now - @started_at).to_f
|
225
|
+
puts_footer
|
226
|
+
@stat.save
|
227
|
+
end
|
228
|
+
|
229
|
+
# Метод отображает на экране информацию о приложении (версия, дата последнего запуска, дата релиза, и пр.)
|
230
|
+
# @param [Syn] type при указании :full выводится полное описание, при других значениях не выводится
|
231
|
+
# подсказка по аргументам командной строки
|
232
|
+
def help(type = :full)
|
233
|
+
last_started_at_human = @stat.last_started_at_human
|
234
|
+
|
235
|
+
puts ::StTools::System.exename + ' - ' + @shortdescription
|
236
|
+
puts "Версия #{@version} (#{@releasedate})"
|
237
|
+
puts last_started_at_human
|
238
|
+
puts @stat.startes_human
|
239
|
+
puts
|
240
|
+
puts @description
|
241
|
+
|
242
|
+
if type == :full
|
243
|
+
@argv.help
|
244
|
+
puts
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
|
252
|
+
def puts_footer
|
253
|
+
return if @footer.nil?
|
254
|
+
line = footer.gsub('{executed_at}', executed_at.to_s)
|
255
|
+
line.gsub!('{memory}', StTools::Human.memory)
|
256
|
+
line.gsub!('{exitcode}', @exitcode.to_s)
|
257
|
+
line.gsub!('{status}', (@exitcode == 0 ? 'SUCCESS' : 'FAIL'))
|
258
|
+
puts line
|
259
|
+
end
|
260
|
+
|
261
|
+
def executed_at=(at)
|
262
|
+
@executed_at = at
|
263
|
+
@stat.executed_at = at
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Данный класс обеспечивает управление аргументами командной строки
|
2
|
+
|
3
|
+
module CliApplication
|
4
|
+
class Argv < OpenStruct
|
5
|
+
|
6
|
+
# Конструктор. Вызывается при создании класса приложения. Данный класс доступен
|
7
|
+
# в главной функции приложения (main) через переменную argv
|
8
|
+
#
|
9
|
+
# @param [Array] argv аргументы командной строки, введенные пользователем
|
10
|
+
# @example Примеры использования
|
11
|
+
# puts argv.city #=> 'Москва'
|
12
|
+
def initialize(argv)
|
13
|
+
@params = Hash.new
|
14
|
+
@full = Hash.new
|
15
|
+
|
16
|
+
argv.each do |one|
|
17
|
+
if one.match(/[a-z\_0-9]\=/i)
|
18
|
+
pair = one.split('=')
|
19
|
+
@params[pair.first.to_s.strip.downcase.to_sym] = pair.last
|
20
|
+
else
|
21
|
+
warn "WARNING: некорректный ключ параметра командной строки: #{one.inspect} (#{File.basename(__FILE__)} at #{__LINE__})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
super(@params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Метод добавления аргумента командной строки. Вызывается при инициализации приложения, служит для определения списка
|
28
|
+
# аргументов командной строки, формирвоания подсказок и установки значения по умолчанию. В классе принят не традиционный
|
29
|
+
# для Linux формат командной строки. Пример вызова: add_city.rb user_id=123 name=Максим city='Верхние Луки'.
|
30
|
+
#
|
31
|
+
# Параметры, добавленные данным методом доступны через переменную argv (см. примеры)
|
32
|
+
#
|
33
|
+
# @param [Sym] action параметр определяет действие, которое надо произвести над параметром командной строки.
|
34
|
+
# @param [String] key название ключа, напрмиер 'user_id', 'name', 'city'.
|
35
|
+
# @param [Object] default значение по умочланию, "подставляемое" при отсутствии заданного пользователем параметра
|
36
|
+
# @param [String] description описание параметра (подсказка)
|
37
|
+
#
|
38
|
+
# @example Примеры использования
|
39
|
+
# app = CliApplication.new(ARGV, __dir__)
|
40
|
+
# app.set_argv(:integer, 'user_id', 0, 'Идентификатор пользователя')
|
41
|
+
# app.set_argv(:string, 'name', 'Без имени', 'Имя пользователя')
|
42
|
+
# app.set_argv(:caps, 'city', 'москВА', 'Город проживания пользователя')
|
43
|
+
#
|
44
|
+
# def main
|
45
|
+
# puts argv.user_id #=> 0
|
46
|
+
# puts argv.name #=> 'Без имени'
|
47
|
+
# puts argv.city #=> 'Москва'
|
48
|
+
# end
|
49
|
+
def set_argv(action, key, default, description)
|
50
|
+
key = key.downcase.strip.to_sym
|
51
|
+
unless @params.keys.include?(key)
|
52
|
+
@params[key] = default
|
53
|
+
end
|
54
|
+
|
55
|
+
case action
|
56
|
+
when :bool, :boolean
|
57
|
+
@params[key] = @params[key].to_s.to_bool
|
58
|
+
when :split
|
59
|
+
@params[key] = ::StTools::String.split(@params[key].to_s, ',', sort: true)
|
60
|
+
when :range
|
61
|
+
@params[key] = @params[key].to_s.to_range(sort: true, uniq: true)
|
62
|
+
when :range_no_uniq
|
63
|
+
@params[key] = @params[key].to_s.to_range(sort: true)
|
64
|
+
when :float
|
65
|
+
@params[key] = @params[key].to_s.strip.to_f
|
66
|
+
when :integer
|
67
|
+
@params[key] = @params[key].to_s.strip.to_i
|
68
|
+
when :downcase
|
69
|
+
@params[key] = @params[key].to_s.downcase
|
70
|
+
when :upcase
|
71
|
+
@params[key] = @params[key].to_s.upcase
|
72
|
+
when :normalize
|
73
|
+
@params[key] = @params[key].to_s.normalize
|
74
|
+
when :caps
|
75
|
+
@params[key] = @params[key].to_s.caps
|
76
|
+
when :string
|
77
|
+
@params[key] = @params[key].to_s
|
78
|
+
else
|
79
|
+
end
|
80
|
+
|
81
|
+
convert_from_hash
|
82
|
+
set_full(action, key, default, @params[key], description)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Метод выводит подсказку по аргументам командной строки
|
86
|
+
def help
|
87
|
+
puts
|
88
|
+
puts "Параметры приложения:"
|
89
|
+
|
90
|
+
screenwidth = ::StTools::System.screen(:width)
|
91
|
+
keylen = self.keylen
|
92
|
+
|
93
|
+
@full.each do |key, data|
|
94
|
+
line = get_helpline(key, data[:description], keylen, screenwidth)
|
95
|
+
line.each { |x| puts x }
|
96
|
+
end
|
97
|
+
puts
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def set_full(action, key, default, value, description)
|
103
|
+
@full[key] = Hash.new
|
104
|
+
@full[key][:action] = action
|
105
|
+
@full[key][:default] = default
|
106
|
+
@full[key][:value] = value
|
107
|
+
@full[key][:description] = description + ' ' + human_default(action, value, default)
|
108
|
+
end
|
109
|
+
|
110
|
+
def human_default(action, value, default)
|
111
|
+
type = value.class.to_s
|
112
|
+
defval = default.inspect
|
113
|
+
"(по умолчанию #{defval}:#{type})"
|
114
|
+
end
|
115
|
+
|
116
|
+
def keylen
|
117
|
+
keylen = 0
|
118
|
+
@full.each do |key, data|
|
119
|
+
keylen = key.to_s.length if key.to_s.length > keylen
|
120
|
+
end
|
121
|
+
keylen
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_helpline(key, line, keylen, screenwidth)
|
125
|
+
out = Array.new
|
126
|
+
width = screenwidth - 2 - keylen - 3
|
127
|
+
chunks = line.chars.each_slice(width).map(&:join)
|
128
|
+
|
129
|
+
chunks.each do |one|
|
130
|
+
if out.count == 0
|
131
|
+
tmp = key.to_s.ljust(keylen, ' ') + ' - '
|
132
|
+
else
|
133
|
+
tmp = ' '.ljust(keylen, ' ') + ' '
|
134
|
+
end
|
135
|
+
out << " #{tmp}#{one.strip}"
|
136
|
+
out
|
137
|
+
end
|
138
|
+
|
139
|
+
out
|
140
|
+
end
|
141
|
+
|
142
|
+
def convert_from_hash
|
143
|
+
@params.each do |key, value|
|
144
|
+
name = new_ostruct_member(key)
|
145
|
+
self[name] = value
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Класс обеспечивает чтение различных конфиг-файлов и их объединение в единый интерфейс.
|
2
|
+
# Например, при задании конфига вида
|
3
|
+
#
|
4
|
+
# cli:
|
5
|
+
# timezone: 3
|
6
|
+
#
|
7
|
+
# к указанным переменным можно получить доступ через вызов puts config.cli.timezone #=> 3
|
8
|
+
#
|
9
|
+
|
10
|
+
module CliApplication
|
11
|
+
class Config < OpenStruct
|
12
|
+
attr_reader :config
|
13
|
+
|
14
|
+
# Конструктор. Вызывается при создании класса приложения. Данный класс доступен
|
15
|
+
# в главной функции приложения (main) через переменную config
|
16
|
+
#
|
17
|
+
# @param [Array] folders директории, в которых расположены базовый класс проекта и класс приложения
|
18
|
+
def initialize(folders)
|
19
|
+
super(nil)
|
20
|
+
return if folders.nil?
|
21
|
+
@folders = folders
|
22
|
+
@filenames = Array.new
|
23
|
+
@config_filename = File.join([folders[:class], 'config.yml'])
|
24
|
+
load_config(@config_filename)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Метод загружает конфиг и делает его доступным через единый интерфейс настроек конфигурации приложения (CliApplication::Config)
|
28
|
+
# При каждом вызове данного метода все конфиги перечитываются заново.
|
29
|
+
#
|
30
|
+
# @param [Sym] type параметр используется для указания местоположения конфига. Если указано :app или :class,
|
31
|
+
# то имя файла с конфигом будет дополнено папкой класса или приложения
|
32
|
+
# @option type [Sym] :app папка, из которой запущено приложение
|
33
|
+
# @option type [Sym] :class папка, в которой хранится базовый класс
|
34
|
+
# @option type [Sym] :absolute указывает на необходимость брать имя файла как задано разработчиком
|
35
|
+
# @return [Nil] нет
|
36
|
+
def add(filename, type)
|
37
|
+
if @folders.keys.include?(type)
|
38
|
+
load_config(File.join(@folders[type], filename))
|
39
|
+
elsif type == :absolute
|
40
|
+
load_config(filename)
|
41
|
+
else
|
42
|
+
warn "Предупреждение: попытка загрузить конфиг неизвестного типа (#{type.inspect}). Допустимы #{@folders.keys.inspect}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
|
50
|
+
def load_config(filename) # :nodoc:
|
51
|
+
raise "Внимание!!! Не найден файл конфигурации '#{filename}'" unless File.exist?(filename)
|
52
|
+
@filenames << filename
|
53
|
+
@filenames.uniq!
|
54
|
+
@config = Hash.new
|
55
|
+
|
56
|
+
@filenames.each do |one|
|
57
|
+
tmp = YAML.load_file(one).deep_symbolize_keys rescue Hash.new
|
58
|
+
@config.merge!(tmp)
|
59
|
+
end
|
60
|
+
|
61
|
+
tmp = JsonStruct.new(@config)
|
62
|
+
tmp.each_pair { |key, value| set_pair(key, value) }
|
63
|
+
valid?
|
64
|
+
|
65
|
+
::Time.zone = self.cli.timezone
|
66
|
+
# todo: добавить установки времени для записей ActiveRecord
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_pair(key, value) # :nodoc:
|
70
|
+
name = new_ostruct_member(key)
|
71
|
+
self[name] = value
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid? # :nodoc:
|
75
|
+
raise "ОШИБКА: не найдена секция 'cli'" if self.cli.nil?
|
76
|
+
raise "ОШИБКА: не найдена секция 'cli.tz'" if self.cli.timezone.nil?
|
77
|
+
raise "ОШИБКА: не найдена секция 'cli.active_record_tz'" if self.cli.ar_timezone.nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Класс обесечивает формирвоание конфигураций баз данных в совместимом с Rails формате
|
2
|
+
|
3
|
+
module CliApplication
|
4
|
+
class Databases
|
5
|
+
|
6
|
+
# Конструктор, который обеспечивает конфигурацию базового класса ActiveRecords::Base,
|
7
|
+
# а именно загружает в класс все конфигурации, с которыми должно работать приложение.
|
8
|
+
def initialize(config)
|
9
|
+
@config = config.to_h || Hash.new
|
10
|
+
ar_configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
# Метод возвращает список конфигураций баз данных
|
14
|
+
#
|
15
|
+
# @return [Array] массив названий конфигураций
|
16
|
+
# @example Примеры использования
|
17
|
+
# puts databases.list #=> [:default, :stat, :work_instance]
|
18
|
+
def list
|
19
|
+
@config.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
# Метод возвращает конфигурацию базы данных
|
23
|
+
#
|
24
|
+
# @param [Sym] ind идентификатор (наименование) конфигурации базы данных
|
25
|
+
# @return [Hash] конфигурация базы данных
|
26
|
+
def [](ind)
|
27
|
+
@config[ind]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def ar_configuration # :nodoc:
|
33
|
+
list.each do |cfg_name|
|
34
|
+
ActiveRecord::Base.configurations[cfg_name] = @config[cfg_name].symbolize_keys
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Расширяем стандартные классы, подмешивая туда функционал StTools
|
2
|
+
|
3
|
+
class String # :nodoc:
|
4
|
+
include ::StTools::Module::String
|
5
|
+
end
|
6
|
+
|
7
|
+
class Integer # :nodoc:
|
8
|
+
include ::StTools::Module::Integer
|
9
|
+
end
|
10
|
+
|
11
|
+
class Time # :nodoc:
|
12
|
+
include ::StTools::Module::Time
|
13
|
+
end
|
14
|
+
|
15
|
+
class Date # :nodoc:
|
16
|
+
include ::StTools::Module::Time
|
17
|
+
end
|
18
|
+
|
19
|
+
# class DateTime
|
20
|
+
# include ::StTools::Module::Time
|
21
|
+
# end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# https://gist.github.com/anonymous/0ea3a14166d24f750bd9
|
2
|
+
|
3
|
+
class JsonStruct < OpenStruct # :nodoc:
|
4
|
+
def initialize(hash=nil)
|
5
|
+
|
6
|
+
@table = {}
|
7
|
+
@hash_table = {}
|
8
|
+
|
9
|
+
if hash
|
10
|
+
recurse = Proc.new do |item|
|
11
|
+
values = []
|
12
|
+
|
13
|
+
item.each do |val|
|
14
|
+
if val.is_a?(Hash)
|
15
|
+
values.push(self.class.new(val))
|
16
|
+
elsif val.is_a?(Array)
|
17
|
+
values.push(recurse.call(val))
|
18
|
+
else
|
19
|
+
values.push(val)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
item.clear
|
24
|
+
item.push(*values)
|
25
|
+
|
26
|
+
item
|
27
|
+
end
|
28
|
+
|
29
|
+
hash.each do |k, v|
|
30
|
+
|
31
|
+
if v.is_a?(Array)
|
32
|
+
recurse.call(v)
|
33
|
+
end
|
34
|
+
|
35
|
+
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
|
36
|
+
@hash_table[k.to_sym] = v
|
37
|
+
new_ostruct_member(k)
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_h
|
44
|
+
@hash_table
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|