textrepo 0.5.4 → 0.5.9

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: fa38f5fbd3d1fd393eeb4ac200d1c051d8ff082cd92beaf0ab1b3756efd79e92
4
- data.tar.gz: 70107459347c9685f722a4aa2367c1f04ec3257c12c489a1436d242e2f322f4f
3
+ metadata.gz: 9e4c8bee5a07bd1bd578fa838c0897358a7436c76246e672f555b7cc88540323
4
+ data.tar.gz: 2e7894038fdf7d8e3ffde5993d37d83b574861195e4df1bbc3494bae26937dc0
5
5
  SHA512:
6
- metadata.gz: 19e12a6e1ac352a005aca887e887089a8daa8ae8fa0573e3d640803f19950747addd52ed77ddb9da9534f7b9596cc125917c9892ea2a14a031aaae33df753e01
7
- data.tar.gz: 3c304dbd6330719398ee377183ac26f29ffdfa89b279ff12fac795e9ad664e7b3b249c8f79d36e3bae215e7e114064dd01d3bb23b6652cd2147c8c09382d01ff
6
+ metadata.gz: bc3ada372e37db9a400c69c7fc5cbfbb59fb2e54878cc5d51e51f31ae6769fd79ef6e4871e1a27c1f3901442aae4c3e683722fa957a4a714f96bdbbf133693b5
7
+ data.tar.gz: 11b86ca186db89e6c68909be0dfd0a1fc1b27203fec77bad6f6b3a93f69328eff83b7796a458eb4e8a9c6fad0febed554571f828eea68c7a1c9cf582d64bcfce
@@ -0,0 +1,18 @@
1
+ name: Build
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0.0
14
+ - name: Run the default task
15
+ run: |
16
+ gem install bundler -v 2.2.3
17
+ bundle install
18
+ bundle exec rake
data/CHANGELOG.md CHANGED
@@ -5,7 +5,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/)
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
- Nothing to record here.
8
+
9
+ ## [0.5.9] - 2021-05-25
10
+ ### Fixed
11
+ - Fix issue #54: add check argument in some methods.
12
+ - Fix issue #52: remove trailing spaces.
13
+
14
+ ### Changed
15
+ - Update copyright year in `LICENSE`. (#53)
16
+ - Use GitHub/Actions instead of Travis-CI.
17
+
18
+ ## [0.5.8] - 2021-03-23
19
+ ### Add
20
+ - Add a section to describe about **text** into `README.md`.
21
+
22
+ ### Fixed
23
+ - Fix issue #49: Timestamp.parse_s will cause a crash.
24
+
25
+ ## [0.5.7] - 2020-11-16
26
+ ### Fixed
27
+ - Fix issue #47: mmdd pattern matches incorrectly (`#entries`).
28
+
29
+ ## [0.5.6] - 2020-11-11
30
+ ### Add
31
+ - Change `Repository` to enumerable.
32
+ - add `#each` method to `Repository`, then include `Enumerable`.
33
+ - Add "-H" option to some searcher default options.
34
+
35
+ ## [0.5.5] - 2020-11-10
36
+ ### Add
37
+ - Add more methods for `Timestamp` class.
38
+ - most of them are delegated to Time class
39
+ - some of them are useful to manipulate `Timestamp` object as
40
+ `String`.
9
41
 
10
42
  ## [0.5.4] - 2020-11-05
11
43
  ### Add
@@ -14,11 +46,11 @@ Nothing to record here.
14
46
  - `Repository#update(timestamp, text, keep_stamp = false)`
15
47
 
16
48
  ## [0.5.3] - 2020-11-03
17
- ### Changed
49
+ ### Fixed
18
50
  - Fix issue #38: fix typo in code for FileSystemRepository.
19
51
 
20
52
  ## [0.5.2] - 2020-11-03
21
- ### Changed
53
+ ### Fixed
22
54
  - Fix issue #34:
23
55
  - fix FileSystemRepository#entries to accept "yyyymo" pattern as a
24
56
  Timestamp pattern.
@@ -26,7 +58,7 @@ Nothing to record here.
26
58
  - Fix issue #31: unfriendly error message of Timestamp.parse_s.
27
59
 
28
60
  ## [0.5.1] - 2020-11-02
29
- ### Changed
61
+ ### Fixed
30
62
  - Fix issue #28.
31
63
  - Modify `Repository#update` to do nothing when the given text is
32
64
  identical to the one in the repository.
@@ -1,6 +1,6 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2020 mnbi
3
+ Copyright (c) 2020, 2021 mnbi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Textrepo
2
2
 
3
- [![Build Status](https://travis-ci.org/mnbi/textrepo.svg?branch=main)](https://travis-ci.org/mnbi/textrepo)
3
+ [![Build Status](https://github.com/mnbi/textrepo/workflows/Build/badge.svg)](https://github.com/mnbi/textrepo/actions?query=workflow%3A"Build")
4
+ [![CodeFactor](https://www.codefactor.io/repository/github/mnbi/textrepo/badge)](https://www.codefactor.io/repository/github/mnbi/textrepo)
4
5
 
5
6
  Textrepo is a repository to store a note with a timestamp. Each note
6
7
  in the repository operates with the associated timestamp.
@@ -61,6 +62,32 @@ stamps.each { |stamp|
61
62
  Also see `examples` directory. There is a small tool to demonstrate
62
63
  how to use `textrepo`.
63
64
 
65
+ ## What is TEXT?
66
+
67
+ In macOS (or similar unix OS), text is a date stored into a regular
68
+ file. Its characteristics are;
69
+
70
+ - a character stream coded in some encoding system (such UTF-8),
71
+ - divided into multiple physical lines with newline character (`\n`).
72
+
73
+ In `textrepo` and its client program, a **text** is usually generated
74
+ from a text file mentioned above. It is;
75
+
76
+ - a character stream coded in UTF-8,
77
+ - consists of multiple logical lines (each of them does not contain a
78
+ newline character).
79
+
80
+ That is, newline characters are removed when text is read from a file
81
+ and added appropriately when it is written into a file.
82
+
83
+ So, **text** is represented with Ruby objects as follows:
84
+
85
+ - **Text** is represented with an `Array` object which contains
86
+ multiple `String` objects.
87
+ - A `String` object represents a **logical line** of **text**.
88
+ - Each `String` does not contain a newline character.
89
+ - An empty string ("") represents a empty line.
90
+
64
91
  ## Development
65
92
 
66
93
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -43,14 +43,8 @@ USAGE
43
43
  def execute(_, conf)
44
44
  name = conf[:repository_name]
45
45
  base = conf[:repository_base]
46
- type = conf[:repository_type]
47
46
 
48
- puts case type
49
- when :file_system
50
- "#{base}/#{name}"
51
- else
52
- "#{base}/#{name}"
53
- end
47
+ puts "#{base}/#{name}"
54
48
  end
55
49
  end
56
50
 
@@ -17,7 +17,7 @@ module Rbnotes
17
17
  begin
18
18
  repo.create(stamp, content)
19
19
  break # success to create a note
20
- rescue Textrepo::DuplicateTimestampError => e
20
+ rescue Textrepo::DuplicateTimestampError => _
21
21
  puts "A text with the timestamp [%s] has been already exists" \
22
22
  " in the repository." % stamp
23
23
 
@@ -38,7 +38,7 @@ module Rbnotes
38
38
  puts "Try to create a note again with a new " \
39
39
  "timestamp [%s]." % stamp
40
40
  end
41
- rescue Textrepo::EmptyTextError => e
41
+ rescue Textrepo::EmptyTextError => _
42
42
  puts "... aborted."
43
43
  puts "The specified file is empyt."
44
44
  exit 1 # error
@@ -36,7 +36,7 @@ module Rbnotes
36
36
  prefix = '# '
37
37
  subject = prefix + subject.lstrip if subject[0, 2] != prefix
38
38
 
39
- ts_part = "#{timestamp_str} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
39
+ ts_part = "#{timestamp_str} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
40
40
  sj_part = truncate_str(subject, subject_width)
41
41
 
42
42
  ts_part + delimiter + sj_part
@@ -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.
@@ -62,7 +62,7 @@ module Textrepo
62
62
  #
63
63
  # The root path of the repository looks like the following:
64
64
  # - conf[:repository_base]/conf[:repository_name]
65
- #
65
+ #
66
66
  # Default values are set when `:repository_name` and `:default_extname`
67
67
  # were not defined in `conf`.
68
68
  #
@@ -89,7 +89,7 @@ module Textrepo
89
89
  super
90
90
  base = conf[:repository_base]
91
91
  @name ||= FAVORITE_REPOSITORY_NAME
92
- @path = File.expand_path("#{name}", base)
92
+ @path = File.expand_path(name, base)
93
93
  FileUtils.mkdir_p(@path)
94
94
  @extname = conf[:default_extname] || FAVORITE_EXTNAME
95
95
  @searcher = find_searcher(conf[:searcher])
@@ -181,16 +181,19 @@ module Textrepo
181
181
  # entries(String = nil) -> Array of Timestamp instances
182
182
 
183
183
  def entries(stamp_pattern = nil)
184
+ size = stamp_pattern.to_s.size
185
+ return [] if size > 0 and !(/\A[\d_]+\Z/ === stamp_pattern)
186
+
184
187
  results = []
185
188
 
186
- case stamp_pattern.to_s.size
189
+ case size
187
190
  when "yyyymoddhhmiss_lll".size
188
191
  stamp = Timestamp.parse_s(stamp_pattern)
189
192
  if exist?(stamp)
190
193
  results << stamp
191
194
  end
192
195
  when 0, "yyyymoddhhmiss".size, "yyyymodd".size, "yyyymo".size
193
- results += find_entries(stamp_pattern)
196
+ results += find_entries("#{stamp_pattern}*")
194
197
  when 4 # "yyyy" or "modd"
195
198
  pat = nil
196
199
  # The following distinction is practically correct, but not
@@ -199,10 +202,10 @@ module Textrepo
199
202
  # any text (I believe...).
200
203
  if stamp_pattern.to_i > 1231
201
204
  # yyyy
202
- pat = stamp_pattern
205
+ pat = "#{stamp_pattern}*"
203
206
  else
204
207
  # modd
205
- pat = "*#{stamp_pattern}"
208
+ pat = "????#{stamp_pattern}*"
206
209
  end
207
210
  results += find_entries(pat)
208
211
  end
@@ -270,7 +273,7 @@ module Textrepo
270
273
  end
271
274
 
272
275
  def find_entries(stamp_pattern)
273
- Dir.glob("#{@path}/**/#{stamp_pattern}*.#{@extname}").map { |e|
276
+ Dir.glob("#{@path}/**/#{stamp_pattern}.#{@extname}").map { |e|
274
277
  begin
275
278
  Timestamp.parse_s(timestamp_str(e))
276
279
  rescue InvalidTimestampStringError => _
@@ -315,22 +318,10 @@ module Textrepo
315
318
  # one file.
316
319
 
317
320
  def invoke_searcher_for_entries(searcher, pattern, entries)
321
+ return [] if entries.empty?
318
322
  output = []
319
323
 
320
- num_of_entries = entries.size
321
- if num_of_entries == 1
322
- # If the search taget is one file, the output needs special
323
- # treatment.
324
- file = abspath(entries[0])
325
- o, s = Open3.capture2(searcher, *find_searcher_options(searcher),
326
- pattern, file)
327
- if s.success? && (! o.empty?)
328
- output += o.lines.map { |line|
329
- # add filename at the beginning of the search result line
330
- [file, line.chomp].join(":")
331
- }
332
- end
333
- elsif num_of_entries > LIMIT_OF_FILES
324
+ if entries.size > LIMIT_OF_FILES
334
325
  output += invoke_searcher_for_entries(searcher, pattern, entries[0..(LIMIT_OF_FILES - 1)])
335
326
  output += invoke_searcher_for_entries(searcher, pattern, entries[LIMIT_OF_FILES..-1])
336
327
  else
@@ -349,14 +340,16 @@ module Textrepo
349
340
  end
350
341
 
351
342
  SEARCHER_OPTS = {
352
- # case insensitive, print line number, recursive search, work as egrep
353
- "grep" => ["-i", "-n", "-R", "-E"],
354
- # case insensitive, print line number, recursive search
355
- "egrep" => ["-i", "-n", "-R"],
356
- # case insensitive, print line number, recursive search, work as gegrep
357
- "ggrep" => ["-i", "-n", "-R", "-E"],
358
- # case insensitive, print line number, recursive search
359
- "gegrep" => ["-i", "-n", "-R"],
343
+ # grep option meaning:
344
+ # - "-i": case insensitive,
345
+ # - "-n": print line number,
346
+ # - "-H": print file name,
347
+ # - "-R": recursive search,
348
+ # - "-E": work as egrep
349
+ "grep" => ["-i", "-n", "-H", "-R", "-E"],
350
+ "egrep" => ["-i", "-n", "-H", "-R"],
351
+ "ggrep" => ["-i", "-n", "-H", "-R", "-E"],
352
+ "gegrep" => ["-i", "-n", "-H", "-R"],
360
353
  # smart case, print line number, no color
361
354
  "rg" => ["-S", "-n", "--no-heading", "--color", "never"],
362
355
  }
@@ -1,6 +1,8 @@
1
1
  module Textrepo
2
2
  class Repository
3
3
 
4
+ include Enumerable
5
+
4
6
  ##
5
7
  # Repository type. It specifies which concrete repository class
6
8
  # will instantiated. For example, the type `:file_system` specifies
@@ -125,6 +127,65 @@ module Textrepo
125
127
 
126
128
  def search(pattern, stamp_pattern = nil); []; end
127
129
 
130
+ ##
131
+ # Calls the given block once for each pair of timestamp and text
132
+ # in self, passing those pair as parameter. Returns the
133
+ # repository itself.
134
+ #
135
+ # If no block is given, an Enumerator is returned.
136
+
137
+ def each(&block)
138
+ if block.nil?
139
+ entries.lazy.map { |timestamp| pair(timestamp) }.to_enum(:each)
140
+ else
141
+ entries.each { |timestamp| yield pair(timestamp) }
142
+ self
143
+ end
144
+ end
145
+
146
+ alias each_pair each
147
+
148
+ ##
149
+ # Calls the given block once for each timestamp in self, passing
150
+ # the timestamp as a parameter. Returns the repository itself.
151
+ #
152
+ # If no block is given, an Enumerator is returned.
153
+
154
+ def each_key(&block)
155
+ if block.nil?
156
+ entries.to_enum(:each)
157
+ else
158
+ entries.each(&block)
159
+ end
160
+ end
161
+
162
+ alias each_timestamp each_key
163
+
164
+ ##
165
+ # Calls the given block once for each timestamp in self, passing
166
+ # the text as a parameter. Returns the repository itself.
167
+ #
168
+ # If no block is given, an Enumerator is returned.
169
+
170
+ def each_value(&block)
171
+ if block.nil?
172
+ entries.lazy.map { |timestamp| read(timestamp) }.to_enum(:each)
173
+ else
174
+ entries.each { |timestamp| yield read(timestamp) }
175
+ end
176
+ end
177
+
178
+ alias each_text each_value
179
+
180
+ # :stopdoc:
181
+
182
+ private
183
+
184
+ def pair(timestamp)
185
+ [timestamp, read(timestamp)]
186
+ end
187
+
188
+ # :startdoc:
128
189
  end
129
190
 
130
191
  require_relative 'file_system_repository'
@@ -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
@@ -76,10 +329,11 @@ module Textrepo
76
329
  # Raises InvalidTimestampStringError if nil was passed as an arguemnt.
77
330
 
78
331
  def split_stamp(stamp_str)
79
- raise InvalidTimestampStringError, stamp_str if stamp_str.nil?
332
+ raise InvalidTimestampStringError, "(nil)" 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.map{|e| e || "" }
83
337
  end
84
338
 
85
339
  ##
@@ -94,21 +348,18 @@ module Textrepo
94
348
  # parse_s("20201028163529_034") -> Timestamp
95
349
 
96
350
  def parse_s(stamp_str)
351
+ raise InvalidTimestampStringError, "(nil)" if stamp_str.nil?
352
+ raise InvalidTimestampStringError, "(empty string)" if stamp_str.empty?
353
+ raise InvalidTimestampStringError, stamp_str if stamp_str.size < 14
354
+
97
355
  begin
98
356
  ye, mo, da, ho, mi, se, sfx = split_stamp(stamp_str).map(&:to_i)
99
357
  Timestamp.new(Time.new(ye, mo, da, ho, mi, se), sfx)
100
- rescue InvalidTimestampStringError, ArgumentError => _
101
- emsg = if stamp_str.nil?
102
- "(nil)"
103
- elsif stamp_str.empty?
104
- "(empty string)"
105
- else
106
- stamp_str
107
- end
108
- raise InvalidTimestampStringError, emsg
358
+ rescue ArgumentRangeError, ArgumentError => _
359
+ raise InvalidTimestampStringError, stamp_str
109
360
  end
110
361
  end
111
-
112
362
  end
363
+
113
364
  end
114
365
  end
@@ -1,3 +1,3 @@
1
1
  module Textrepo
2
- VERSION = '0.5.4'
2
+ VERSION = '0.5.9'
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.4
4
+ version: 0.5.9
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-05 00:00:00.000000000 Z
11
+ date: 2021-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -32,11 +32,11 @@ executables: []
32
32
  extensions: []
33
33
  extra_rdoc_files: []
34
34
  files:
35
+ - ".github/workflows/main.yml"
35
36
  - ".gitignore"
36
- - ".travis.yml"
37
37
  - CHANGELOG.md
38
38
  - Gemfile
39
- - LICENSE.txt
39
+ - LICENSE
40
40
  - README.md
41
41
  - Rakefile
42
42
  - bin/console
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
79
  requirements: []
80
- rubygems_version: 3.1.4
80
+ rubygems_version: 3.2.15
81
81
  signing_key:
82
82
  specification_version: 4
83
83
  summary: A repository to store text with timestamp.
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7.2
6
- before_install: gem install bundler -v 2.1.4