Class: INat::Application

Inherits:
Object
  • Object
show all
Includes:
App, INat::App::Config, INat::App::Info, INat::App::Preamble
Defined in:
lib/inat/app/config.rb,
lib/inat/app/application.rb

Overview

TODO: переделать в модуль INat::App::Config

Constant Summary collapse

API_DEFAULT =
'https://api.inaturalist.org/v1/'
DEFAULT_LOG =
"./#{ NAME }.log"
DEFAULTS =
{
  threads: {
    enable: true,
    tasks: 3,
  },
  data: {
    cache: true,
    directory: File.expand_path("~/.cache/#{ NAME }/"),
    update: UpdateMode::UPDATE,
    update_period: Period::DAY,
    outdate_period: Period::WEEK,
  },
  verbose: MessageLevel::ERROR,
  log: {
    enable: false,
    file: DEFAULT_LOG,
    level: MessageLevel::WARNING,
    shift: {
      age: nil,
      size: nil,
    },
  },
  api: {
    root: API_DEFAULT,
    locale: nil,
    preferred_place_id: nil,
    open_timeout: nil,
    read_timeout: nil,
  },
  preamble: [],
}
CONFIG_FILE =
File.expand_path "~/.config/#{ NAME }.yml"

Constants included from INat::App::Info

INat::App::Info::ABOUT, INat::App::Info::AUTHOR, INat::App::Info::EMAIL, INat::App::Info::EXE, INat::App::Info::HOMEPAGE, INat::App::Info::NAME, INat::App::Info::SOURCE_URL, INat::App::Info::USAGE, INat::App::Info::VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from INat::App::Preamble

#do_clean_all, #do_clean_observations, #do_clean_orphans, #do_clean_requests, #preamble!

Constructor Details

#initializeApplication

Returns a new instance of Application.



18
19
20
21
22
# File 'lib/inat/app/application.rb', line 18

def initialize
  setup!
  G.config = @config.freeze
  G.logger = INat::App::Logger::new self
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



298
299
300
# File 'lib/inat/app/config.rb', line 298

def config
  @config
end

Instance Method Details

#loggerObject



14
15
16
# File 'lib/inat/app/application.rb', line 14

def logger
  G.logger
end

#parse_args!Object (private)



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/inat/app/config.rb', line 69

private def parse_args!
  options = {}
  opts = OptionParser::new USAGE do |o|

    o.accept UpdateMode do |mode|
      UpdateMode::parse mode, case_sensitive: false
    end

    o.accept MessageLevel do |level|
      MessageLevel::parse level, case_sensitive: false
    end

    o.accept ShiftAge do |shift_age|
      ShiftAge::parse shift_age
    end

    o.accept Period do |period|
      Period::parse period
    end

    o.separator ''
    o.separator "\e[1mInformation:\e[0m"

    o.on '-h', '--help', 'Show help and exit.' do
      puts ABOUT
      puts
      puts opts.help
      exit 0
    end

    o.on '-?', '--usage', 'Show short help and exit.' do
      puts opts.help
      exit 0
    end

    o.on '--about', 'Show program info and exit.' do
      puts ABOUT
      exit 0
    end

    o.on '--version', 'Show version and exit.' do
      puts VERSION
      exit 0
    end

    o.separator ''
    o.separator "\e[1mConfiguration:\e[0m"

    o.on '-c', '--config FILE', String, "Configuration file instead standard (~/.config/#{ NAME }.yml)." do |value|
      options[:config_file] = value
    end

    o.separator ''
    o.separator "\e[1mFlow Control:\e[0m"

    o.on '-1', '--no-threads', 'Disable threads.' do
      options[:threads] ||= {}
      options[:threads][:enable] = false
    end

    o.on '-t', '--tasks NUMBER', Integer, 'Limit number of concurrent tasks.' do |value|
      options[:threads] ||= {}
      options[:threads][:tasks] = value
    end

    o.separator ''
    o.separator "\e[1mCache Control:\e[0m"

    o.on '-u', '--update MODE', UpdateMode, "Update mode:", "  'default' (or 'update')",
                                                            "  'force-update' (or 'force')",
                                                            "  'force-reload' (or 'reload')",
                                                            "  'skip-existing' (or 'minimal')",
                                                            "  'no-update' (or 'offline')" do |value|
      options[:data] ||= {}
      options[:data][:update] = value
    end

    o.on '-f', '--force-update', 'Force update.' do
      options[:data] ||= {}
      options[:data][:update] = UpdateMode::FORCE
    end

    o.on '-F', '--force-reload', '--reload', 'Force reload.' do
      options[:data] ||= {}
      options[:data][:update] = UpdateMode::RELOAD
    end

    o.on '--skip-existing', 'Skip updating if exist.' do
      options[:data] ||= {}
      options[:data][:update] = UpdateMode::MINIMAL
    end

    o.on '-n', '--offline', '--no-update', 'No updating.' do
      options[:data] ||= {}
      options[:data][:update] = UpdateMode::OFFLINE
    end

    o.separator ''

    o.on '--update-period PERIOD', Period, 'Update period.' do |value|
      options[:data] ||= {}
      options[:data][:update_period] = value
    end

    o.on '--obsolete-period PERIOD', Period, 'Obsolete period.' do |value|
      options[:data] ||= {}
      options[:data][:obsolete_period] = value
    end

    o.on '--cache-directory PATH', String, 'Cache directory path.' do |value|
      options[:data] ||= {}
      options[:data][:directory] = value
    end

    o.separator ''

    o.on '--clean-requests', 'Clean outdated requests in data cache.' do
      options[:preamble] << :clean_requests
    end

    o.on '--clean-observations', 'Clean observations without request in data cache.' do
      options[:preamble] << :clean_observations
    end

    o.on '--clean-orphans', 'Clean orphan objects in data cache.' do
      options[:preamble] << :clean_orphans
    end

    o.on '--clean-all', 'Clean data cache.' do
      options[:preamble] << :clean_all
    end

    o.separator ''
    o.separator "\e[1mAPI Parameters:\e[0m"

    o.on '-A', '--api-root URI', String, 'API root' do |value|
      options[:api] ||= {}
      options[:api][:root] = value
    end

    o.on '-L', '--locale LOCALE', String, 'Set the locale.' do |value|
      value = nil if value.downcase == 'none'
      options[:api] ||= {}
      options[:api][:locale] = value
    end

    o.on '-P', '--preferred-place ID', Integer, 'Preferred place for API.' do |value|
      value = nil if value == 0
      options[:api] ||= {}
      options[:api][:preferred_place_id] = value
    end

    o.separator ''
    o.separator "\e[1mLogging Control:\e[0m"

    o.on '--verbose-level LEVEL', MessageLevel, 'Set the logging level for $STDERR.', "  'fatal'",
                                                                                          "  'error'",
                                                                                          "  'warn' (or 'warning')",
                                                                                          "  'info'",
                                                                                          "  'debug'" do |value|
      options[:verbose] = value
    end

    o.on '-v', '--verbose', 'Set verbose level to INFO.' do
      options[:verbose] = MessageLevel::INFO
    end

    o.on '--log-level LEVEL', MessageLevel, 'Set the logging level for file log' do |value|
      options[:log] ||= {}
      options[:log][:level] = value
    end

    o.on '--log-file FILE', String, 'File name for log.' do |value|
      value = DEFAULT_LOG if /^default$/i === value
      options[:log] ||= {}
      options[:log][:file] = value
    end

    o.on '-l', '--log [FILE]', String, "Enable logging to file. If file is not specified './#{ NAME }.log' used.",
                                       "  'none', 'no' or 'false' to disabling." do |value|
      options[:log] ||= {}
      case value
      when nil, /^(true|yes|default)$/i
        options[:log][:enable] = true
        options[:log][:file] = DEFAULT_LOG
      when /^(false|no|none)$/i
        options[:log][:enable] = false
        options[:log][:file] = DEFAULT_LOG
      else
        options[:log][:enable] = true
        options[:log][:file] = value.to_s
      end
    end

    o.on '--log-shift-age AGE', ShiftAge, 'Log file shift age.' do |value|
      value = nil if value == 0
      options[:log] ||= {}
      options[:log][:shift] ||= {}
      options[:log][:shift][:age] = value
    end

    o.on '--log-shift-size SIZE', Integer, 'Log file shift size.' do |value|
      value = nil if value == 0
      options[:log] ||= {}
      options[:log][:shift] ||= {}
      options[:log][:shift][:size] = value
    end

    o.separator ''

    o.separator "\e[1mFile Arguments:\e[0m"
    o.separator "\t‹task[, ...]›\t\t     One or more names of task files.\n" +
                "\t\t\t\t     If name has not extension try to read '‹task›' than '‹task›.inat' than '‹task›.rb'."

  end
  files = parse_files(opts.parse(ARGV))
  [ options, files ]
end

#parse_files(files) ⇒ Object (private)



58
59
60
61
62
63
64
65
66
67
# File 'lib/inat/app/config.rb', line 58

private def parse_files files
  files.map do |file|
    if file.start_with?('@')
      list = file[1..]
      File.readlines(list, chomp: true).filter { |l| !l.empty? }
    else
      file
    end
  end.flatten
end

#runObject



45
46
47
48
49
# File 'lib/inat/app/application.rb', line 45

def run
  preamble!
  tasks!
  start!
end

#setup!Object (private)



288
289
290
291
292
293
294
295
296
# File 'lib/inat/app/config.rb', line 288

private def setup!
  @config = DEFAULTS
  options, @files = parse_args!
  config_file = options[:config_file] || CONFIG_FILE
  @config.deep_merge! YAML.load_file(config_file, symbolize_names: true, freeze: true) if File.exist?(config_file)
  @config.deep_merge! options
  @config[:verbose] = MessageLevel::parse @config[:verbose]
  @config[:log][:level] = MessageLevel::parse @config[:log][:level]
end

#start!Object (private)



31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/inat/app/application.rb', line 31

private def start!
  @threads = []
  count = [ @tasks.size, G.config[:threads][:tasks] ].min
  count.times do
    @threads << Thread.start do
      while !@tasks.empty?
        task = @tasks.pop
        task.execute
      end
    end
  end
  @threads.each { |th| th.join }
end

#tasks!Object (private)



24
25
26
27
28
29
# File 'lib/inat/app/application.rb', line 24

private def tasks!
  @tasks = Queue::new
  @files.each do |file|
    @tasks.push Task::new(self, file)
  end
end