topicz 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +7 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/topicz +12 -0
- data/lib/topicz/application.rb +44 -0
- data/lib/topicz/command_factory.rb +21 -0
- data/lib/topicz/commands/_index.rb +18 -0
- data/lib/topicz/commands/alfred_command.rb +95 -0
- data/lib/topicz/commands/create_command.rb +48 -0
- data/lib/topicz/commands/editor_command.rb +14 -0
- data/lib/topicz/commands/help_command.rb +31 -0
- data/lib/topicz/commands/init_command.rb +61 -0
- data/lib/topicz/commands/journal_command.rb +64 -0
- data/lib/topicz/commands/note_command.rb +68 -0
- data/lib/topicz/commands/path_command.rb +39 -0
- data/lib/topicz/commands/report_command.rb +50 -0
- data/lib/topicz/commands/repository_command.rb +61 -0
- data/lib/topicz/defaults.rb +12 -0
- data/lib/topicz/repository.rb +123 -0
- data/lib/topicz/version.rb +3 -0
- data/topicz.gemspec +27 -0
- data/vendor/cache/diff-lcs-1.2.5.gem +0 -0
- data/vendor/cache/docile-1.1.5.gem +0 -0
- data/vendor/cache/fakefs-0.8.1.gem +0 -0
- data/vendor/cache/json-1.8.3.gem +0 -0
- data/vendor/cache/rake-10.5.0.gem +0 -0
- data/vendor/cache/rspec-3.3.0.gem +0 -0
- data/vendor/cache/rspec-core-3.3.2.gem +0 -0
- data/vendor/cache/rspec-expectations-3.3.1.gem +0 -0
- data/vendor/cache/rspec-mocks-3.3.2.gem +0 -0
- data/vendor/cache/rspec-support-3.3.0.gem +0 -0
- data/vendor/cache/simplecov-0.11.2.gem +0 -0
- data/vendor/cache/simplecov-html-0.10.0.gem +0 -0
- data/vendor/cache/zaru-0.1.0.gem +0 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a22e7da57e83490f3607152af595bec565270200
|
4
|
+
data.tar.gz: 52a68b28cf5a622b7444ee0170ae45db753610ff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ad1e9d6067d868e516c681c35cdf68c60ee00933b59ebbf1eb03a4df6154ba05a2200ed85e4450edd4d199f366d17ad183e31bc01e90bd1fb0c2606e3c4756f
|
7
|
+
data.tar.gz: 22716d00ed11955671a772d47d76871b5f99bf7fd46ebfbbb792b6a4c569daa991a06150a5391a91fc42b6c5ae1b8b036b04d89e288fbb50e3a4e60115e15c1f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2016 Vincent Oostindië
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Topicz
|
2
|
+
|
3
|
+
Topicz is a simple topic repository administration tool, where the repository is a regular filesystem.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install it as:
|
8
|
+
|
9
|
+
$ gem install topicz
|
10
|
+
|
11
|
+
See below (*Development*) in case you’re working with a Git clone directly.
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
$ topicz —help
|
16
|
+
Usage: topicz [options] <command> [options]
|
17
|
+
-c, --config FILE Uses FILE as the configuration file
|
18
|
+
|
19
|
+
Where <command> is one of:
|
20
|
+
|
21
|
+
init : Initializes a new topic repository
|
22
|
+
create : Creates a new topic
|
23
|
+
path : Prints the full path to a topic
|
24
|
+
journal: Opens a (new) weekly journal entry for a topic
|
25
|
+
note : Opens a new note for a topic
|
26
|
+
alfred : Searches in Alfred Script Filter format
|
27
|
+
report : Generates a weekly report of all topics
|
28
|
+
help : Shows help about a command
|
29
|
+
|
30
|
+
## Background
|
31
|
+
|
32
|
+
In my daily work I have to deal with a lot of different topics. New topics pop up often and existing topics are in varying degree of completeness. Sometimes nothing happens to a topic for weeks. Sometimes topics are short-lived, sometimes they stick around for years. All in all, keeping track of all the topics I work on is a big challenge for me.
|
33
|
+
|
34
|
+
The way my mind works: if I can’t find a structure to organize my work in, I feel stressed. But like everybody else I prefer to be relaxed. Also I don’t like proprietary formats or tools that force me in a specific way of working.
|
35
|
+
|
36
|
+
So I created my own structure, completely filesystem based, and scripts on top of it to integrate it with the other tools I use on my Mac, like [Alfred](http://www.alfredapp.com). Topicz collects all those scripts into a consistent, single command-line interface that I can easily change and extend whenever I need.
|
37
|
+
|
38
|
+
## The topic repository
|
39
|
+
|
40
|
+
A topic repository is nothing more than a directory on disk. This directory has subdirectories, one for each topic. The name of the subdirectory is what you like it to be. A topic, in turn, holds a number of directories. Here’s a basic layout:
|
41
|
+
|
42
|
+
. (topic root)
|
43
|
+
├── Topic 1
|
44
|
+
│ ├── topic.yaml
|
45
|
+
│ ├── Documents
|
46
|
+
│ ├── Journal
|
47
|
+
│ ├── Logs
|
48
|
+
│ └── Reference Material
|
49
|
+
├── Topic 2
|
50
|
+
├── Topic …
|
51
|
+
└── Topic n
|
52
|
+
|
53
|
+
The `topic.yaml` file is entirely optional. More information on that later.
|
54
|
+
|
55
|
+
A topic can certainly have other subdirectories than the ones shown above. These four are the ones I always use. Where needed I add more. For example when I go to a conference, I typically add the directories `Travel` and `Bills`.
|
56
|
+
|
57
|
+
### Documents and Reference Material
|
58
|
+
|
59
|
+
The `Documents` and `Reference Material` directories speak for themselves. The first is for stuff I create, the second for stuff that others create. There are no rules as to the files and subdirectories in these directories, although I tend to prefix each filename with the date I created or received the file in `YYYY-mm-DD` format. But that’s just my way of keeping things organized.
|
60
|
+
|
61
|
+
### Journal
|
62
|
+
|
63
|
+
The `Journal` directory is more interesting. It contains Markdown files only, and each filename follows the pattern `<year>-week-<week>.md`. If there was some kind of progress on a topic in a certain week, I describe it here. Every week I send a progress report to my colleagues about all the topics I worked on. Topicz’s `report` command creates this report for me automatically from the separate journal entries. And of course Topicz also helps me creating and opening the right journal files, depending on the topic I work on.
|
64
|
+
|
65
|
+
### Notes
|
66
|
+
|
67
|
+
The `Notes` directory is for meeting notes, scratchpads, reminders, whatever. Every note is a Markdown file and each filename follows the pattern `<YYYY-mm-DD> <subject>.md`. Again Topicz helps me to quickly create and open notes for the topic I’m working on.
|
68
|
+
|
69
|
+
## Repository configuration
|
70
|
+
|
71
|
+
Topicz needs to know just one thing: where your repository is. You stick this into a configuration file, and tell Topicz about this file using the `-c` flag. If you have one topic repository, you can also store this file as `~/.topiczrc`. Then you don’t need to specify it when using the CLI.
|
72
|
+
|
73
|
+
The first time you use Topicz, just do this:
|
74
|
+
|
75
|
+
$ topics init /path/to/my/topic/repository
|
76
|
+
|
77
|
+
Topicz will do two things. First it will create an empty directory at the location you specified. Secondly it will write this location into `~/.topiczrc`.
|
78
|
+
|
79
|
+
Some commands call an external text editor, for example to edit journals and notes. By default Topicz will use the `EDITOR` environment variable. You can override it on a per-repository basis by defining an `editor` property in the configuration file.
|
80
|
+
|
81
|
+
## Topic configuration
|
82
|
+
|
83
|
+
Each topic directory can have a `topic.yaml` file in it, which holds a YAML file describing that topic. It looks as follows:
|
84
|
+
|
85
|
+
title: <topic title>
|
86
|
+
id: <topic ID>
|
87
|
+
|
88
|
+
If the YAML file is missing, or a field in the file is missing, then Topicz falls back on sensible defaults: the title is set to the name of the topic directory, and the ID is generated from this name by lowercasing it, replacing whitespace with hyphens, and removing strange characters.
|
89
|
+
|
90
|
+
> This file is where I’m planning to add lots of functionality to. For example to store topic relationships, or to store metadata. See *Plans* below.
|
91
|
+
|
92
|
+
You’re free to edit this file at any time, although Topicz also provides commands to help you do this. You never actually need to edit these YAML files themselves, if you don’t want to.
|
93
|
+
|
94
|
+
## Development
|
95
|
+
|
96
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
97
|
+
|
98
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
99
|
+
|
100
|
+
## Plans
|
101
|
+
|
102
|
+
* Allow topics to be archived when no longer relevant.
|
103
|
+
* Allow relationships between topics to be defined in the YAML files, and use these to generate graphs for Graphviz: `depends-on`, `part-of`, `relates-to`. The CLI will help to guarantee correctness, for example when archiving a topic, or when renaming one’s title and/or ID.
|
104
|
+
* Allow metadata to be defined for each topic, like main stakeholders, categories and topic goals.
|
105
|
+
|
106
|
+
## Contributing
|
107
|
+
|
108
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/voostindie/topicz.
|
109
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "topicz"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/topicz
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'topicz/version'
|
2
|
+
require 'topicz/command_factory'
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Topicz
|
7
|
+
|
8
|
+
class Application
|
9
|
+
|
10
|
+
attr_reader :command
|
11
|
+
|
12
|
+
def initialize(arguments = ARGV, factory = CommandFactory.new)
|
13
|
+
config_file = nil
|
14
|
+
OptionParser.new do |options|
|
15
|
+
options.banner = 'Usage: topicz [options] <command> [options]'
|
16
|
+
options.program_name = 'topicz'
|
17
|
+
options.version = Topicz::VERSION
|
18
|
+
options.on('-c', '--config FILE', 'Uses FILE as the configuration file') do |file|
|
19
|
+
config_file = file
|
20
|
+
end
|
21
|
+
options.separator ''
|
22
|
+
options.separator 'Where <command> is one of: '
|
23
|
+
options.separator ''
|
24
|
+
options.separator Topicz::COMMANDS.to_s
|
25
|
+
end.order! arguments
|
26
|
+
|
27
|
+
unless arguments.empty?
|
28
|
+
command = arguments.shift
|
29
|
+
if Topicz::COMMANDS.has_key? command
|
30
|
+
@command = factory.create_command(command, config_file, arguments)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
if @command != nil
|
37
|
+
@command.execute
|
38
|
+
else
|
39
|
+
raise 'Invalid command. Try topicz --help'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'topicz/commands/_index'
|
2
|
+
|
3
|
+
module Topicz
|
4
|
+
|
5
|
+
class CommandFactory
|
6
|
+
|
7
|
+
def load_command(name)
|
8
|
+
unless COMMANDS.has_key?name
|
9
|
+
raise "Unsupported command: #{name}"
|
10
|
+
end
|
11
|
+
require "topicz/commands/#{name}_command"
|
12
|
+
Object.const_get("Topicz::Commands::#{name.capitalize}Command")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_command(name, config_file = nil, arguments = [])
|
16
|
+
load_command(name).new(config_file, arguments)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Topicz
|
2
|
+
|
3
|
+
COMMANDS = {
|
4
|
+
'init' => 'Initializes a new topic repository',
|
5
|
+
'create' => 'Creates a new topic',
|
6
|
+
'path' => 'Prints the full path to a topic',
|
7
|
+
'journal' => 'Opens a (new) weekly journal entry for a topic',
|
8
|
+
'note' => 'Opens a new note for a topic',
|
9
|
+
'alfred' => 'Searches in Alfred Script Filter format',
|
10
|
+
'report' => 'Generates a weekly report of all topics',
|
11
|
+
'help' => 'Shows help about a command',
|
12
|
+
}
|
13
|
+
|
14
|
+
def COMMANDS.to_s
|
15
|
+
COMMANDS.collect { |command, description| " #{command.ljust(7)}: #{description}" }.join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative 'repository_command'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Topicz::Commands
|
5
|
+
|
6
|
+
class AlfredCommand < RepositoryCommand
|
7
|
+
|
8
|
+
def initialize(config_file = nil, arguments = [])
|
9
|
+
super(config_file)
|
10
|
+
@identifiers = false
|
11
|
+
@mode = :browse
|
12
|
+
option_parser.order! arguments
|
13
|
+
@filter = arguments.join ' '
|
14
|
+
end
|
15
|
+
|
16
|
+
def option_parser
|
17
|
+
OptionParser.new do |options|
|
18
|
+
options.banner = 'Usage: alfred <filter>'
|
19
|
+
options.on('-m', '--mode MODE', 'Set the output mode') do |mode|
|
20
|
+
case mode.strip.downcase
|
21
|
+
when 'browse' then
|
22
|
+
@mode = :browse
|
23
|
+
@identifiers = false
|
24
|
+
when 'journal' then
|
25
|
+
@mode = :journal
|
26
|
+
@identifiers = true
|
27
|
+
when 'note' then
|
28
|
+
@mode = :note
|
29
|
+
@identifiers = true
|
30
|
+
else
|
31
|
+
raise "Invalid mode: '#{mode}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
options.separator ''
|
35
|
+
options.separator 'Searches for topics and produces the result in JSON for Alfred\'s Script Filter'
|
36
|
+
options.separator ''
|
37
|
+
options.separator 'The filter specifies the text to search on. The text is matched against the topic\'s: '
|
38
|
+
options.separator '- path on the filesystem'
|
39
|
+
options.separator '- id, if specified in the topic\'s topic.yaml file'
|
40
|
+
options.separator '- title, if specified in the topic\'s topic.yaml file'
|
41
|
+
options.separator '- aliases, if specified in the topic\'s topic.yaml file'
|
42
|
+
options.separator ''
|
43
|
+
options.separator 'Supported modes are:'
|
44
|
+
options.separator ' browse : For browsing through topics'
|
45
|
+
options.separator ' journal : For creating journal entries'
|
46
|
+
options.separator ' note : For creating notes'
|
47
|
+
options.separator ''
|
48
|
+
options.separator 'Alfred automatically orders the items in its UI based on your usage.'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def execute
|
53
|
+
topics = @repository.find_all @filter
|
54
|
+
items = []
|
55
|
+
topics.each do |topic|
|
56
|
+
item = {
|
57
|
+
# https://www.alfredapp.com/help/workflows/inputs/script-filter/json/
|
58
|
+
uid: topic.id, # Affects the ordering in Alfred, based on learning
|
59
|
+
type: 'file',
|
60
|
+
title: topic.title,
|
61
|
+
subtitle: create_subtitle(topic),
|
62
|
+
arg: @identifiers ? topic.id : topic.fullpath,
|
63
|
+
icon: {
|
64
|
+
type: 'fileicon',
|
65
|
+
path: topic.fullpath
|
66
|
+
}
|
67
|
+
}
|
68
|
+
if @mode == :browse
|
69
|
+
item['mods'] = {
|
70
|
+
cmd: {
|
71
|
+
subtitle: "Open topic '#{topic.title}' in Finder"
|
72
|
+
},
|
73
|
+
alt: {
|
74
|
+
subtitle: "Open topic '#{topic.title}' in editor"
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
items << item
|
79
|
+
end
|
80
|
+
puts ({items: items}.to_json)
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_subtitle(topic)
|
84
|
+
case @mode
|
85
|
+
when :browse then
|
86
|
+
"Browse topic '#{topic.title}' in Alfred Browser"
|
87
|
+
when :journal then
|
88
|
+
"Open this weeks journal entry for topic '#{topic.title}'"
|
89
|
+
when :note then
|
90
|
+
"Create a new note for topic '#{topic.title}'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative 'repository_command'
|
2
|
+
require 'topicz/defaults'
|
3
|
+
require 'json'
|
4
|
+
require 'zaru'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Topicz::Commands
|
8
|
+
|
9
|
+
class CreateCommand < RepositoryCommand
|
10
|
+
|
11
|
+
def initialize(config_file = nil, arguments = [])
|
12
|
+
super(config_file)
|
13
|
+
@title = arguments.join(' ').strip
|
14
|
+
end
|
15
|
+
|
16
|
+
def option_parser
|
17
|
+
OptionParser.new do |options|
|
18
|
+
options.banner = 'Usage: create <name>'
|
19
|
+
options.separator ''
|
20
|
+
options.separator 'Creates a new topic with the specified name'
|
21
|
+
options.separator ''
|
22
|
+
options.separator 'Basically all this command does is create a new directory structure to hold a topic.'
|
23
|
+
options.separator ''
|
24
|
+
options.separator 'It is an error if a topic with the specified name already exists.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute
|
29
|
+
if @repository.exist_title?(@title)
|
30
|
+
raise "Topic already exists: '#{@title}'."
|
31
|
+
end
|
32
|
+
path = Zaru.sanitize! @title
|
33
|
+
fullpath = File.join(@repository.root, path)
|
34
|
+
if File.exist?(fullpath)
|
35
|
+
raise "Directory already exists: '#{path}'."
|
36
|
+
end
|
37
|
+
FileUtils.mkdir fullpath
|
38
|
+
Topicz::DIRECTORIES.each { |dir| FileUtils.mkdir(File.join(fullpath, dir)) }
|
39
|
+
if path != @title
|
40
|
+
File.open(File.join(fullpath, Topicz::TOPIC_FILE), 'w') do | file |
|
41
|
+
file.write(YAML.dump({'title' => @title}))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
puts "Created topic '#{@title}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'repository_command'
|
2
|
+
|
3
|
+
module Topicz::Commands
|
4
|
+
|
5
|
+
class EditorCommand < RepositoryCommand
|
6
|
+
|
7
|
+
def editor
|
8
|
+
@config['editor'] ||
|
9
|
+
ENV['EDITOR'] ||
|
10
|
+
raise('No editor configured. Set one in the topicz configuraton file, or in the EDITOR environment variable.')
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'topicz/command_factory'
|
2
|
+
|
3
|
+
module Topicz::Commands
|
4
|
+
|
5
|
+
class HelpCommand
|
6
|
+
|
7
|
+
def initialize(config_file = nil, arguments = [])
|
8
|
+
option_parser.order! arguments
|
9
|
+
@help =
|
10
|
+
if arguments == nil || arguments.empty?
|
11
|
+
self
|
12
|
+
else
|
13
|
+
Topicz::CommandFactory.new.load_command(arguments.shift).new
|
14
|
+
end.option_parser
|
15
|
+
end
|
16
|
+
|
17
|
+
def option_parser
|
18
|
+
OptionParser.new do |options|
|
19
|
+
options.banner = 'Usage: help <command>'
|
20
|
+
options.separator ''
|
21
|
+
options.separator 'Shows help about a specific command. Valid commands are:'
|
22
|
+
options.separator ''
|
23
|
+
options.separator Topicz::COMMANDS.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
puts @help.help
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'topicz/defaults'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Topicz::Commands
|
7
|
+
|
8
|
+
class InitCommand
|
9
|
+
|
10
|
+
def initialize(config_file = nil, arguments = [])
|
11
|
+
@config_file = Topicz::DEFAULT_CONFIG_LOCATION
|
12
|
+
option_parser.order! arguments
|
13
|
+
unless arguments.empty?
|
14
|
+
@directory = arguments.shift
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def option_parser
|
19
|
+
OptionParser.new do |options|
|
20
|
+
options.banner = 'Usage: init [options] <directory>'
|
21
|
+
options.on('-c', '--config FILE') do |file|
|
22
|
+
@config_file = file
|
23
|
+
end
|
24
|
+
options.separator ''
|
25
|
+
options.separator 'Initializes a new topic repository. This creates a directory and writes its
|
26
|
+
location into a configuration file.'
|
27
|
+
options.separator ''
|
28
|
+
options.separator 'If the directory already exists, this command fails.'
|
29
|
+
options.separator ''
|
30
|
+
options.separator 'If a configuration file already exists, it will not be overwritten.'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute
|
35
|
+
unless @directory
|
36
|
+
raise 'Pass the location of the new repository as an argument.'
|
37
|
+
end
|
38
|
+
if File.exist? @directory
|
39
|
+
raise "A file or directory already exists at this location: #{@directory}."
|
40
|
+
end
|
41
|
+
create_repository
|
42
|
+
create_configuration
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_repository
|
46
|
+
FileUtils.mkdir_p(@directory)
|
47
|
+
puts "New topic repository created at: #{@directory}."
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_configuration
|
51
|
+
if File.exist? @config_file
|
52
|
+
puts "Skipping creation of configuration file; one already exists at #{@config_file}."
|
53
|
+
else
|
54
|
+
File.open(@config_file, 'w') do |file|
|
55
|
+
file.write(YAML.dump({'repository' => @directory}))
|
56
|
+
end
|
57
|
+
puts "Configuration file saved to: #{@config_file}."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative 'editor_command'
|
2
|
+
require 'topicz/defaults'
|
3
|
+
require 'json'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Topicz::Commands
|
7
|
+
|
8
|
+
class JournalCommand < EditorCommand
|
9
|
+
|
10
|
+
def initialize(config_file = nil, arguments = [], kernel = Kernel)
|
11
|
+
super(config_file)
|
12
|
+
@kernel = kernel
|
13
|
+
@strict = false
|
14
|
+
@week = Date.today.cweek
|
15
|
+
@year = Date.today.cwyear
|
16
|
+
option_parser.order! arguments
|
17
|
+
@filter = arguments.join ' '
|
18
|
+
end
|
19
|
+
|
20
|
+
def option_parser
|
21
|
+
OptionParser.new do |options|
|
22
|
+
options.banner = 'Usage: journal <filter>'
|
23
|
+
options.on('-s', '--strict', 'Do a full strict match on topic IDs only') do
|
24
|
+
@strict = true
|
25
|
+
end
|
26
|
+
options.on('-w', '--week WEEK', 'Use week WEEK instead of the current week') do |week|
|
27
|
+
@week = week.to_i
|
28
|
+
end
|
29
|
+
options.on('-y', '--year YEAR', 'Use year YEAR instead of the current year') do |year|
|
30
|
+
@year = year.to_i
|
31
|
+
end
|
32
|
+
options.separator ''
|
33
|
+
options.separator 'Opens the weekly journal for the specified topic.'
|
34
|
+
options.separator ''
|
35
|
+
options.separator 'The filter specifies the text to search on. The text is matched against the topic\'s: '
|
36
|
+
options.separator '- path on the filesystem'
|
37
|
+
options.separator '- id, if specified in the topic\'s topic.yaml file'
|
38
|
+
options.separator '- title, if specified in the topic\'s topic.yaml file'
|
39
|
+
options.separator '- aliases, if specified in the topic\'s topic.yaml file'
|
40
|
+
options.separator ''
|
41
|
+
options.separator 'The filter must return precisely one topic. Zero or more matches give an error.'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute
|
46
|
+
topic = find_exactly_one_topic(@filter, @strict)
|
47
|
+
path = File.join(topic.fullpath, Topicz::DIR_JOURNAL)
|
48
|
+
FileUtils.mkdir(path) unless Dir.exist? path
|
49
|
+
|
50
|
+
year = @year.to_s
|
51
|
+
week = @week.to_s.rjust(2, '0')
|
52
|
+
path = File.join(path, "#{year}-week-#{week}.md")
|
53
|
+
|
54
|
+
unless File.exists? path
|
55
|
+
File.open(path, 'w') do |file|
|
56
|
+
file.puts("# #{topic.title} - Week #{week}, #{year}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
@kernel.exec "#{editor} \"#{path}\""
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'editor_command'
|
2
|
+
require 'topicz/defaults'
|
3
|
+
require 'json'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'zaru'
|
6
|
+
|
7
|
+
module Topicz::Commands
|
8
|
+
|
9
|
+
class NoteCommand < EditorCommand
|
10
|
+
|
11
|
+
def initialize(config_file = nil, arguments = [], kernel = Kernel)
|
12
|
+
super(config_file)
|
13
|
+
@kernel = kernel
|
14
|
+
@strict = false
|
15
|
+
option_parser.order! arguments
|
16
|
+
@filter = arguments.shift
|
17
|
+
@title = arguments.empty? ? nil : arguments.join(' ')
|
18
|
+
end
|
19
|
+
|
20
|
+
def option_parser
|
21
|
+
OptionParser.new do |options|
|
22
|
+
options.banner = 'Usage: note <filter> [<title>]'
|
23
|
+
options.on('-s', '--strict', 'Do a full strict match on topic IDs only') do
|
24
|
+
@strict = true
|
25
|
+
end
|
26
|
+
options.separator ''
|
27
|
+
options.separator 'Creates a new note for the specified topic.'
|
28
|
+
options.separator ''
|
29
|
+
options.separator 'The filter specifies the text to search on. The text is matched against the topic\'s: '
|
30
|
+
options.separator '- path on the filesystem'
|
31
|
+
options.separator '- id, if specified in the topic\'s topic.yaml file'
|
32
|
+
options.separator '- title, if specified in the topic\'s topic.yaml file'
|
33
|
+
options.separator '- aliases, if specified in the topic\'s topic.yaml file'
|
34
|
+
options.separator ''
|
35
|
+
options.separator 'The filter must return precisely one topic. Zero or more matches give an error.'
|
36
|
+
options.separator ''
|
37
|
+
options.separator 'The note title is optional. If omitted the title will be \'Unnamed note\'.'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute
|
42
|
+
topic = find_exactly_one_topic(@filter, @strict)
|
43
|
+
path = File.join(topic.fullpath, Topicz::DIR_NOTES)
|
44
|
+
FileUtils.mkdir(path) unless Dir.exist? path
|
45
|
+
|
46
|
+
if @title
|
47
|
+
date = DateTime.now.strftime('%Y-%m-%d')
|
48
|
+
title = @title
|
49
|
+
filename = Zaru.sanitize! "#{date} #{title}.md"
|
50
|
+
else
|
51
|
+
date = DateTime.now.strftime('%Y-%m-%d %H%M')
|
52
|
+
title = 'Unnamed note'
|
53
|
+
filename = "#{date}.md"
|
54
|
+
end
|
55
|
+
|
56
|
+
path = File.join(path, filename)
|
57
|
+
|
58
|
+
unless File.exists? path
|
59
|
+
File.open(path, 'w') do | file |
|
60
|
+
file.puts("# #{topic.title} - #{title}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@kernel.exec "#{editor} \"#{path}\""
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'repository_command'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Topicz::Commands
|
5
|
+
|
6
|
+
class PathCommand < RepositoryCommand
|
7
|
+
|
8
|
+
def initialize(config_file = nil, arguments = [])
|
9
|
+
super(config_file)
|
10
|
+
@strict = false
|
11
|
+
option_parser.order! arguments
|
12
|
+
@filter = arguments.join ' '
|
13
|
+
end
|
14
|
+
|
15
|
+
def option_parser
|
16
|
+
OptionParser.new do |options|
|
17
|
+
options.banner = 'Usage: path <filter>'
|
18
|
+
options.on('-s', '--strict', 'Do a full strict match on topic IDs only') do
|
19
|
+
@strict = true
|
20
|
+
end
|
21
|
+
options.separator ''
|
22
|
+
options.separator 'Prints the absolute path to the topic that matches <filter>.'
|
23
|
+
options.separator ''
|
24
|
+
options.separator 'The filter specifies the text to search on. The text is matched against the topic\'s: '
|
25
|
+
options.separator '- path on the filesystem'
|
26
|
+
options.separator '- id, if specified in the topic\'s topic.yaml file'
|
27
|
+
options.separator '- title, if specified in the topic\'s topic.yaml file'
|
28
|
+
options.separator '- aliases, if specified in the topic\'s topic.yaml file'
|
29
|
+
options.separator ''
|
30
|
+
options.separator 'The filter must return precisely one topic. Zero or more matches give an error.'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute
|
35
|
+
print find_exactly_one_topic(@filter, @strict).fullpath
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'repository_command'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Topicz::Commands
|
5
|
+
|
6
|
+
class ReportCommand < RepositoryCommand
|
7
|
+
|
8
|
+
def initialize(config_file = nil, arguments = [])
|
9
|
+
super(config_file)
|
10
|
+
@week = Date.today.cweek
|
11
|
+
@year = Date.today.cwyear
|
12
|
+
option_parser.order! arguments
|
13
|
+
@filter = arguments.join ' '
|
14
|
+
end
|
15
|
+
|
16
|
+
def option_parser
|
17
|
+
OptionParser.new do |options|
|
18
|
+
options.banner = 'Usage: report'
|
19
|
+
options.on('-w', '--week WEEK', 'Use week WEEK instead of the current week') do |week|
|
20
|
+
@week = week.to_i
|
21
|
+
end
|
22
|
+
options.on('-y', '--year YEAR', 'Use year YEAR instead of the current year') do |year|
|
23
|
+
@year = year.to_i
|
24
|
+
end
|
25
|
+
options.separator ''
|
26
|
+
options.separator 'Generates a weekly report from all journals across all topics.'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute
|
31
|
+
year = @year.to_s
|
32
|
+
week = @week.to_s.rjust(2, '0')
|
33
|
+
path = File.join(Topicz::DIR_JOURNAL, "#{year}-week-#{week}.md")
|
34
|
+
|
35
|
+
@repository.topics.each do |topic|
|
36
|
+
journal = File.join(topic.fullpath, path)
|
37
|
+
next unless File.exist? journal
|
38
|
+
|
39
|
+
puts "## #{topic.title}"
|
40
|
+
puts
|
41
|
+
puts File.readlines(journal)
|
42
|
+
.drop(2).join() # Drop first 2 lines: title (week) and empty line
|
43
|
+
.gsub(/^#(.*)/, '##\1') # Add an extra '#' in front of every title
|
44
|
+
.strip # Remove leading and ending whitespace
|
45
|
+
puts
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'topicz/defaults'
|
2
|
+
require 'topicz/repository'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Topicz::Commands
|
6
|
+
|
7
|
+
class RepositoryCommand
|
8
|
+
|
9
|
+
def initialize(config_file = nil)
|
10
|
+
@config = load_config(config_file)
|
11
|
+
@repository = load_repository
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_config(config_file)
|
15
|
+
file = config_file != nil ? config_file : Topicz::DEFAULT_CONFIG_LOCATION
|
16
|
+
unless File.exist? file
|
17
|
+
raise "File doesn't exist: #{file}."
|
18
|
+
end
|
19
|
+
unless File.readable? file
|
20
|
+
raise "File isn't readable: #{file}."
|
21
|
+
end
|
22
|
+
begin
|
23
|
+
config = YAML.load_file(file)
|
24
|
+
rescue
|
25
|
+
raise "Not a valid YAML file: #{file}."
|
26
|
+
end
|
27
|
+
unless config.has_key?('repository')
|
28
|
+
raise "Missing required property 'repository' in configuration file: #{file}."
|
29
|
+
end
|
30
|
+
config
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_repository
|
34
|
+
directory = @config['repository']
|
35
|
+
unless Dir.exist? directory
|
36
|
+
raise "Repository directory doesn't exist: #{directory}."
|
37
|
+
end
|
38
|
+
Topicz::Repository.new(directory)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_exactly_one_topic(filter, strict)
|
42
|
+
if strict
|
43
|
+
topic = @repository[filter]
|
44
|
+
if topic == nil
|
45
|
+
raise "No topic found with ID: '#{filter}'"
|
46
|
+
end
|
47
|
+
topic
|
48
|
+
else
|
49
|
+
topics = @repository.find_all filter
|
50
|
+
if topics.length == 0
|
51
|
+
raise "No topics found matching the search filter: '#{filter}'"
|
52
|
+
end
|
53
|
+
if topics.length > 1
|
54
|
+
matches = topics.map { |t| t.title }.join("\n")
|
55
|
+
raise "Multiple topics match the search filter: '#{filter}'. Matches:\n#{matches}"
|
56
|
+
end
|
57
|
+
topics[0]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Topicz
|
2
|
+
DEFAULT_CONFIG_LOCATION = File.join(Dir.home, '.topiczrc')
|
3
|
+
|
4
|
+
TOPIC_FILE = 'topic.yaml'
|
5
|
+
|
6
|
+
DIR_NOTES = 'Notes'
|
7
|
+
DIR_JOURNAL = 'Journal'
|
8
|
+
DIR_REFERENCE = 'Reference'
|
9
|
+
DIR_DOCUMENTS = 'Documents'
|
10
|
+
|
11
|
+
DIRECTORIES = [DIR_NOTES, DIR_JOURNAL, DIR_REFERENCE, DIR_DOCUMENTS]
|
12
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Topicz
|
4
|
+
|
5
|
+
class Repository
|
6
|
+
|
7
|
+
attr_reader :root
|
8
|
+
|
9
|
+
def initialize(root)
|
10
|
+
@root = root
|
11
|
+
@topics = {}
|
12
|
+
errors = []
|
13
|
+
Dir.foreach(root) do |path|
|
14
|
+
next if path.start_with?('.')
|
15
|
+
next unless File.directory?(File.join(root, path))
|
16
|
+
topic = Topic.new(root, path)
|
17
|
+
if @topics.has_key?(topic.id)
|
18
|
+
errors << "Error in topic '#{topic.title}': ID '#{topic.id}' is already in use."
|
19
|
+
end
|
20
|
+
@topics[topic.id] = topic
|
21
|
+
end
|
22
|
+
@topics.each_value do |topic|
|
23
|
+
topic.references.each do |ref|
|
24
|
+
unless @topics.has_key?(ref)
|
25
|
+
errors << "Error in topic '#{topic.title}': Reference to non-existing topic ID '#{ref}'."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
unless errors.empty?
|
30
|
+
raise errors
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](id)
|
35
|
+
@topics[id]
|
36
|
+
end
|
37
|
+
|
38
|
+
def exist_title?(title)
|
39
|
+
@topics.values.any? { |t| t.title == title }
|
40
|
+
end
|
41
|
+
|
42
|
+
def topics
|
43
|
+
@topics.values
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_all(filter = nil)
|
47
|
+
@topics.values.select { |t| t.matches(filter) }
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class Topic
|
53
|
+
|
54
|
+
attr_reader :id, :path, :title, :category, :aliases, :metadata
|
55
|
+
|
56
|
+
def initialize(root, path)
|
57
|
+
@root = root
|
58
|
+
@path = path
|
59
|
+
|
60
|
+
descriptor = File.join(@root, @path, 'topic.yaml')
|
61
|
+
yaml = if File.exist?(descriptor)
|
62
|
+
YAML.load_file(descriptor)
|
63
|
+
else
|
64
|
+
{}
|
65
|
+
end
|
66
|
+
|
67
|
+
@id = yaml['id'] || create_id(path)
|
68
|
+
@category = yaml['category'] || 'none'
|
69
|
+
@title = yaml['title'] || @path
|
70
|
+
@aliases = yaml['aliases'] || []
|
71
|
+
@parents = yaml['depends on'] || {}
|
72
|
+
@relations = yaml['relates to'] || {}
|
73
|
+
@metadata = yaml['metadata'] || {}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Checks whether this topic's title or one of its aliases matches the filter
|
77
|
+
# The filter may be `nil`, in which case it is said to match.
|
78
|
+
def matches(filter)
|
79
|
+
return true unless filter
|
80
|
+
filter = filter.downcase
|
81
|
+
@title.downcase.include?(filter) ||
|
82
|
+
@id.downcase.include?(filter) ||
|
83
|
+
!(@aliases.select { |a| a.downcase.include?(filter) }.empty?) ||
|
84
|
+
@path.downcase.include?(filter)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Full path to this topic on disk
|
88
|
+
def fullpath
|
89
|
+
File.join(@root, @path)
|
90
|
+
end
|
91
|
+
|
92
|
+
# List of unique topic IDs that this topic refers to.
|
93
|
+
def references
|
94
|
+
@parents.keys
|
95
|
+
end
|
96
|
+
|
97
|
+
def parents
|
98
|
+
@parents.keys
|
99
|
+
end
|
100
|
+
|
101
|
+
def relations
|
102
|
+
@relations.keys
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_s
|
106
|
+
<<-EOS
|
107
|
+
Topic '#{@id}' {
|
108
|
+
title: '#{@title}',
|
109
|
+
aliases: '#{@aliases}',
|
110
|
+
category: '#{@category}',
|
111
|
+
parents: #{@parents},
|
112
|
+
relations: #{@relations}
|
113
|
+
}
|
114
|
+
EOS
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def create_id(path)
|
119
|
+
path.downcase.gsub(/[\_,;\.\&]/, '').gsub(/[ ]/, '-').gsub(/\-\-/, '-')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
data/topicz.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'topicz/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "topicz"
|
8
|
+
spec.version = Topicz::VERSION
|
9
|
+
spec.authors = ["Vincent Oostindië"]
|
10
|
+
spec.email = ["vincent@ulso.nl"]
|
11
|
+
|
12
|
+
spec.summary = %q{Filesystem based topic administration tool}
|
13
|
+
spec.description = %q{Topicz is a filesystem based topic administration tool. All it does is manipulate files and directories. But it does so in a structured way, helping you to keep track of your own documents, reference material, and topic relationships.}
|
14
|
+
spec.homepage = "https://github.com/voostindie/topicz"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'zaru', '~> 0.1.0'
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.3.0" # this one works with the IntelliJ Rake Runner plugin
|
25
|
+
spec.add_development_dependency "simplecov", "~> 0.11.2"
|
26
|
+
spec.add_development_dependency "fakefs", "~> 0.8.1"
|
27
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: topicz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vincent Oostindië
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zaru
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.3.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.11.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.11.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: fakefs
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.8.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.8.1
|
97
|
+
description: Topicz is a filesystem based topic administration tool. All it does is
|
98
|
+
manipulate files and directories. But it does so in a structured way, helping you
|
99
|
+
to keep track of your own documents, reference material, and topic relationships.
|
100
|
+
email:
|
101
|
+
- vincent@ulso.nl
|
102
|
+
executables:
|
103
|
+
- topicz
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- ".rspec"
|
109
|
+
- ".travis.yml"
|
110
|
+
- Gemfile
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- bin/console
|
115
|
+
- bin/setup
|
116
|
+
- exe/topicz
|
117
|
+
- lib/topicz/application.rb
|
118
|
+
- lib/topicz/command_factory.rb
|
119
|
+
- lib/topicz/commands/_index.rb
|
120
|
+
- lib/topicz/commands/alfred_command.rb
|
121
|
+
- lib/topicz/commands/create_command.rb
|
122
|
+
- lib/topicz/commands/editor_command.rb
|
123
|
+
- lib/topicz/commands/help_command.rb
|
124
|
+
- lib/topicz/commands/init_command.rb
|
125
|
+
- lib/topicz/commands/journal_command.rb
|
126
|
+
- lib/topicz/commands/note_command.rb
|
127
|
+
- lib/topicz/commands/path_command.rb
|
128
|
+
- lib/topicz/commands/report_command.rb
|
129
|
+
- lib/topicz/commands/repository_command.rb
|
130
|
+
- lib/topicz/defaults.rb
|
131
|
+
- lib/topicz/repository.rb
|
132
|
+
- lib/topicz/version.rb
|
133
|
+
- topicz.gemspec
|
134
|
+
- vendor/cache/diff-lcs-1.2.5.gem
|
135
|
+
- vendor/cache/docile-1.1.5.gem
|
136
|
+
- vendor/cache/fakefs-0.8.1.gem
|
137
|
+
- vendor/cache/json-1.8.3.gem
|
138
|
+
- vendor/cache/rake-10.5.0.gem
|
139
|
+
- vendor/cache/rspec-3.3.0.gem
|
140
|
+
- vendor/cache/rspec-core-3.3.2.gem
|
141
|
+
- vendor/cache/rspec-expectations-3.3.1.gem
|
142
|
+
- vendor/cache/rspec-mocks-3.3.2.gem
|
143
|
+
- vendor/cache/rspec-support-3.3.0.gem
|
144
|
+
- vendor/cache/simplecov-0.11.2.gem
|
145
|
+
- vendor/cache/simplecov-html-0.10.0.gem
|
146
|
+
- vendor/cache/zaru-0.1.0.gem
|
147
|
+
homepage: https://github.com/voostindie/topicz
|
148
|
+
licenses: []
|
149
|
+
metadata: {}
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
requirements: []
|
165
|
+
rubyforge_project:
|
166
|
+
rubygems_version: 2.6.6
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: Filesystem based topic administration tool
|
170
|
+
test_files: []
|