Class: Photein::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/photein/config.rb

Constant Summary collapse

OPTIONS =
[
  ['-v',             '--verbose',                 'print verbose output'],
  ['-s SOURCE',      '--source=SOURCE',           'path to the source directory'],
  ['-m MASTER',      '--library-master=MASTER',   'path to a destination directory (master)'],
  ['-d DESKTOP',     '--library-desktop=DESKTOP', 'path to a destination directory (desktop-optimized)'],
  ['-w WEB',         '--library-web=WEB',         'path to a destination directory (web-optimized)'],
  ['-r',             '--recursive',               'ingest source files recursively'],
  ['-k',             '--keep',                    'do not delete source files'],
  ['-i',             '--interactive',             'ask whether to import each file found'],
  ['-n',             '--dry-run',                 'perform a "no-op" trial run'],
  [                  '--shift-timestamp=N',       'adjust metadata timestamps by N hours'],
  [                  '--local-tz=TIMEZONE',       "backfill missing GPS* metadata on videos\n                                     (to name files in local time instead of UTC)"],
  [                  '--safe',                    'skip files in use by other processes']
].freeze
OPTION_NAMES =
OPTIONS
.flatten
.grep(/^--/)
.map { |option| option[/\w[a-z\-]+/] }
.map(&:to_sym)
SECONDS_PER_HOUR =
60 * 60
TZ_GEOCOORDS =
File.expand_path('../../data/tz_coords.json', File.dirname(__FILE__))
.then(&File.method(:read))
.then(&JSON.method(:parse))
.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Config

Returns a new instance of Config.



38
39
40
# File 'lib/photein/config.rb', line 38

def initialize(params = {})
  @params = params
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &blk) ⇒ Object



65
66
67
68
69
70
71
72
73
74
# File 'lib/photein/config.rb', line 65

def method_missing(m, *args, &blk)
  case m = m.to_s.tr('_', '-').to_sym
  when *OPTION_NAMES
    @params[m]
  when *OPTION_NAMES.map { |opt| "#{opt}=" }.map(&:to_sym)
    @params[m.sub(/=$/, '')] = args.shift
  else
    super
  end
end

Class Method Details

.base_configObject



105
106
107
# File 'lib/photein/config.rb', line 105

def base_config
  @base_config ||= Photein::Config.new
end

.method_missing(m, *args, &blk) ⇒ Object



143
144
145
146
147
# File 'lib/photein/config.rb', line 143

def method_missing(m, *args, &blk)
  return super unless m.to_s.tr('_', '-').sub(/=$/, '').to_sym

  base_config.send(m, *args)
end

.parse_opts!Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/photein/config.rb', line 109

def parse_opts!
  parser = OptionParser.new do |opts|
    opts.version = Photein::VERSION
    opts.banner  = <<~BANNER
      Usage: photein [--version] [-h | --help] [<args>]
    BANNER

    OPTIONS.each { |opt| opts.on(*opt) }
  end.tap { |p| p.parse!(into: base_config.instance_variable_get('@params')) }

  # This param is only required on the base config
  if !base_config.instance_variable_get('@params').key?(:source)
    raise "no source directory given"
  end

  base_config.validate_params!
rescue => e
  warn("#{parser.program_name}: #{e.message}")
  warn(parser.help) if e.is_a?(OptionParser::ParseError)
  exit 1
end

.respond_to_missing?(m, *args) ⇒ Boolean

Returns:

  • (Boolean)


149
150
151
# File 'lib/photein/config.rb', line 149

def respond_to_missing?(m, *args)
  base_config.respond_to?(m) || super
end

.set(**params) ⇒ Object

Do not remove! Used in https://github.com/rlue/xferase



132
133
134
# File 'lib/photein/config.rb', line 132

def set(**params)
  base_config.instance_variable_get('@params').replace(params)
end

.with(opts) ⇒ Object



136
137
138
139
140
141
# File 'lib/photein/config.rb', line 136

def with(opts)
  opts.transform_keys { |k| k.to_s.tr('_', '-').to_sym }
    .then(&base_config.instance_variable_get('@params').method(:merge))
    .then(&Photein::Config.method(:new))
    .tap(&:validate_params!)
end

Instance Method Details

#destinationsObject



84
85
86
87
88
89
90
# File 'lib/photein/config.rb', line 84

def destinations
  @destinations ||= {
    master:  @params[:'library-master'],
    desktop: @params[:'library-desktop'],
    web:     @params[:'library-web']
  }.compact.transform_values(&Pathname.method(:new))
end

#local_tzObject



96
97
98
# File 'lib/photein/config.rb', line 96

def local_tz
  @local_tz ||= @params[:'local-tz']&.then(&TZInfo::Timezone.method(:get))
end

#respond_to_missing?(m, *args) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/photein/config.rb', line 76

def respond_to_missing?(m, *args)
  OPTION_NAMES.include?(m.to_s.tr('_', '-').sub(/=$/, '').to_sym) || super
end

#sourceObject



80
81
82
# File 'lib/photein/config.rb', line 80

def source
  @source ||= Pathname(@params[:source])
end

#timestamp_deltaObject



92
93
94
# File 'lib/photein/config.rb', line 92

def timestamp_delta
  @timestamp_delta ||= @params[:'shift-timestamp'].to_i * SECONDS_PER_HOUR
end

#tz_coordinatesObject



100
101
102
# File 'lib/photein/config.rb', line 100

def tz_coordinates
  @tz_coordinates ||= TZ_GEOCOORDS[@params[:'local-tz']]
end

#validate_params!Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/photein/config.rb', line 42

def validate_params!
  @params[:verbose] ||= @params[:'dry-run']

  if @params.key?(:'shift-timestamp') && !@params[:'shift-timestamp'].match?(/^-?\d+$/)
    raise "invalid --shift-timestamp option (must be integer)"
  end

  if @params.key?(:'local-tz')
    if !TZInfo::Timezone.all_identifiers.include?(@params[:'local-tz'])
      raise 'invalid --local-tz option (must be from IANA tz database)'
    end

    if tz_coordinates.nil?
      raise 'invalid --local-tz option (must reference a location)'
    end
  end

  @params.freeze

  (%i[library-master library-desktop library-web] & @params.keys)
    .then { |dest_dirs| raise "no destination directory given" if dest_dirs.empty? }
end