Module: Doing::Util

Extended by:
Util
Included in:
CSVExport, CalendarImport, DayoneExport, DoingImport, HTMLExport, JSONExport, JSONImport, MarkdownExport, TaskPaperExport, TemplateExport, TimingImport, Util, Backup
Defined in:
lib/doing/util.rb,
lib/doing/util_backup.rb

Overview

Utilities

Defined Under Namespace

Modules: Backup

Instance Method Summary collapse

Instance Method Details

#args_for_editor(editor) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/doing/util.rb', line 149

def args_for_editor(editor)
  return editor if editor =~ /-\S/

  args = case editor
         when /^(subl|code|mate)$/
           ['-w']
         when /^(vim|mvim)$/
           ['-f']
         else
           []
         end
  "#{editor} #{args.join(' ')}"
end

#deep_merge_hashes(master_hash, other_hash) ⇒ Object

Non-destructive version of deep_merge_hashes!

Parameters:

  • master_hash (Hash)

    The master hash

  • other_hash (Hash)

    The other hash

Returns:

  • the merged hashes.

See Also:

  • {deep_merge_hashes!}


58
59
60
# File 'lib/doing/util.rb', line 58

def deep_merge_hashes(master_hash, other_hash)
  deep_merge_hashes!(master_hash.clone, other_hash)
end

#deep_merge_hashes!(target, overwrite) ⇒ Hash

Merges a master hash with another hash, recursively.

This code was lovingly stolen from some random gem: http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html

Thanks to whoever made it.

Parameters:

  • target (Hash)

    the "parent" hash whose values will be overridden

  • overwrite (Hash)

    the other hash whose values will be persisted after the merge

Returns:

  • (Hash)

    merged hashes



77
78
79
80
81
82
83
# File 'lib/doing/util.rb', line 77

def deep_merge_hashes!(target, overwrite)
  merge_values(target, overwrite)
  merge_default_proc(target, overwrite)
  duplicate_frozen_values(target)

  target
end

#default_editorObject



141
142
143
# File 'lib/doing/util.rb', line 141

def default_editor
  @default_editor ||= find_default_editor
end

#duplicable?(obj) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
90
91
92
# File 'lib/doing/util.rb', line 85

def duplicable?(obj)
  case obj
  when nil, false, true, Symbol, Numeric
    false
  else
    true
  end
end

#duplicate_frozen_values(target) ⇒ Object



44
45
46
47
48
# File 'lib/doing/util.rb', line 44

def duplicate_frozen_values(target)
  target.each do |key, val|
    target[key] = val.dup if val.frozen? && duplicable?(val)
  end
end

#editor_with_argsObject



145
146
147
# File 'lib/doing/util.rb', line 145

def editor_with_args
  args_for_editor(default_editor)
end

#exec_available(cli) ⇒ Object

Test if command line tool is available

Parameters:

  • cli (String)

    The name or path of the cli



21
22
23
24
25
# File 'lib/doing/util.rb', line 21

def exec_available(cli)
  return false unless cli.good?

  !TTY::Which.which(cli).nil?
end

#find_default_editor(editor_for = 'default') ⇒ Object



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
# File 'lib/doing/util.rb', line 163

def find_default_editor(editor_for = 'default')
  # return nil unless $stdout.isatty || ENV['DOING_EDITOR_TEST']

  return ENV['EDITOR'] if ENV['DOING_EDITOR_TEST']

  editor_config = Doing.setting('editors')

  if editor_config.is_a?(String)
    msg = "Please update your configuration, 'editors' should be a mapping."
    msg << ' Delete the key and run `doing config --update`.'
    Doing.logger.warn('Deprecated:', msg)
    return editor_config
  end

  if editor_config[editor_for]
    editor = editor_config[editor_for]
    Doing.logger.debug('Editor:', "Using #{editor} from config 'editors.#{editor_for}' for #{editor_for}")
    return editor if editor.good?
  end

  if editor_for != 'editor' && editor_config['default']
    editor = editor_config['default']
    Doing.logger.debug('Editor:', "Using #{editor} from config: 'editors.default' for #{editor_for}")
    return editor if editor.good?
  end

  editor ||= ENV['DOING_EDITOR'] || ENV['GIT_EDITOR'] || ENV['EDITOR']

  if editor.good?
    Doing.logger.debug('Editor:', "Found editor in environment variables: #{editor} for #{editor_for}")
    return editor
  end

  Doing.logger.debug('Editor:', 'No EDITOR environment variable, testing available editors')
  editors = %w[vim vi code subl mate mvim nano emacs]
  editors.each do |ed|
    try = TTY::Which.which(ed)
    if try
      Doing.logger.debug('Editor:', "Using editor #{try} for #{editor_for}")
      return try
    end

    Doing.logger.debug('Editor:', "#{ed} not available")
  end

  nil
end

#first_available_exec(*commands) ⇒ Object

Return the first valid executable from a list of commands

Examples:

Doing::Util.first_available_exec('bat', 'less -Xr', 'more -r', 'cat')


33
34
35
36
# File 'lib/doing/util.rb', line 33

def first_available_exec(*commands)
  commands.compact.map(&:strip).reject(&:empty?).uniq
          .find { |cmd| exec_available(cmd.split.first) }
end

#mergable?(value) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
# File 'lib/doing/util.rb', line 94

def mergable?(value)
  value.is_a?(Hash)
end

#merge_default_proc(target, overwrite) ⇒ Object



38
39
40
41
42
# File 'lib/doing/util.rb', line 38

def merge_default_proc(target, overwrite)
  return unless target.is_a?(Hash) && overwrite.is_a?(Hash) && target.default_proc.nil?

  target.default_proc = overwrite.default_proc
end

#merge_values(target, overwrite) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/doing/util.rb', line 98

def merge_values(target, overwrite)
  target.merge!(overwrite) do |_key, old_val, new_val|
    if new_val.nil?
      old_val
    elsif mergable?(old_val) && mergable?(new_val)
      deep_merge_hashes(old_val, new_val)
    else
      new_val
    end
  end
end

#safe_load_file(filename) ⇒ Object



137
138
139
# File 'lib/doing/util.rb', line 137

def safe_load_file(filename)
  SafeYAML.load_file(filename) || {}
end

#user_homeObject



8
9
10
11
12
13
14
# File 'lib/doing/util.rb', line 8

def user_home
  if Dir.respond_to?('home')
    Dir.home
  else
    File.expand_path('~')
  end
end

#write_to_file(file, content, backup: true) ⇒ Object

Write content to a file

Parameters:

  • file (String)

    The path to the file to (over)write

  • content (String)

    The content to write to the file

  • backup (Boolean) (defaults to: true)

    create a ~ backup



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/doing/util.rb', line 117

def write_to_file(file, content, backup: true)
  unless file
    puts content
    return
  end
  Doing.logger.benchmark(:write_file, :start)
  file = File.expand_path(file)

  Backup.write_backup(file) if backup

  File.open(file, 'w+') do |f|
    f.puts content
    Doing.logger.debug('Write:', "File written: #{file}")
  end
  Doing.logger.benchmark(:_post_write_hook, :start)
  Hooks.trigger :post_write, file
  Doing.logger.benchmark(:_post_write_hook, :finish)
  Doing.logger.benchmark(:write_file, :finish)
end