doing 2.0.7.pre → 2.0.11

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/.yardoc/checksums +20 -0
  3. data/.yardoc/complete +0 -0
  4. data/.yardoc/object_types +0 -0
  5. data/.yardoc/objects/root.dat +0 -0
  6. data/.yardoc/proxy_types +0 -0
  7. data/.yardopts +1 -0
  8. data/CHANGELOG.md +13 -9
  9. data/Gemfile.lock +30 -10
  10. data/README.md +1 -1
  11. data/Rakefile +8 -1
  12. data/bin/doing +368 -21
  13. data/doc/Array.html +135 -0
  14. data/doc/Doing/Color.html +506 -0
  15. data/doc/Doing/Configuration.html +680 -0
  16. data/doc/Doing/Errors/DoingNoTraceError.html +186 -0
  17. data/doc/Doing/Errors/DoingRuntimeError.html +186 -0
  18. data/doc/Doing/Errors/DoingStandardError.html +186 -0
  19. data/doc/Doing/Errors/EmptyInput.html +186 -0
  20. data/doc/Doing/Errors/NoResults.html +186 -0
  21. data/doc/Doing/Errors/PluginException.html +248 -0
  22. data/doc/Doing/Errors/UserCancelled.html +186 -0
  23. data/doc/Doing/Errors/WrongCommand.html +186 -0
  24. data/doc/Doing/Errors.html +191 -0
  25. data/doc/Doing/Hooks.html +364 -0
  26. data/doc/Doing/Item.html +1385 -0
  27. data/doc/Doing/Items.html +393 -0
  28. data/doc/Doing/LogAdapter.html +1650 -0
  29. data/doc/Doing/Note.html +535 -0
  30. data/doc/Doing/Pager.html +268 -0
  31. data/doc/Doing/Plugins.html +849 -0
  32. data/doc/Doing/Util.html +870 -0
  33. data/doc/Doing/WWID.html +4827 -0
  34. data/doc/Doing.html +145 -0
  35. data/doc/GLI/Commands/MarkdownDocumentListener.html +763 -0
  36. data/doc/GLI/Commands.html +115 -0
  37. data/doc/GLI.html +115 -0
  38. data/doc/Hash.html +332 -0
  39. data/doc/Status.html +292 -0
  40. data/doc/String.html +1714 -0
  41. data/doc/Symbol.html +250 -0
  42. data/doc/Time.html +182 -0
  43. data/doc/_index.html +411 -0
  44. data/doc/class_list.html +51 -0
  45. data/doc/css/common.css +1 -0
  46. data/doc/css/full_list.css +58 -0
  47. data/doc/css/style.css +497 -0
  48. data/doc/file.README.html +123 -0
  49. data/doc/file_list.html +56 -0
  50. data/doc/frames.html +17 -0
  51. data/doc/index.html +123 -0
  52. data/doc/js/app.js +314 -0
  53. data/doc/js/full_list.js +216 -0
  54. data/doc/js/jquery.js +4 -0
  55. data/doc/method_list.html +1867 -0
  56. data/doc/top-level-namespace.html +112 -0
  57. data/doing.gemspec +5 -1
  58. data/doing.rdoc +354 -6
  59. data/example_plugin.rb +6 -6
  60. data/lib/doing/array.rb +15 -2
  61. data/lib/doing/configuration.rb +14 -12
  62. data/lib/doing/errors.rb +1 -1
  63. data/lib/doing/hash.rb +1 -1
  64. data/lib/doing/item.rb +113 -23
  65. data/lib/doing/log_adapter.rb +132 -119
  66. data/lib/doing/note.rb +1 -1
  67. data/lib/doing/plugin_manager.rb +5 -5
  68. data/lib/doing/plugins/export/csv_export.rb +1 -1
  69. data/lib/doing/plugins/export/template_export.rb +5 -7
  70. data/lib/doing/plugins/import/calendar_import.rb +8 -2
  71. data/lib/doing/plugins/import/doing_import.rb +10 -10
  72. data/lib/doing/plugins/import/timing_import.rb +12 -4
  73. data/lib/doing/string.rb +96 -21
  74. data/lib/doing/symbol.rb +9 -5
  75. data/lib/doing/time.rb +1 -1
  76. data/lib/doing/util.rb +18 -11
  77. data/lib/doing/version.rb +1 -1
  78. data/lib/doing/wwid.rb +510 -368
  79. data/lib/doing/wwidfile.rb +5 -5
  80. data/lib/doing.rb +2 -1
  81. data/lib/examples/plugins/say_export.rb +6 -6
  82. data/lib/examples/plugins/{templates → wiki_export/templates}/wiki.css +0 -0
  83. data/lib/examples/plugins/{templates → wiki_export/templates}/wiki.haml +0 -0
  84. data/lib/examples/plugins/{templates → wiki_export/templates}/wiki_index.haml +0 -0
  85. data/lib/examples/plugins/{wiki_export.rb → wiki_export/wiki_export.rb} +0 -0
  86. data/rdocfixer.rb +1 -1
  87. data/yard_templates/default/method_details/setup.rb +3 -0
  88. metadata +121 -8
data/example_plugin.rb CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  module Doing
28
28
  ##
29
- ## @brief Plugin class
29
+ ## Plugin class
30
30
  ##
31
31
  class SayExport
32
32
  include Doing::Util
@@ -65,7 +65,7 @@ module Doing
65
65
  ##
66
66
  ## wwid.config['plugins'][PLUGIN_NAME][KEY]
67
67
  ##
68
- ## @brief Method to return plugin settings (required)
68
+ ## Method to return plugin settings (required)
69
69
  ##
70
70
  ## @return Hash of settings for this plugin
71
71
  ##
@@ -87,7 +87,7 @@ module Doing
87
87
  ## included in settings. The method should return a
88
88
  ## string (not output it to the STDOUT).
89
89
  ##
90
- ## @brief Method to return template (optional)
90
+ ## Method to return template (optional)
91
91
  ##
92
92
  ## @param trigger The trigger passed to the
93
93
  ## template function. When this
@@ -96,7 +96,7 @@ module Doing
96
96
  ## used to determine which one is
97
97
  ## output.
98
98
  ##
99
- ## @return (String) template contents
99
+ ## @return [String] template contents
100
100
  ##
101
101
  def self.template(trigger)
102
102
  return unless trigger =~ /^say(it)?$/
@@ -106,7 +106,7 @@ module Doing
106
106
 
107
107
 
108
108
  ##
109
- ## @brief Render data received from an output
109
+ ## Render data received from an output
110
110
  ## command
111
111
  ##
112
112
  ## @param wwid The wwid object with config
@@ -118,7 +118,7 @@ module Doing
118
118
  ## flags passed to command
119
119
  ## (variables[:options])
120
120
  ##
121
- ## @return (String) Rendered output
121
+ ## @return [String] Rendered output
122
122
  ##
123
123
  def self.render(wwid, items, variables: {})
124
124
  return if items.nil? || items.empty?
data/lib/doing/array.rb CHANGED
@@ -2,7 +2,20 @@
2
2
 
3
3
  module Doing
4
4
  ##
5
- ## @brief Array helpers
5
+ ## Array helpers
6
6
  ##
7
- class ::Array end
7
+ class ::Array
8
+ def to_tags
9
+ map { |t| t.sub(/^@?/, '@') }
10
+ end
11
+
12
+ def highlight_tags(color = 'cyan')
13
+ tag_color = Doing::Color.send(color)
14
+ to_tags.map { |t| "#{tag_color}#{t}" }
15
+ end
16
+
17
+ def log_tags
18
+ highlight_tags.join(', ')
19
+ end
20
+ end
8
21
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Doing
4
4
  ##
5
- ## @brief Configuration object
5
+ ## Configuration object
6
6
  ##
7
7
  class Configuration
8
8
  attr_reader :settings
@@ -106,14 +106,15 @@ module Doing
106
106
 
107
107
  def value_for_key(keypath = '')
108
108
  cfg = @settings
109
+ real_path = []
109
110
  unless keypath =~ /^[.*]?$/
110
111
  paths = keypath.split(/[:.]/)
111
112
  while paths.length.positive? && !cfg.nil?
112
113
  path = paths.shift
113
114
  new_cfg = nil
114
115
  cfg.each do |key, val|
115
- next unless key =~ /#{path.to_rx(2)}/
116
-
116
+ next unless key =~ path.to_rx(distance: 2)
117
+ real_path << key
117
118
  new_cfg = val
118
119
  break
119
120
  end
@@ -122,12 +123,13 @@ module Doing
122
123
  Doing.logger.error("Key match not found: #{path}")
123
124
  break
124
125
  end
125
-
126
126
  cfg = new_cfg
127
127
  end
128
128
  end
129
-
130
- cfg
129
+ Doing.logger.debug('Config:', "translated key path #{keypath} to #{real_path.join('.')}")
130
+ result = {}
131
+ result[real_path[-1]] = cfg
132
+ result
131
133
  end
132
134
 
133
135
  # It takes the input, fills in the defaults where values do not exist.
@@ -148,9 +150,9 @@ module Doing
148
150
  end
149
151
 
150
152
  ##
151
- ## @brief Read user configuration and merge with defaults
153
+ ## Read user configuration and merge with defaults
152
154
  ##
153
- ## @param opt (Hash) Additional Options
155
+ ## @param opt [Hash] Additional Options
154
156
  ##
155
157
  def configure(opt = {})
156
158
  @ignore_local = opt[:ignore_local] if opt[:ignore_local]
@@ -219,7 +221,7 @@ module Doing
219
221
  end
220
222
 
221
223
  ##
222
- ## @brief Read local configurations
224
+ ## Read local configurations
223
225
  ##
224
226
  ## @return Hash of config options
225
227
  ##
@@ -251,7 +253,7 @@ module Doing
251
253
  end
252
254
 
253
255
  ##
254
- ## @brief Reads a configuration.
256
+ ## Reads a configuration.
255
257
  ##
256
258
  def read_config
257
259
  unless File.exist?(config_file)
@@ -279,9 +281,9 @@ module Doing
279
281
  end
280
282
 
281
283
  ##
282
- ## @brief Finds a project-specific configuration file
284
+ ## Finds a project-specific configuration file
283
285
  ##
284
- ## @return (String) A file path
286
+ ## @return [String] A file path
285
287
  ##
286
288
  def find_local_config
287
289
  dir = Dir.pwd
data/lib/doing/errors.rb CHANGED
@@ -27,7 +27,7 @@ module Doing
27
27
  end
28
28
 
29
29
  class WrongCommand < ::StandardError
30
- def initialize(msg = 'wrong command', topic = 'Error:')
30
+ def initialize(msg = 'wrong command', topic: 'Error:')
31
31
  Doing.logger.warn(topic, msg)
32
32
 
33
33
  super(msg)
data/lib/doing/hash.rb CHANGED
@@ -4,7 +4,7 @@ module Doing
4
4
  # Hash helpers
5
5
  class ::Hash
6
6
  ##
7
- ## @brief Freeze all values in a hash
7
+ ## Freeze all values in a hash
8
8
  ##
9
9
  ## @return { description_of_the_return_value }
10
10
  ##
data/lib/doing/item.rb CHANGED
@@ -2,11 +2,24 @@
2
2
 
3
3
  module Doing
4
4
  ##
5
- ## @brief This class describes a single WWID item
5
+ ## This class describes a single WWID item
6
6
  ##
7
7
  class Item
8
+ include Amatch
9
+
8
10
  attr_accessor :date, :title, :section, :note
9
11
 
12
+ ##
13
+ ## Initialize an item with date, title, section, and
14
+ ## optional note
15
+ ##
16
+ ## @param date [Time] The item's start date
17
+ ## @param title [String] The title
18
+ ## @param section [String] The section to which
19
+ ## the item belongs
20
+ ## @param note [Array or String] The note
21
+ ## (optional)
22
+ ##
10
23
  def initialize(date, title, section, note = nil)
11
24
  @date = date.is_a?(Time) ? date : Time.parse(date)
12
25
  @title = title
@@ -18,14 +31,36 @@ module Doing
18
31
  # @date = new_date.is_a?(Time) ? new_date : Time.parse(new_date)
19
32
  # end
20
33
 
34
+ ##
35
+ ## Get the difference between the item's start date and
36
+ ## the value of its @done tag (if present)
37
+ ##
38
+ ## @return Interval in seconds
39
+ ##
21
40
  def interval
22
41
  @interval ||= calc_interval
23
42
  end
24
43
 
44
+ ##
45
+ ## Get the value of the item's @done tag
46
+ ##
47
+ ## @return [Time] @done value
48
+ ##
25
49
  def end_date
26
50
  @end_date ||= Time.parse(Regexp.last_match(1)) if @title =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
27
51
  end
28
52
 
53
+ def hash_id
54
+ (@date.to_s + @title + @section + @note.join(' ')).hash
55
+ end
56
+
57
+ ##
58
+ ## Test for equality between items
59
+ ##
60
+ ## @param other [Item] The other item
61
+ ##
62
+ ## @return [Boolean] is equal?
63
+ ##
29
64
  def equal?(other)
30
65
  return false if @title.strip != other.title.strip
31
66
 
@@ -36,10 +71,25 @@ module Doing
36
71
  true
37
72
  end
38
73
 
74
+ ##
75
+ ## Test if two items occur at the same time (same start date and equal duration)
76
+ ##
77
+ ## @param item_b [Item] The item to compare
78
+ ##
79
+ ## @return [Boolean] is equal?
80
+ ##
39
81
  def same_time?(item_b)
40
82
  date == item_b.date ? interval == item_b.interval : false
41
83
  end
42
84
 
85
+ ##
86
+ ## Test if the interval between start date and @done
87
+ ## value overlaps with another item's
88
+ ##
89
+ ## @param item_b [Item] The item to compare
90
+ ##
91
+ ## @return [Boolean] overlaps?
92
+ ##
43
93
  def overlapping_time?(item_b)
44
94
  return true if same_time?(item_b)
45
95
 
@@ -52,43 +102,83 @@ module Doing
52
102
  (start_a >= start_b && start_a <= end_b) || (end_a >= start_b && end_a <= end_b) || (start_a < start_b && end_a > end_b)
53
103
  end
54
104
 
105
+ ##
106
+ ## Add (or remove) tags from the title of the item
107
+ ##
108
+ ## @param tag [String] The tag to add
109
+ ## @param value [String] A value to include as @tag(value)
110
+ ## @param remove [Boolean] if true remove instead of adding
111
+ ## @param rename_to [String] if not nil, rename target tag to this tag name
112
+ ## @param regex [Boolean] treat target tag string as regex pattern
113
+ ##
55
114
  def tag(tag, value: nil, remove: false, rename_to: nil, regex: false)
56
115
  @title.tag!(tag, value: value, remove: remove, rename_to: rename_to, regex: regex).strip!
57
116
  end
58
117
 
118
+ ##
119
+ ## Get a list of tags on the item
120
+ ##
121
+ ## @return [Array] array of tags (no values)
122
+ ##
59
123
  def tags
60
124
  @title.scan(/(?<= |\A)@([^\s(]+)/).map {|tag| tag[0]}.sort.uniq
61
125
  end
62
126
 
63
- def tags?(tags, bool = :and)
127
+ ##
128
+ ## Test if item contains tag(s)
129
+ ##
130
+ ## @param tags (Array or String) The tags to test. Can be an array or a comma-separated string.
131
+ ## @param bool (Symbol) The boolean to use for multiple tags (:and, :or, :not)
132
+ ## @param negate [Boolean] negate the result?
133
+ ##
134
+ ## @return [Boolean] true if tag/bool combination passes
135
+ ##
136
+ def tags?(tags, bool = :and, negate: false)
64
137
  tags = split_tags(tags)
65
138
  bool = bool.normalize_bool
66
139
 
67
- case bool
68
- when :and
69
- all_tags?(tags)
70
- when :not
71
- no_tags?(tags)
72
- else
73
- any_tags?(tags)
74
- end
75
- end
76
-
77
- def search(search)
140
+ matches = case bool
141
+ when :and
142
+ all_tags?(tags)
143
+ when :not
144
+ no_tags?(tags)
145
+ else
146
+ any_tags?(tags)
147
+ end
148
+ negate ? !matches : matches
149
+ end
150
+
151
+ ##
152
+ ## Test if item matches search string
153
+ ##
154
+ ## @param search [String] The search string
155
+ ## @param negate [Boolean] negate results
156
+ ## @param case_type (Symbol) The case-sensitivity
157
+ ## type (:sensitive,
158
+ ## :ignore, :smart)
159
+ ##
160
+ ## @return [Boolean] matches search criteria
161
+ ##
162
+ def search(search, distance: 3, negate: false, case_type: :smart, fuzzy: false)
78
163
  text = @title + @note.to_s
79
- pattern = case search.strip
80
- when %r{^/.*?/$}
81
- search.sub(%r{/(.*?)/}, '\1')
82
- when /^'/
83
- case_sensitive = true
84
- search.sub(/^'(.*?)'?$/, '\1')
164
+
165
+ if search.is_rx? || !fuzzy
166
+ matches = text =~ search.to_rx(distance: distance, case_type: case_type)
167
+ else
168
+ distance = 0.25 if distance > 1
169
+ score = if (case_type == :smart && search !~ /[A-Z]/) || case_type == :ignore
170
+ text.downcase.pair_distance_similar(search.downcase)
85
171
  else
86
- case_sensitive = true if search =~ /[A-Z]/
87
- search.split('').join('.{0,3}')
172
+ score = text.pair_distance_similar(search)
88
173
  end
89
- rx = Regexp.new(pattern, !case_sensitive)
90
174
 
91
- text =~ rx
175
+ if score >= distance
176
+ matches = true
177
+ Doing.logger.debug('Fuzzy Match:', %(#{@title}, "#{search}" #{score}))
178
+ end
179
+ end
180
+
181
+ negate ? !matches : matches
92
182
  end
93
183
 
94
184
  def should_finish?