rbnotes 0.4.10 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/Gemfile.lock +1 -1
- data/etc/zsh/_rbnotes +93 -0
- data/exe/rbnotes +1 -0
- data/lib/rbnotes.rb +1 -0
- data/lib/rbnotes/commands.rb +3 -2
- data/lib/rbnotes/commands/import.rb +26 -5
- data/lib/rbnotes/commands/show.rb +60 -15
- data/lib/rbnotes/commands/statistics.rb +55 -0
- data/lib/rbnotes/conf.rb +18 -7
- data/lib/rbnotes/error.rb +11 -0
- data/lib/rbnotes/statistics.rb +101 -0
- data/lib/rbnotes/utils.rb +42 -14
- data/lib/rbnotes/version.rb +2 -2
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 4170ccc84305971d10f3726351cdc4138b5fabaa7293bc10a827ed0458f28b8c
         | 
| 4 | 
            +
              data.tar.gz: 291efc32e2e65462e8c6996ecbbc7ffbb4e6887a5d0065bbb843d01dfbb8c78c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 1f6d2659c1a50f462867a53bff3f7b45e8b38035c3af44407b70d359c6af9409481f1033059384bd8d6b42d03981ce4f955ac87ee5fe7e99f5b50465d47f4e1b
         | 
| 7 | 
            +
              data.tar.gz: f5949cfe44eef0951a80ec8949b6bb8a3440d5b483ddacdaf652d8b4dd258a7eb53db42c40290e9a2ebd6e3a1bf6e204f2382b41dd40b23ac8b271dd05547672
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -5,7 +5,19 @@ 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 | 
            -
             | 
| 8 | 
            +
            ## [0.4.11] - 2020-12-07
         | 
| 9 | 
            +
            ### Added
         | 
| 10 | 
            +
            - Add a new command `statistics`. (#73)
         | 
| 11 | 
            +
              - limited features
         | 
| 12 | 
            +
            - Add a completion file for `zsh`.
         | 
| 13 | 
            +
              - a new file `etc/zsh/_rbnotes`
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ### Changed
         | 
| 16 | 
            +
            - Add a new option for `import` to use `mtime`. (#82)
         | 
| 17 | 
            +
            - Add a feature to show multiple notes at once. (#79)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ### Fixed
         | 
| 20 | 
            +
            - Fix issue #77: no error with a non-existing config file.
         | 
| 9 21 |  | 
| 10 22 | 
             
            ## [0.4.10] - 2020-11-20
         | 
| 11 23 | 
             
            ### Added
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/etc/zsh/_rbnotes
    ADDED
    
    | @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            #compdef rbnotes
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            local __rbnotes_cmd __rbnotes_debug
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            __rbnotes_process() {
         | 
| 6 | 
            +
            }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            function _rbnotes() {
         | 
| 9 | 
            +
                local context curcontext=$curcontext stat line
         | 
| 10 | 
            +
                typeset -A opt_args
         | 
| 11 | 
            +
                local ret=1
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                _arguments \
         | 
| 14 | 
            +
            	-C \
         | 
| 15 | 
            +
            	'(- *)'{-v,--version}'[print version]' \
         | 
| 16 | 
            +
            	'(- *)'{-h,--help}'[show help]' \
         | 
| 17 | 
            +
            	'(- *)'{-c,--conf}'[config file]: :->conffile' \
         | 
| 18 | 
            +
            	'1: :__rbnotes_commands' \
         | 
| 19 | 
            +
            	'*:: :->args'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                case $state in
         | 
| 22 | 
            +
            	(conffile)
         | 
| 23 | 
            +
            	    _files -g "*.yml" && ret=0
         | 
| 24 | 
            +
            	    ;;
         | 
| 25 | 
            +
            	(args)
         | 
| 26 | 
            +
            	    case $words[1] in
         | 
| 27 | 
            +
            		(add)
         | 
| 28 | 
            +
            		    _arguments \
         | 
| 29 | 
            +
            			-C \
         | 
| 30 | 
            +
            			'(-t --timestamp)'{-t,--timestamp}'[set timestamp]' \
         | 
| 31 | 
            +
            			'(-)*:: :->null_state' \
         | 
| 32 | 
            +
            			&& ret=0
         | 
| 33 | 
            +
            		    ;;
         | 
| 34 | 
            +
            		(export)
         | 
| 35 | 
            +
            		    _directories && ret=0
         | 
| 36 | 
            +
            		    ;;
         | 
| 37 | 
            +
            		(help)
         | 
| 38 | 
            +
            		    _arguments \
         | 
| 39 | 
            +
            			-C \
         | 
| 40 | 
            +
            			'1: :__rbnotes_commands' \
         | 
| 41 | 
            +
            			&& ret=0
         | 
| 42 | 
            +
            		    ;;
         | 
| 43 | 
            +
            		(import)
         | 
| 44 | 
            +
            		    _files -g '*.md' && ret=0
         | 
| 45 | 
            +
            		    ;;
         | 
| 46 | 
            +
            		(list|pick)
         | 
| 47 | 
            +
            		    _arguments \
         | 
| 48 | 
            +
            			-C \
         | 
| 49 | 
            +
            			'1: :__rbnotes_list_keywords' \
         | 
| 50 | 
            +
            			&& ret=0
         | 
| 51 | 
            +
            		    ;;
         | 
| 52 | 
            +
            		(update)
         | 
| 53 | 
            +
            		    _arguments \
         | 
| 54 | 
            +
            			-C \
         | 
| 55 | 
            +
            			'(-k --keep)'{-k,--keep}'[keep timestamp]' \
         | 
| 56 | 
            +
            			'(-)*:: :->nul_state' \
         | 
| 57 | 
            +
            			&& ret=0
         | 
| 58 | 
            +
            		    ;;
         | 
| 59 | 
            +
            	    esac
         | 
| 60 | 
            +
            	    ;;
         | 
| 61 | 
            +
                esac
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                return ret
         | 
| 64 | 
            +
            }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            __rbnotes_commands() {
         | 
| 67 | 
            +
                local -a _cmds
         | 
| 68 | 
            +
                _cmds=( $(rbnotes commands -d) )
         | 
| 69 | 
            +
                _describe -t commands Commands _cmds
         | 
| 70 | 
            +
            }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            __rbnotes_list_keywords() {
         | 
| 73 | 
            +
                local -a _kw _this_month _this_year
         | 
| 74 | 
            +
                _this_month=$(date "+%Y%m")
         | 
| 75 | 
            +
                _last_month=$(date -v-1m "+%Y%m")
         | 
| 76 | 
            +
                _this_year=$(date "+%Y")
         | 
| 77 | 
            +
                _kw=(
         | 
| 78 | 
            +
            	{to,today}':Today'
         | 
| 79 | 
            +
            	{ye,yesterday}':Yesterday'
         | 
| 80 | 
            +
            	{tw,this_week}':This week'
         | 
| 81 | 
            +
            	{lw,last_week}':Last week'
         | 
| 82 | 
            +
            	"${_this_month}:This month"
         | 
| 83 | 
            +
            	"${_last_month}:Last month"
         | 
| 84 | 
            +
            	"${_this_year}:This year"
         | 
| 85 | 
            +
            	)
         | 
| 86 | 
            +
                _describe -t keywords Keywords _kw
         | 
| 87 | 
            +
            }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            _rbnotes "$@"
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            # Local Variables:
         | 
| 92 | 
            +
            # mode: shell-script
         | 
| 93 | 
            +
            # End:
         | 
    
        data/exe/rbnotes
    CHANGED
    
    
    
        data/lib/rbnotes.rb
    CHANGED
    
    
    
        data/lib/rbnotes/commands.rb
    CHANGED
    
    | @@ -61,10 +61,11 @@ Example usage: | |
| 61 61 | 
             
              #{Rbnotes::NAME} commands [-d]
         | 
| 62 62 | 
             
              #{Rbnotes::NAME} delete [TIMESTAMP]
         | 
| 63 63 | 
             
              #{Rbnotes::NAME} export [TIMESTAMP [FILENAME]]
         | 
| 64 | 
            -
              #{Rbnotes::NAME} import FILE
         | 
| 64 | 
            +
              #{Rbnotes::NAME} import [-m|--use-mtime] FILE
         | 
| 65 65 | 
             
              #{Rbnotes::NAME} list [STAMP_PATTERN|KEYWORD]
         | 
| 66 66 | 
             
              #{Rbnotes::NAME} search PATTERN [STAMP_PATTERN]
         | 
| 67 | 
            -
              #{Rbnotes::NAME} show [TIMESTAMP]
         | 
| 67 | 
            +
              #{Rbnotes::NAME} show [TIMESTAMP...]
         | 
| 68 | 
            +
              #{Rbnotes::NAME} statistics ([-y]|[-m])
         | 
| 68 69 | 
             
              #{Rbnotes::NAME} update [-k] [TIMESTAMP]
         | 
| 69 70 |  | 
| 70 71 | 
             
            Further help for each command:
         | 
| @@ -4,9 +4,12 @@ module Rbnotes::Commands | |
| 4 4 | 
             
              # Imports a existing file which specified by the argument as a note.
         | 
| 5 5 | 
             
              #
         | 
| 6 6 | 
             
              # A timestamp is generated referring to the birthtime of the given
         | 
| 7 | 
            -
              # file.  If birthtime is not available on the system,  | 
| 7 | 
            +
              # file.  If birthtime is not available on the system, uses mtime
         | 
| 8 8 | 
             
              # (modification time).
         | 
| 9 9 | 
             
              #
         | 
| 10 | 
            +
              # When the option, "-m" (or "--use-mtime") is specified, uses mtime
         | 
| 11 | 
            +
              # instead of birthtime.
         | 
| 12 | 
            +
              #
         | 
| 10 13 | 
             
              # Occasionally, there is another note which has the same timestmap
         | 
| 11 14 | 
             
              # in the repository.  Then, tries to create a new timestamp with a
         | 
| 12 15 | 
             
              # suffix.  Unluckily, when such timestamp with a suffix already
         | 
| @@ -25,11 +28,29 @@ module Rbnotes::Commands | |
| 25 28 | 
             
                #     execute([PATHNAME], Rbnotes::Conf or Hash) -> nil
         | 
| 26 29 |  | 
| 27 30 | 
             
                def execute(args, conf)
         | 
| 31 | 
            +
                  @opts = {}
         | 
| 32 | 
            +
                  while args.size > 0
         | 
| 33 | 
            +
                    arg = args.shift
         | 
| 34 | 
            +
                    case arg
         | 
| 35 | 
            +
                    when "-m", "--use-mtime"
         | 
| 36 | 
            +
                      @opts[:use_mtime] = true
         | 
| 37 | 
            +
                    else
         | 
| 38 | 
            +
                      args.unshift(arg)
         | 
| 39 | 
            +
                      break
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 28 43 | 
             
                  file = args.shift
         | 
| 29 44 | 
             
                  unless file.nil?
         | 
| 30 45 | 
             
                    st = File::Stat.new(file)
         | 
| 31 | 
            -
                     | 
| 32 | 
            -
                     | 
| 46 | 
            +
                    time = nil
         | 
| 47 | 
            +
                    if @opts[:use_mtime]
         | 
| 48 | 
            +
                      time = st.mtime
         | 
| 49 | 
            +
                    else
         | 
| 50 | 
            +
                      time = st.respond_to?(:birthtime) ? st.birthtime : st.mtime
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    stamp = Textrepo::Timestamp.new(time)
         | 
| 33 54 | 
             
                    puts "Import [%s] (timestamp [%s]) ..." % [file, stamp]
         | 
| 34 55 |  | 
| 35 56 | 
             
                    repo = Textrepo.init(conf)
         | 
| @@ -72,7 +93,7 @@ module Rbnotes::Commands | |
| 72 93 | 
             
                      puts "Cannot create a text into the repository with the" \
         | 
| 73 94 | 
             
                           " specified file [%s]."  % file
         | 
| 74 95 | 
             
                      puts "For, the birthtime [%s] is identical to some notes" \
         | 
| 75 | 
            -
                           " already exists in the reopsitory." %  | 
| 96 | 
            +
                           " already exists in the reopsitory." % time
         | 
| 76 97 | 
             
                      puts "Change the birthtime of the target file, then retry."
         | 
| 77 98 | 
             
                    else
         | 
| 78 99 | 
             
                      puts "... Done."
         | 
| @@ -86,7 +107,7 @@ module Rbnotes::Commands | |
| 86 107 | 
             
                def help                    # :nodoc:
         | 
| 87 108 | 
             
                  puts <<HELP
         | 
| 88 109 | 
             
            usage:
         | 
| 89 | 
            -
                #{Rbnotes::NAME} import FILE
         | 
| 110 | 
            +
                #{Rbnotes::NAME} import [-m|--use-mtime] FILE
         | 
| 90 111 |  | 
| 91 112 | 
             
            Imports a existing file which specified by the argument as a note.
         | 
| 92 113 |  | 
| @@ -1,52 +1,97 @@ | |
| 1 1 | 
             
            module Rbnotes::Commands
         | 
| 2 2 |  | 
| 3 3 | 
             
              ##
         | 
| 4 | 
            -
              # Shows the content of the  | 
| 4 | 
            +
              # Shows the content of the notes specified by arguments.  Each
         | 
| 5 5 | 
             
              # argument must be a string which can be converted into
         | 
| 6 6 | 
             
              # Textrepo::Timestamp object.
         | 
| 7 7 | 
             
              #
         | 
| 8 | 
            -
              # A string for Timestamp must be:
         | 
| 8 | 
            +
              # A string for Textrepo::Timestamp must be:
         | 
| 9 9 | 
             
              #
         | 
| 10 10 | 
             
              #     "20201106112600"     : year, date, time and sec
         | 
| 11 11 | 
             
              #     "20201106112600_012" : with suffix
         | 
| 12 12 | 
             
              #
         | 
| 13 | 
            -
              # If no argument is passed, reads the standard input for  | 
| 13 | 
            +
              # If no argument is passed, reads the standard input for arguments.
         | 
| 14 14 |  | 
| 15 15 | 
             
              class Show < Command
         | 
| 16 16 |  | 
| 17 17 | 
             
                def description             # :nodoc:
         | 
| 18 | 
            -
                  "Show the content of  | 
| 18 | 
            +
                  "Show the content of notes"
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 21 | 
             
                def execute(args, conf)
         | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 22 | 
            +
                  stamps = Rbnotes.utils.read_multiple_timestamps(args)
         | 
| 24 23 | 
             
                  repo = Textrepo.init(conf)
         | 
| 25 | 
            -
             | 
| 24 | 
            +
             | 
| 25 | 
            +
                  content = stamps.map { |stamp| [stamp, repo.read(stamp)] }.to_h
         | 
| 26 26 |  | 
| 27 27 | 
             
                  pager = conf[:pager]
         | 
| 28 28 | 
             
                  unless pager.nil?
         | 
| 29 | 
            -
                     | 
| 30 | 
            -
                    Open3.pipeline_w(pager) { |stdin|
         | 
| 31 | 
            -
                      stdin.puts content
         | 
| 32 | 
            -
                      stdin.close
         | 
| 33 | 
            -
                    }
         | 
| 29 | 
            +
                    puts_with_pager(pager, make_output(content))
         | 
| 34 30 | 
             
                  else
         | 
| 35 | 
            -
                    puts content
         | 
| 31 | 
            +
                    puts make_output(content)
         | 
| 36 32 | 
             
                  end
         | 
| 37 33 | 
             
                end
         | 
| 38 34 |  | 
| 39 35 | 
             
                def help                    # :nodoc:
         | 
| 40 36 | 
             
                  puts <<HELP
         | 
| 41 37 | 
             
            usage:
         | 
| 42 | 
            -
                #{Rbnotes::NAME} show [TIMESTAMP]
         | 
| 38 | 
            +
                #{Rbnotes::NAME} show [TIMESTAMP...]
         | 
| 43 39 |  | 
| 44 | 
            -
            Show the content of given  | 
| 40 | 
            +
            Show the content of given notes.  TIMESTAMP must be a fully qualified
         | 
| 45 41 | 
             
            one, such "20201016165130" or "20201016165130_012" if it has a suffix.
         | 
| 46 42 |  | 
| 47 43 | 
             
            The command try to read its argument from the standard input when no
         | 
| 48 44 | 
             
            argument was passed in the command line.
         | 
| 49 45 | 
             
            HELP
         | 
| 50 46 | 
             
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # :stopdoc:
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                private
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def puts_with_pager(pager, output)
         | 
| 53 | 
            +
                  require "open3"
         | 
| 54 | 
            +
                  Open3.pipeline_w(pager) { |stdin|
         | 
| 55 | 
            +
                    stdin.puts output
         | 
| 56 | 
            +
                    stdin.close
         | 
| 57 | 
            +
                  }
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                require "io/console/size"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def make_output(content)
         | 
| 63 | 
            +
                  if content.size <= 1
         | 
| 64 | 
            +
                    return content.values[0]
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  _, column = IO.console_size
         | 
| 68 | 
            +
                  output = content.map { |timestamp, text|
         | 
| 69 | 
            +
                    ary = [make_heading(timestamp, [column, 72].min)]
         | 
| 70 | 
            +
                    ary.concat(text)
         | 
| 71 | 
            +
                    ary
         | 
| 72 | 
            +
                  }
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  output = insert_delimiter(output, "")
         | 
| 75 | 
            +
                  output.flatten
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                def make_heading(timestamp, column)
         | 
| 79 | 
            +
                  stamp_str = timestamp.to_s
         | 
| 80 | 
            +
                  length = column - (stamp_str.size + 2)
         | 
| 81 | 
            +
                  "#{stamp_str} #{Array.new(length, '-').join}"
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def insert_delimiter(ary, delimiter = "")
         | 
| 85 | 
            +
                  result = []
         | 
| 86 | 
            +
                  ary.each { |e|
         | 
| 87 | 
            +
                    result << e
         | 
| 88 | 
            +
                    result << delimiter
         | 
| 89 | 
            +
                  }
         | 
| 90 | 
            +
                  result.delete_at(-1)
         | 
| 91 | 
            +
                  result
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                # :startdoc:
         | 
| 95 | 
            +
             | 
| 51 96 | 
             
              end
         | 
| 52 97 | 
             
            end
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            module Rbnotes::Commands
         | 
| 2 | 
            +
              ##
         | 
| 3 | 
            +
              # Shows statistics.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              class Statistics < Command
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def description             # :nodoc:
         | 
| 8 | 
            +
                  "Show statistics values"
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def execute(args, conf)
         | 
| 12 | 
            +
                  report = :total
         | 
| 13 | 
            +
                  while args.size > 0
         | 
| 14 | 
            +
                    arg = args.shift
         | 
| 15 | 
            +
                    case arg
         | 
| 16 | 
            +
                    when "-y", "--yearly"
         | 
| 17 | 
            +
                      report = :yearly
         | 
| 18 | 
            +
                      break
         | 
| 19 | 
            +
                    when "-m", "--monthly"
         | 
| 20 | 
            +
                      report = :monthly
         | 
| 21 | 
            +
                      break
         | 
| 22 | 
            +
                    else
         | 
| 23 | 
            +
                      args.unshift(arg)
         | 
| 24 | 
            +
                      raise ArgumentError, "invalid option or argument: %s" % args.join(" ")
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  stats = Rbnotes::Statistics.new(conf)
         | 
| 29 | 
            +
                  case report
         | 
| 30 | 
            +
                  when :yearly
         | 
| 31 | 
            +
                    stats.yearly_report
         | 
| 32 | 
            +
                  when :monthly
         | 
| 33 | 
            +
                    stats.monthly_report
         | 
| 34 | 
            +
                  else
         | 
| 35 | 
            +
                    stats.total_report
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def help
         | 
| 40 | 
            +
                  puts <<HELP
         | 
| 41 | 
            +
            usage:
         | 
| 42 | 
            +
                #{Rbnotes::NAME} statistics ([-y|--yearly]|[-m|--monthly])
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            option:
         | 
| 45 | 
            +
                -y, --yearly  : print yearly report
         | 
| 46 | 
            +
                -m, --monthly : print monthly report
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            Show statistics.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            In the version #{Rbnotes::VERSION}, only number of notes is supported.
         | 
| 51 | 
            +
            HELP
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
    
        data/lib/rbnotes/conf.rb
    CHANGED
    
    | @@ -32,16 +32,18 @@ module Rbnotes | |
| 32 32 | 
             
                DIRNAME_COMMON_CONF = ".config"
         | 
| 33 33 |  | 
| 34 34 | 
             
                def initialize(conf_path = nil)   # :nodoc:
         | 
| 35 | 
            -
                  @conf_path = conf_path | 
| 36 | 
            -
             | 
| 35 | 
            +
                  @conf_path = conf_path
         | 
| 37 36 | 
             
                  @conf = {}
         | 
| 38 | 
            -
             | 
| 37 | 
            +
             | 
| 38 | 
            +
                  if use_default_values?
         | 
| 39 | 
            +
                    @conf.merge!(DEFAULT_VALUES)
         | 
| 40 | 
            +
                  else
         | 
| 41 | 
            +
                    @conf_path ||= default_conf_file
         | 
| 42 | 
            +
                    raise NoConfFileError, @conf_path unless File.exist?(@conf_path)
         | 
| 43 | 
            +
             | 
| 39 44 | 
             
                    yaml_str = File.open(@conf_path, "r") { |f| f.read }
         | 
| 40 45 | 
             
                    @conf = YAML.load(yaml_str)
         | 
| 41 | 
            -
                  else
         | 
| 42 | 
            -
                    @conf.merge(DEFAULT_VALUES)
         | 
| 43 46 | 
             
                  end
         | 
| 44 | 
            -
                  self
         | 
| 45 47 | 
             
                end
         | 
| 46 48 |  | 
| 47 49 | 
             
                def_delegators(:@conf,
         | 
| @@ -99,10 +101,19 @@ module Rbnotes | |
| 99 101 | 
             
                  end
         | 
| 100 102 | 
             
                  return path
         | 
| 101 103 | 
             
                end
         | 
| 102 | 
            -
             | 
| 104 | 
            +
             | 
| 105 | 
            +
                def default_conf_file
         | 
| 106 | 
            +
                  File.join(base_path, FILENAME_CONF)
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def use_default_values?
         | 
| 110 | 
            +
                  @conf_path.nil? && !File.exist?(default_conf_file)
         | 
| 111 | 
            +
                end
         | 
| 103 112 |  | 
| 104 113 | 
             
              # :startdoc:
         | 
| 105 114 |  | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 106 117 | 
             
              class << self
         | 
| 107 118 | 
             
                ##
         | 
| 108 119 | 
             
                # Gets the instance of Rbnotes::Conf.  An optional argument is to
         | 
    
        data/lib/rbnotes/error.rb
    CHANGED
    
    | @@ -13,6 +13,7 @@ module Rbnotes | |
| 13 13 | 
             
                PROGRAM_ABORT     = "External program was aborted: %s"
         | 
| 14 14 | 
             
                UNKNOWN_KEYWORD   = "Unknown keyword: %s"
         | 
| 15 15 | 
             
                INVALID_TIMESTAMP_PATTERN = "Invalid timestamp pattern: %s"
         | 
| 16 | 
            +
                NO_CONF_FILE      = "No configuration file: %s"
         | 
| 16 17 | 
             
              end
         | 
| 17 18 |  | 
| 18 19 | 
             
              # :startdoc:
         | 
| @@ -75,4 +76,14 @@ module Rbnotes | |
| 75 76 | 
             
                end
         | 
| 76 77 | 
             
              end
         | 
| 77 78 |  | 
| 79 | 
            +
              ##
         | 
| 80 | 
            +
              # An error raised when the specified configuration file does not
         | 
| 81 | 
            +
              # exist.
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              class NoConfFileError < Error
         | 
| 84 | 
            +
                def initialize(filename)
         | 
| 85 | 
            +
                  super(ErrMsg::NO_CONF_FILE % filename)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 78 89 | 
             
            end
         | 
| @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            module Rbnotes
         | 
| 2 | 
            +
              ##
         | 
| 3 | 
            +
              # Calculates statistics of the repository.
         | 
| 4 | 
            +
              class Statistics
         | 
| 5 | 
            +
                include Enumerable
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(conf)
         | 
| 8 | 
            +
                  @repo = Textrepo.init(conf)
         | 
| 9 | 
            +
                  @values = construct_values(@repo)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def total_report
         | 
| 13 | 
            +
                  puts @repo.entries.size
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def yearly_report
         | 
| 17 | 
            +
                  self.each_year { |year, monthly_values|
         | 
| 18 | 
            +
                    num_of_notes = monthly_values.map { |_mon, values| values.size }.sum
         | 
| 19 | 
            +
                    puts "#{year}: #{num_of_notes}"
         | 
| 20 | 
            +
                  }
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def monthly_report
         | 
| 24 | 
            +
                  self.each { |year, mon, values|
         | 
| 25 | 
            +
                    num_of_notes = values.size
         | 
| 26 | 
            +
                    puts "#{year}/#{mon}: #{num_of_notes}"
         | 
| 27 | 
            +
                  }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def each(&block)
         | 
| 31 | 
            +
                  if block.nil?
         | 
| 32 | 
            +
                    @values.map { |year, monthly_values|
         | 
| 33 | 
            +
                      monthly_values.each { |mon, values|
         | 
| 34 | 
            +
                        [year, mon, values]
         | 
| 35 | 
            +
                      }
         | 
| 36 | 
            +
                    }.to_enum(:each)
         | 
| 37 | 
            +
                  else
         | 
| 38 | 
            +
                    @values.each { |year, monthly_values|
         | 
| 39 | 
            +
                      monthly_values.each { |mon, values|
         | 
| 40 | 
            +
                        yield [year, mon, values]
         | 
| 41 | 
            +
                      }
         | 
| 42 | 
            +
                    }
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def years
         | 
| 47 | 
            +
                  @values.keys
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def months(year)
         | 
| 51 | 
            +
                  @values[year] || []
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def each_year(&block)
         | 
| 55 | 
            +
                  if block.nil?
         | 
| 56 | 
            +
                    @values.map { |year, monthly_values|
         | 
| 57 | 
            +
                      [year, monthly_values]
         | 
| 58 | 
            +
                    }.to_enum(:each)
         | 
| 59 | 
            +
                  else
         | 
| 60 | 
            +
                    @values.each { |year, monthly_values|
         | 
| 61 | 
            +
                      yield [year, monthly_values]
         | 
| 62 | 
            +
                    }
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                private
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def construct_values(repo)
         | 
| 69 | 
            +
                  values = {}
         | 
| 70 | 
            +
                  repo.each { |timestamp, text|
         | 
| 71 | 
            +
                    value = StatisticValue.new(timestamp, text)
         | 
| 72 | 
            +
                    y = value.year
         | 
| 73 | 
            +
                    m = value.mon
         | 
| 74 | 
            +
                    values[y] ||= {}
         | 
| 75 | 
            +
                    values[y][m] ||= []
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    values[y][m] << value
         | 
| 78 | 
            +
                  }
         | 
| 79 | 
            +
                  values
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                class StatisticValue
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  attr_reader :lines
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def initialize(timestamp, text)
         | 
| 87 | 
            +
                    @timestamp = timestamp
         | 
| 88 | 
            +
                    @lines = text.size
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  def year
         | 
| 92 | 
            +
                    @timestamp[:year]
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def mon
         | 
| 96 | 
            +
                    @timestamp[:mon]
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         | 
    
        data/lib/rbnotes/utils.rb
    CHANGED
    
    | @@ -103,23 +103,16 @@ module Rbnotes | |
| 103 103 | 
             
                end
         | 
| 104 104 |  | 
| 105 105 | 
             
                ##
         | 
| 106 | 
            -
                #  | 
| 107 | 
            -
                #  | 
| 106 | 
            +
                # Generates multiple Textrepo::Timestamp objects from the command
         | 
| 107 | 
            +
                # line arguments.  When no argument is given, try to read from
         | 
| 108 | 
            +
                # STDIN.
         | 
| 108 109 | 
             
                #
         | 
| 109 110 | 
             
                # :call-seq:
         | 
| 110 | 
            -
                #    | 
| 111 | 
            +
                #   read_multiple_timestamps(args) -> [String]
         | 
| 111 112 |  | 
| 112 | 
            -
                def  | 
| 113 | 
            -
                   | 
| 114 | 
            -
                   | 
| 115 | 
            -
                  #     foo bar baz ...
         | 
| 116 | 
            -
                  #
         | 
| 117 | 
            -
                  # then, only the first string is interested
         | 
| 118 | 
            -
                  begin
         | 
| 119 | 
            -
                    io.gets.split(":")[0].rstrip
         | 
| 120 | 
            -
                  rescue NoMethodError => _
         | 
| 121 | 
            -
                    nil
         | 
| 122 | 
            -
                  end
         | 
| 113 | 
            +
                def read_multiple_timestamps(args)
         | 
| 114 | 
            +
                  strings = args.size < 1 ? read_multiple_args($stdin) : args
         | 
| 115 | 
            +
                  strings.map { |str| Textrepo::Timestamp.parse_s(str) }
         | 
| 123 116 | 
             
                end
         | 
| 124 117 |  | 
| 125 118 | 
             
                ##
         | 
| @@ -241,6 +234,41 @@ module Rbnotes | |
| 241 234 | 
             
                # :stopdoc:
         | 
| 242 235 |  | 
| 243 236 | 
             
                private
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                ##
         | 
| 239 | 
            +
                # Reads an argument from the IO object.  Typically, it is intended
         | 
| 240 | 
            +
                # to be used with STDIN.
         | 
| 241 | 
            +
                #
         | 
| 242 | 
            +
                # :call-seq:
         | 
| 243 | 
            +
                #   read_arg(IO) -> String
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                def read_arg(io)
         | 
| 246 | 
            +
                  read_multiple_args(io)[0]
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                ##
         | 
| 250 | 
            +
                # Reads arguments from the IO object.  Typically, it is intended
         | 
| 251 | 
            +
                # to be used with STDIN.
         | 
| 252 | 
            +
                #
         | 
| 253 | 
            +
                # :call-seq:
         | 
| 254 | 
            +
                #   read_multiple_arg(IO) -> [String]
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                def read_multiple_args(io)
         | 
| 257 | 
            +
                  strings = io.readlines
         | 
| 258 | 
            +
                  strings.map { |str|
         | 
| 259 | 
            +
                    # assumes the reading line looks like:
         | 
| 260 | 
            +
                    #
         | 
| 261 | 
            +
                    #     foo bar baz ...
         | 
| 262 | 
            +
                    #
         | 
| 263 | 
            +
                    # then, only the first string is interested
         | 
| 264 | 
            +
                    begin
         | 
| 265 | 
            +
                      str.split(":")[0].rstrip
         | 
| 266 | 
            +
                    rescue NoMethodError => _
         | 
| 267 | 
            +
                      nil
         | 
| 268 | 
            +
                    end
         | 
| 269 | 
            +
                  }.compact
         | 
| 270 | 
            +
                end
         | 
| 271 | 
            +
             | 
| 244 272 | 
             
                def search_in_path(name)
         | 
| 245 273 | 
             
                  search_paths = ENV["PATH"].split(":")
         | 
| 246 274 | 
             
                  found = search_paths.map { |path|
         | 
    
        data/lib/rbnotes/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rbnotes
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.4. | 
| 4 | 
            +
              version: 0.4.11
         | 
| 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 | 
            +
            date: 2020-12-07 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: textrepo
         | 
| @@ -59,6 +59,7 @@ files: | |
| 59 59 | 
             
            - conf/config.yml
         | 
| 60 60 | 
             
            - conf/config_deve.yml
         | 
| 61 61 | 
             
            - conf/config_test.yml
         | 
| 62 | 
            +
            - etc/zsh/_rbnotes
         | 
| 62 63 | 
             
            - exe/rbnotes
         | 
| 63 64 | 
             
            - lib/rbnotes.rb
         | 
| 64 65 | 
             
            - lib/rbnotes/commands.rb
         | 
| @@ -72,9 +73,11 @@ files: | |
| 72 73 | 
             
            - lib/rbnotes/commands/pick.rb
         | 
| 73 74 | 
             
            - lib/rbnotes/commands/search.rb
         | 
| 74 75 | 
             
            - lib/rbnotes/commands/show.rb
         | 
| 76 | 
            +
            - lib/rbnotes/commands/statistics.rb
         | 
| 75 77 | 
             
            - lib/rbnotes/commands/update.rb
         | 
| 76 78 | 
             
            - lib/rbnotes/conf.rb
         | 
| 77 79 | 
             
            - lib/rbnotes/error.rb
         | 
| 80 | 
            +
            - lib/rbnotes/statistics.rb
         | 
| 78 81 | 
             
            - lib/rbnotes/utils.rb
         | 
| 79 82 | 
             
            - lib/rbnotes/version.rb
         | 
| 80 83 | 
             
            - rbnotes.gemspec
         |