textrepo 0.5.0 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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