textrepo 0.5.0 → 0.5.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc5cf6089b4883c93dc228e19aa0a149d71a8d9cc26a92e6c75fdc4ee0b2d694
4
- data.tar.gz: 8ebecace02d486b6b6c12256d52adfd44963a3a14f6d39729b83d426bce3a26e
3
+ metadata.gz: e768443355f1c5764d5b5061f65c513485446f6df9ebce67b5b5f58a4ac30b20
4
+ data.tar.gz: 9062103447a89fd2e7484c448ac80315906a2602fba363e142c47f0d5ecc592d
5
5
  SHA512:
6
- metadata.gz: aef27bf0363a66eeb1676ccda0682c253155875a8d0c16b27189674836edf2563b8df87b7f3b56cdb5d02c895f724f47a956588a5d97d24d83cdddce76fc083e
7
- data.tar.gz: 81a4f8a76eb0ec8545e29ddbd537d49ec00f6009f5617dad61c61d8a5552c6df090d6d37fd507a7976c5f5f60b7d0c8b43f9a99d98d7efa1e933828270711d87
6
+ metadata.gz: 4889cfefdf5cadbaddd47f83e31a870829a9b144dddb346b8cf6f72d55fac68d6a75c1d7f180e315720f90eeb70801fd2a987920055c2d4334537955bd544678
7
+ data.tar.gz: 49c5b9a1d34de8335b815e7e56070d2fa9bfdaa2a9a931f53c93ac28bff6e3da03f307f57c5faf8155bf0c01ef7ee5a61956d0605cc7d372247f184da0b0c3a6
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
  ## [Unreleased]
8
8
  Nothing to record here.
9
9
 
10
+ ## [0.5.5] - 2020-11-10
11
+ ### Add
12
+ - Add more methods for `Timestamp` class.
13
+ - most of them are delegated to Time class
14
+ - some of them are useful to manipulate `Timestamp` object as
15
+ `String`.
16
+
17
+ ## [0.5.4] - 2020-11-05
18
+ ### Add
19
+ - Add a feature for `Repository#update` to keep timestamp unchanged
20
+ - add the third argument as:
21
+ - `Repository#update(timestamp, text, keep_stamp = false)`
22
+
23
+ ## [0.5.3] - 2020-11-03
24
+ ### Changed
25
+ - Fix issue #38: fix typo in code for FileSystemRepository.
26
+
27
+ ## [0.5.2] - 2020-11-03
28
+ ### Changed
29
+ - Fix issue #34:
30
+ - fix FileSystemRepository#entries to accept "yyyymo" pattern as a
31
+ Timestamp pattern.
32
+ - Fix issue #33: fix typo in the doc for FileSystemRepository.new.
33
+ - Fix issue #31: unfriendly error message of Timestamp.parse_s.
34
+
35
+ ## [0.5.1] - 2020-11-02
36
+ ### Changed
37
+ - Fix issue #28.
38
+ - Modify `Repository#update` to do nothing when the given text is
39
+ identical to the one in the repository.
40
+
10
41
  ## [0.5.0] - 2020-11-01
11
42
  ### Added
12
43
  - Add a new API `Repository#search`.
@@ -22,15 +22,26 @@ module Textrepo
22
22
 
23
23
  # :stopdoc:
24
24
  module ErrMsg
25
- UNKNOWN_REPO_TYPE = 'unknown type for repository: %s'
26
- DUPLICATE_TIMESTAMP = 'duplicate timestamp: %s'
27
- EMPTY_TEXT = 'empty text'
28
- MISSING_TIMESTAMP = 'missing timestamp: %s'
25
+ ARGUMENT_RANGE = "argument out of range: %s"
26
+ UNKNOWN_REPO_TYPE = "unknown type for repository: %s"
27
+ DUPLICATE_TIMESTAMP = "duplicate timestamp: %s"
28
+ EMPTY_TEXT = "empty text"
29
+ MISSING_TIMESTAMP = "missing timestamp: %s"
29
30
  INVALID_TIMESTAMP_STRING = "invalid string as timestamp: %s"
30
31
  INVALID_SEARCH_RESULT = "invalid result by searcher: %s"
31
32
  end
32
33
  # :startdoc:
33
34
 
35
+ ##
36
+ # An error raised if argument is out of range for Timestamp class.
37
+
38
+ class ArgumentRangeError < Error
39
+ def initialize(arg)
40
+ super(ErrMsg::ARGUMENT_RANGE % arg)
41
+ end
42
+ end
43
+
44
+
34
45
  ##
35
46
  # An error raised if unknown type was specified as the repository
36
47
  # type.
@@ -67,9 +67,10 @@ module Textrepo
67
67
  # were not defined in `conf`.
68
68
  #
69
69
  # Be careful to set `:searcher_options`, it must be to specify the
70
- # searcher behavior equivalent to `grep` with "-inR". The default
71
- # value for the searcher options is defined for BSD grep (default
72
- # grep on macOS), GNU grep, and ripgrep (aka rg). They are:
70
+ # searcher behavior equivalent to `grep` with "-inRE". The
71
+ # default values for the searcher options is defined for BSD grep
72
+ # (default grep on macOS), GNU grep, and ripgrep (aka rg). They
73
+ # are:
73
74
  #
74
75
  # "grep" => ["-i", "-n", "-R", "-E"]
75
76
  # "egrep" => ["-i", "-n", "-R"]
@@ -77,7 +78,7 @@ module Textrepo
77
78
  # "gegrep" => ["-i", "-n", "-R"]
78
79
  # "rg" => ["-S", "-n", "--no-heading", "--color", "never"]
79
80
  #
80
- # If use those 3 searchers, it is not recommended to set
81
+ # If use those searchers, it is not recommended to set
81
82
  # `:searcher_options`. The default value works well in
82
83
  # `textrepo`.
83
84
  #
@@ -129,26 +130,32 @@ module Textrepo
129
130
  end
130
131
 
131
132
  ##
132
- # Updates the file content in the repository. A new timestamp
133
- # will be attached to the text.
133
+ # Updates the file content in the repository. A new Timestamp
134
+ # object will be attached to the text. Then, returns the new
135
+ # Timestamp object.
136
+ #
137
+ # When true is passed as the third argument, keeps the Timestamp
138
+ # unchanged, though updates the content. Then, returns the given
139
+ # Timestamp object.
140
+ #
141
+ # See the documentation of Repository#update to know about errors
142
+ # and constraints of this method.
134
143
  #
135
144
  # :call-seq:
136
- # update(Timestamp, Array) -> Timestamp
145
+ # update(Timestamp, Array, true or false) -> Timestamp
137
146
 
138
- def update(timestamp, text)
147
+ def update(timestamp, text, keep_stamp = false)
139
148
  raise EmptyTextError if text.empty?
140
- org_abs = abspath(timestamp)
141
- raise MissingTimestampError, timestamp unless FileTest.exist?(org_abs)
149
+ raise MissingTimestampError, timestamp unless exist?(timestamp)
142
150
 
143
- # the text must be stored with the new timestamp
144
- new_stamp = Timestamp.new(Time.now)
145
- new_abs = abspath(new_stamp)
146
- write_text(new_abs, text)
151
+ # does nothing if given text is the same in the repository one
152
+ return timestamp if read(timestamp) == text
147
153
 
148
- # delete the original file in the repository
149
- FileUtils.remove_file(org_abs)
154
+ stamp = keep_stamp ? timestamp : Timestamp.new(Time.now)
155
+ write_text(abspath(stamp), text)
156
+ FileUtils.remove_file(abspath(timestamp)) unless keep_stamp
150
157
 
151
- new_stamp
158
+ stamp
152
159
  end
153
160
 
154
161
  ##
@@ -182,7 +189,7 @@ module Textrepo
182
189
  if exist?(stamp)
183
190
  results << stamp
184
191
  end
185
- when 0, "yyyymoddhhmiss".size, "yyyymodd".size
192
+ when 0, "yyyymoddhhmiss".size, "yyyymodd".size, "yyyymo".size
186
193
  results += find_entries(stamp_pattern)
187
194
  when 4 # "yyyy" or "modd"
188
195
  pat = nil
@@ -317,7 +324,7 @@ module Textrepo
317
324
  file = abspath(entries[0])
318
325
  o, s = Open3.capture2(searcher, *find_searcher_options(searcher),
319
326
  pattern, file)
320
- if s.success? && (! o.empty)
327
+ if s.success? && (! o.empty?)
321
328
  output += o.lines.map { |line|
322
329
  # add filename at the beginning of the search result line
323
330
  [file, line.chomp].join(":")
@@ -333,7 +340,7 @@ module Textrepo
333
340
  files = find_files(entries)
334
341
  o, s = Open3.capture2(searcher, *find_searcher_options(searcher),
335
342
  pattern, *files)
336
- if s.success? && (! o.empty)
343
+ if s.success? && (! o.empty?)
337
344
  output += o.lines.map(&:chomp)
338
345
  end
339
346
  end
@@ -43,13 +43,27 @@ module Textrepo
43
43
  def read(timestamp); []; end
44
44
 
45
45
  ##
46
- # Updates the content with text in the repository, which is
47
- # associated to the timestamp. Returns the timestamp.
46
+ # Updates the content with given text in the repository, which is
47
+ # associated to the given Timestamp object. Returns the Timestamp
48
+ # newly generated during the execution.
49
+ #
50
+ # When true is passed as the third argument, keeps the Timestamp
51
+ # unchanged, though updates the content. Then, returns the given
52
+ # Timestamp object.
53
+ #
54
+ # If the given Timestamp object is not existed as a Timestamp
55
+ # attached to text in the repository, raises
56
+ # MissingTimestampError.
57
+ #
58
+ # If the given text is empty, raises EmptyTextError.
59
+ #
60
+ # If the given text is identical to the text in the repository,
61
+ # does nothing. Returns the given timestamp itself.
48
62
  #
49
63
  # :call-seq:
50
- # update(Timestamp, Array) -> Timestamp
64
+ # update(Timestamp, Array, true or false) -> Timestamp
51
65
 
52
- def update(timestamp, text); timestamp; end
66
+ def update(timestamp, text, keep_stamp = false); timestamp; end
53
67
 
54
68
  ##
55
69
  # Deletes the content in the repository, which is associated to
@@ -1,3 +1,5 @@
1
+ require "forwardable"
2
+
1
3
  module Textrepo
2
4
  ##
3
5
  # Timestamp is generated from a Time object. It converts a time to
@@ -18,6 +20,7 @@ module Textrepo
18
20
 
19
21
  class Timestamp
20
22
  include Comparable
23
+ extend Forwardable
21
24
 
22
25
  ##
23
26
  # Time object which generates the Timestamp object.
@@ -29,16 +32,28 @@ module Textrepo
29
32
 
30
33
  attr_reader :suffix
31
34
 
35
+ ##
36
+ # String object which is regarded as a value of Timestamp object.
37
+ # The value is generated from @time and @suffix.
38
+
39
+ attr_reader :str
40
+
32
41
  ##
33
42
  # Creates a Timestamp object from a Time object. In addition, an
34
43
  # Integer can be passed as a suffix use.
35
44
  #
45
+ # Since Textrepo adapts 1 second as the time resolution, the
46
+ # subsec part of a given time will be ignored.
47
+ #
36
48
  # :call-seq:
37
49
  # new(Time, Integer = nil) -> Timestamp
38
50
 
39
51
  def initialize(time, suffix = nil)
40
- @time = time
52
+ raise ArgumentRangeError, suffix unless is_valid_suffix?(suffix)
53
+ parts = [:year, :mon, :day, :hour, :min, :sec].map{ |s| time.send(s) }
54
+ @time = Time.new(*parts)
41
55
  @suffix = suffix
56
+ @str = time_to_str(@time, @suffix)
42
57
  end
43
58
 
44
59
  def <=>(other) # :nodoc:
@@ -51,19 +66,257 @@ module Textrepo
51
66
  end
52
67
 
53
68
  ##
54
- # Generate an obvious time string.
69
+ # Generates an obvious time string.
55
70
  #
56
71
  # %Y %m %d %H %M %S suffix
57
72
  # "2020-12-30 12:34:56 (0 | nil)" -> "20201230123456"
58
73
  # "2020-12-30 12:34:56 (7)" -> "20201230123456_007"
59
74
 
60
75
  def to_s
76
+ @str
77
+ end
78
+
79
+ alias to_str to_s
80
+
81
+ # :stopdoc:
82
+
83
+ # delegators to Time object
84
+
85
+ def_instance_delegators :@time, :year, :mon, :day, :hour, :min, :sec
86
+ def_instance_delegators :@time, :wday, :monday?, :tuesday?, :wednesday?, :thursday?, :friday?, :saturday?, :sunday?
87
+ def_instance_delegators :@time, :asctime, :ctime, :strftime
88
+ def_instance_delegators :@time, :subsec, :nsec, :usec
89
+ def_instance_delegators :@time, :tv_nsec, :tv_sec, :tv_usec
90
+ def_instance_delegators :@time, :to_f, :to_i, :to_r
91
+ def_instance_delegators :@time, :yday, :mday
92
+ def_instance_delegators :@time, :month
93
+
94
+ # :startdoc:
95
+
96
+ def hash # :nodoc:
97
+ @str[0, 14].to_i * 1000 + @suffix.to_i
98
+ end
99
+
100
+ def eql?(other) # :nodoc:
101
+ other.is_a?(Timestamp) && @time == other.time && @suffix == other.suffix
102
+ end
103
+
104
+ ##
105
+ # Returns a new Timestamp object which is given seconds ahead.
106
+ # Even if the suffix is not nil, the new Timestamp object will
107
+ # always have nil as its suffix.
108
+ #
109
+ # :call-seq:
110
+ # +(Integer) -> Timestamp
111
+
112
+ def +(seconds)
113
+ Timestamp.new(@time + seconds, nil)
114
+ end
115
+
116
+ ##
117
+ # Returns difference of seconds between self and an argument. If
118
+ # the argument is an Integer object, returns a new Timestamp
119
+ # object which is the given seconds behind.
120
+ #
121
+ # Even if the suffix is not nil, the new Timestamp object will
122
+ # always have nil as its suffix.
123
+ #
124
+ # :call-seq:
125
+ # -(Time) -> Float
126
+ # -(Timetamp) -> Float
127
+ # -(Integer) -> Timestamp
128
+
129
+ def -(arg)
130
+ case arg
131
+ when Time
132
+ @time - arg
133
+ when Timestamp
134
+ @time - arg.time
135
+ when Integer
136
+ Timestamp.new(@time - arg, nil)
137
+ when NilClass
138
+ raise TypeError, "can't convert nil into an exact number"
139
+ else
140
+ raise ArgumentError, arg
141
+ end
142
+ end
143
+
144
+ ##
145
+ # Generates an array contains components of the Timestamp object.
146
+ # Components means "year", "mon", "day", "hour", "min", "sec", and
147
+ # "suffix".
148
+
149
+ def to_a
150
+ a = [:year, :mon, :day, :hour, :min, :sec, :suffix].map { |s| self.send(s) }
151
+ a.delete_at(-1) if a[-1].nil?
152
+ a
153
+ end
154
+
155
+ # :stopdoc:
156
+
157
+ # delegators to String object
158
+
159
+ def_instance_delegators :@str, :size, :length
160
+ def_instance_delegators :@str, :include?, :match, :match?
161
+
162
+ # :startdoc:
163
+
164
+ ##
165
+ # Returns a character or sub-string specified with args.
166
+ #
167
+ # Following type of objects could be used as args:
168
+ #
169
+ # - Integer : specifies an index
170
+ # - Integer, Integer : specified an start index and length of sub-string
171
+ # - Range : specified range of sub-string
172
+ # - Symbol : specified a type of part
173
+ #
174
+ # Following symbols could be specified:
175
+ #
176
+ # - :year
177
+ # - :mon, or :month
178
+ # - :day
179
+ # - :hour
180
+ # - :min
181
+ # - :sec
182
+ # - :suffix
183
+ #
184
+ # :call-seq:
185
+ # self[nth as Integer] -> String | nil
186
+ # self[nth as Integer, len as Integer] -> String | nil
187
+ # self[range as Range] -> String
188
+ # self[symbol as Symbol] -> String
189
+
190
+ def [](*args)
191
+ raise ArgumentError, "wrong number of arguments (given %s, execpted 1..2)" % args.size unless (1..2).include?(args.size)
192
+
193
+ arg = args[0]
194
+ case arg
195
+ when Symbol, String
196
+ key = arg.to_sym
197
+ if key == :suffix
198
+ @suffix.nil? ? nil : FMTSTRS[key] % @suffix
199
+ elsif FMTSTRS.keys.include?(key)
200
+ @time.strftime(FMTSTRS[key])
201
+ else
202
+ nil
203
+ end
204
+ else
205
+ @str[*args]
206
+ end
207
+ end
208
+
209
+ alias slice []
210
+
211
+ ##
212
+ # Returns a Timestamp object which has a next Time object.
213
+ #
214
+ # If true was passed as an argument, use incremented suffix as
215
+ # base instead of a next Time object.
216
+ #
217
+ # For example,
218
+ #
219
+ # "20201110160100" -> "20201110160101" (false as arg)
220
+ # "20201110160100" -> "20201110160100_001" (true as arg)
221
+ # "20201110160200_001" -> "20201110160201" (false as arg)
222
+ # "20201110160200_001" -> "20201110160200_002" (true as arg)
223
+ #
224
+ # If suffix was 999 before call this method, raises
225
+ # ArgumentRangeError.
226
+
227
+ def next(use_suffix = nil)
228
+ if use_suffix
229
+ Timestamp.new(@time, increase_suffix(@suffix.to_i, 1))
230
+ else
231
+ Timestamp.new(@time + 1, nil)
232
+ end
233
+ end
234
+
235
+ alias succ next
236
+
237
+ ##
238
+ # Updates the time value to a next Time destructively. See the
239
+ # document for Timestamp#next for more details.
240
+ #
241
+ # If suffix was 999 before call this method, raises
242
+ # ArgumentRangeError.
243
+
244
+ def next!(use_suffix = nil)
245
+ if use_suffix
246
+ @suffix = increase_suffix(@suffix.to_i, 1)
247
+ else
248
+ @time += 1
249
+ @suffix = nil
250
+ end
251
+ @str = time_to_str(@time, @suffix)
252
+ self
253
+ end
254
+
255
+ alias succ! next!
256
+
257
+ ##
258
+ # Splits the timestamp string into array of time parts, such as
259
+ # year, month, day, hour, minute, and second. Then, returns the
260
+ # array.
261
+ #
262
+ # When a block was passed, it would apply to each part of the
263
+ # array. Then, returns self.
264
+
265
+ def split(_ = $;, _ = 0, &blk)
266
+ parts = Timestamp.split_stamp(@str)
267
+ if blk.nil?
268
+ parts
269
+ else
270
+ parts.each { |p| yield p }
271
+ self
272
+ end
273
+ end
274
+
275
+ # :stopdoc:
276
+
277
+ def initialize_copy(_)
278
+ @time = @time.dup
279
+ @suffix = @suffix
280
+ @str = @str.dup
281
+ end
282
+
283
+ def freeze; @time.freeze; @suffix.freeze; @str.freeze; end
284
+ def taint; @time.taint; @suffix.taint; @str.taint; end
285
+ def untaint; @time.untaint; @suffix.untaint; @str.untaint; end
286
+
287
+ private
288
+
289
+ def is_valid_suffix?(suffix)
290
+ suffix.nil? || (0..999).include?(suffix)
291
+ end
292
+
293
+ def increase_suffix(suffix, num)
294
+ increased = suffix + num
295
+ raise ArgumentRangeError, suffix unless is_valid_suffix?(increased)
296
+ increased
297
+ end
298
+
299
+ def time_to_str(time, suffix = nil)
61
300
  s = @time.strftime("%Y%m%d%H%M%S")
62
301
  s += "_#{"%03u" % @suffix}" unless @suffix.nil? || @suffix == 0
63
302
  s
64
303
  end
65
304
 
305
+ FMTSTRS = {
306
+ :year => "%Y", :mon => "%m", :month => "%m", :day => "%d",
307
+ :hour => "%H", :min => "%M", :sec => "%S", :suffix => "%03u",
308
+ }
309
+
310
+ # :startdoc:
66
311
  class << self
312
+
313
+ ##
314
+ # Returns a Timestamp object generated from the current time.
315
+
316
+ def now(suffix = nil)
317
+ Timestamp.new(Time.now, suffix)
318
+ end
319
+
67
320
  ##
68
321
  # Splits a string which represents a timestamp into components.
69
322
  # Each component represents a part of constructs to instantiate
@@ -79,7 +332,8 @@ module Textrepo
79
332
  raise InvalidTimestampStringError, stamp_str if stamp_str.nil?
80
333
  # yyyy mo dd hh mi ss sfx
81
334
  a = [0..3, 4..5, 6..7, 8..9, 10..11, 12..13, 15..17].map {|r| stamp_str[r]}
82
- a[-1].nil? ? a[0..-2] : a
335
+ a.delete_at(-1) if a[-1].nil?
336
+ a
83
337
  end
84
338
 
85
339
  ##
@@ -98,10 +352,17 @@ module Textrepo
98
352
  ye, mo, da, ho, mi, se, sfx = split_stamp(stamp_str).map(&:to_i)
99
353
  Timestamp.new(Time.new(ye, mo, da, ho, mi, se), sfx)
100
354
  rescue InvalidTimestampStringError, ArgumentError => _
101
- raise InvalidTimestampStringError, stamp_str
355
+ emsg = if stamp_str.nil?
356
+ "(nil)"
357
+ elsif stamp_str.empty?
358
+ "(empty string)"
359
+ else
360
+ stamp_str
361
+ end
362
+ raise InvalidTimestampStringError, emsg
102
363
  end
103
364
  end
104
-
105
365
  end
366
+
106
367
  end
107
368
  end
@@ -1,3 +1,3 @@
1
1
  module Textrepo
2
- VERSION = '0.5.0'
2
+ VERSION = '0.5.5'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textrepo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - mnbi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-01 00:00:00.000000000 Z
11
+ date: 2020-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler