Class: Textrepo::FileSystemRepository

Inherits:
Repository
  • Object
show all
Defined in:
lib/textrepo/file_system_repository.rb

Overview

A concrete class which implements Repository interfaces. This repository uses the default file system of the operating system as a text storage.

Constant Summary collapse

FAVORITE_REPOSITORY_NAME =

Default name for the repository which uses when no name is specified in the configuration settings.

'notes'
FAVORITE_EXTNAME =

Default extension of notes which uses when no extname is specified in the configuration settings.

'md'
FAVORITE_SEARCHER =

Default searcher program to search text in the repository.

'grep'

Instance Attribute Summary collapse

Attributes inherited from Repository

#name, #type

Instance Method Summary collapse

Methods inherited from Repository

#each, #each_key, #each_value

Constructor Details

#initialize(conf) ⇒ FileSystemRepository

Creates a new repository object. The argument, ‘conf` must be a Hash object. It should hold the follwoing values:

  • MANDATORY:

    • :repository_type => ‘:file_system`

    • :repository_base => the parent directory path for the repository

  • OPTIONAL: (if not specified, default values are used)

    • :repository_name => basename of the root path for the repository

    • :default_extname => extname for a file stored into in the repository

    • :searcher => a program to search like ‘grep`

    • :searcher_options => an Array of option to pass to the searcher

The root path of the repository looks like the following:

Default values are set when ‘:repository_name` and `:default_extname` were not defined in `conf`.

Be careful to set ‘:searcher_options`, it must be to specify the searcher behavior equivalent to `grep` with “-inRE”. The default values for the searcher options is defined for BSD grep (default grep on macOS), GNU grep, and ripgrep (aka rg). They are:

"grep"   => ["-i", "-n", "-R", "-E"]
"egrep"  => ["-i", "-n", "-R"]
"ggrep"  => ["-i", "-n", "-R", "-E"]
"gegrep" => ["-i", "-n", "-R"]
"rg"     => ["-S", "-n", "--no-heading", "--color", "never"]

If use those searchers, it is not recommended to set ‘:searcher_options`. The default value works well in `textrepo`.

:call-seq:

new(Hash or Hash like object) -> FileSystemRepository


88
89
90
91
92
93
94
95
96
97
# File 'lib/textrepo/file_system_repository.rb', line 88

def initialize(conf)
  super
  base = conf[:repository_base]
  @name ||= FAVORITE_REPOSITORY_NAME
  @path = File.expand_path(name, base)
  FileUtils.mkdir_p(@path)
  @extname = conf[:default_extname] || FAVORITE_EXTNAME
  @searcher = find_searcher(conf[:searcher])
  @searcher_options = conf[:searcher_options]
end

Instance Attribute Details

#extnameObject (readonly)

Extension of notes sotres in the repository.



21
22
23
# File 'lib/textrepo/file_system_repository.rb', line 21

def extname
  @extname
end

#pathObject (readonly)

Repository root.



16
17
18
# File 'lib/textrepo/file_system_repository.rb', line 16

def path
  @path
end

#searcherObject (readonly)

Searcher program name.



26
27
28
# File 'lib/textrepo/file_system_repository.rb', line 26

def searcher
  @searcher
end

#searcher_optionsObject (readonly)

An array of options to pass to the searcher program.



31
32
33
# File 'lib/textrepo/file_system_repository.rb', line 31

def searcher_options
  @searcher_options
end

Instance Method Details

#create(timestamp, text) ⇒ Object

Creates a file into the repository, which contains the specified text and is associated to the timestamp.

:call-seq:

create(Timestamp, Array) -> Timestamp


106
107
108
109
110
111
112
113
# File 'lib/textrepo/file_system_repository.rb', line 106

def create(timestamp, text)
  abs = abspath(timestamp)
  raise DuplicateTimestampError, timestamp if FileTest.exist?(abs)
  raise EmptyTextError if text.nil? || text.empty?

  write_text(abs, text)
  timestamp
end

#delete(timestamp) ⇒ Object

Deletes the file in the repository.

:call-seq:

delete(Timestamp) -> Array


167
168
169
170
171
172
173
174
175
# File 'lib/textrepo/file_system_repository.rb', line 167

def delete(timestamp)
  abs = abspath(timestamp)
  raise MissingTimestampError, timestamp unless FileTest.exist?(abs)
  content = read(timestamp)

  FileUtils.remove_file(abs)

  content
end

#entries(stamp_pattern = nil) ⇒ Object

Finds entries of text those timestamp matches the specified pattern.

:call-seq:

entries(String = nil) -> Array of Timestamp instances


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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/textrepo/file_system_repository.rb', line 183

def entries(stamp_pattern = nil)
  size = stamp_pattern.to_s.size
  return [] if size > 0 and !(/\A[\d_]+\Z/ === stamp_pattern)

  results = []

  case size
  when "yyyymoddhhmiss_lll".size
    stamp = Timestamp.parse_s(stamp_pattern)
    if exist?(stamp)
      results << stamp
    end
  when 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
    # 0 <--- ""
    # 5 <--- "yyyy m" (incomplete)
    # 6 <--- "yyyy mo"
    # 7 <--- "yyyy mo d" (incomplete)
    # 8 <--- "yyyy mo dd"
    # 9 <--- "yyyy mo dd h" (incomplete)
    # 10 <-- "yyyy mo dd hh"
    # 11 <-- "yyyy mo dd hh m" (incomplete)
    # 12 <-- "yyyy mo dd hh mm"
    # 13 <-- "yyyy mo dd hh mm s" (incomplete)
    # 14 <-- "yyyy mo dd hh mm ss"
    results += find_entries("#{stamp_pattern}*")
  when 4                    # "yyyy" or "modd"
    pat = nil
    # The following distinction is practically correct, but not
    # perfect.  It simply assumes that a year is greater than
    # 1231.  For, a year before 1232 is too old for us to create
    # any text (I believe...).
    if stamp_pattern.to_i > 1231
      # yyyy
      pat = "#{stamp_pattern}*"
    else
      # modd
      pat = "????#{stamp_pattern}*"
    end
    results += find_entries(pat)
  end

  results
end

#exist?(timestamp) ⇒ Boolean

Check the existence of text which is associated with the given timestamp.

:call-seq:

exist?(Timestamp) -> true or false

Returns:

  • (Boolean)


234
235
236
# File 'lib/textrepo/file_system_repository.rb', line 234

def exist?(timestamp)
  FileTest.exist?(abspath(timestamp))
end

#read(timestamp) ⇒ Object

Reads the file content in the repository. Then, returns its content.

:call-seq:

read(Timestamp) -> Array


122
123
124
125
126
127
128
129
130
# File 'lib/textrepo/file_system_repository.rb', line 122

def read(timestamp)
  abs = abspath(timestamp)
  raise MissingTimestampError, timestamp unless FileTest.exist?(abs)
  content = nil
  File.open(abs, 'r') { |f|
    content = f.readlines(chomp: true)
  }
  content
end

#search(pattern, stamp_pattern = nil) ⇒ Object

Searches a pattern in all text. The given pattern is a word to search or a regular expression. The pattern would be passed to a searcher program as it passed.

See the document for Textrepo::Repository#search to know about the search result.

:call-seq:

search(String for pattern, String for Timestamp pattern) -> Array


249
250
251
252
253
254
255
256
257
# File 'lib/textrepo/file_system_repository.rb', line 249

def search(pattern, stamp_pattern = nil)
  result = nil
  if stamp_pattern.nil?
    result = invoke_searcher_at_repo_root(@searcher, pattern)
  else
    result = invoke_searcher_for_entries(@searcher, pattern, entries(stamp_pattern))
  end
  construct_search_result(result)
end

#update(timestamp, text, keep_stamp = false) ⇒ Object

Updates the file content in the repository. A new Timestamp object will be attached to the text. Then, returns the new Timestamp object.

When true is passed as the third argument, keeps the Timestamp unchanged, though updates the content. Then, returns the given Timestamp object.

See the documentation of Repository#update to know about errors and constraints of this method.

:call-seq:

update(Timestamp, Array, true or false) -> Timestamp

Raises:



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/textrepo/file_system_repository.rb', line 147

def update(timestamp, text, keep_stamp = false)
  raise EmptyTextError if text.empty?
  raise MissingTimestampError, timestamp unless exist?(timestamp)

  # does nothing if given text is the same in the repository one
  return timestamp if read(timestamp) == text

  stamp = keep_stamp ? timestamp : Timestamp.new(Time.now)
  write_text(abspath(stamp), text)
  FileUtils.remove_file(abspath(timestamp)) unless keep_stamp

  stamp
end