cue 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/LICENSE.md +21 -0
- data/README.md +1 -0
- data/bin/cue +16 -0
- data/bin/cue-add +4 -0
- data/bin/cue-clear +4 -0
- data/bin/cue-delete +4 -0
- data/bin/cue-list +4 -0
- data/bin/cue-show +4 -0
- data/bin/cue-toggle +4 -0
- data/lib/colorfool.rb +25 -0
- data/lib/cue.rb +2 -0
- data/lib/cue/command/add.rb +27 -0
- data/lib/cue/command/base.rb +98 -0
- data/lib/cue/command/clear.rb +11 -0
- data/lib/cue/command/delete.rb +21 -0
- data/lib/cue/command/list.rb +12 -0
- data/lib/cue/command/show.rb +21 -0
- data/lib/cue/command/toggle.rb +23 -0
- data/lib/cue/command/utils/editor.rb +40 -0
- data/lib/cue/group.rb +17 -0
- data/lib/cue/indicator.rb +39 -0
- data/lib/cue/item.rb +79 -0
- data/lib/cue/store/file.rb +129 -0
- data/lib/cue/store/memory.rb +38 -0
- data/lib/cue/store/redis.rb +110 -0
- data/lib/cue/version.rb +3 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3e67b30c304c9b38c0b750b943c045b8f5ccd20
|
4
|
+
data.tar.gz: 1f4757388541654942e0adee30d1aadc051f5bc4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0c64eede657d74523a8390aced2eefc1f0a7ede4f02e55c3268c34f7decedcb105b4fde9d069d4f7f8c6533e9cbf64dd7a0778e1d8e79784477b3f2c81946b7c
|
7
|
+
data.tar.gz: 2922380a75b9823bec5a02ab0bc730c00f1b672c6937531d02f5b5deff50121f07edf5c70a36d591c87cbaa9a418dfc47962ff5d18d74a6b1f5bd4f181e4fc59
|
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) arcsum42@gmail.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# cue
|
data/bin/cue
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
args = ARGV.dup
|
4
|
+
subcommand = args.first
|
5
|
+
|
6
|
+
if subcommand.nil?
|
7
|
+
system('cue-list')
|
8
|
+
elsif subcommand == 'rm'
|
9
|
+
args.shift
|
10
|
+
system('cue-delete', *args)
|
11
|
+
elsif ['add', 'clear', 'delete', 'list', 'show', 'toggle'].include?(subcommand)
|
12
|
+
args.shift
|
13
|
+
system("cue-#{subcommand}", *args)
|
14
|
+
else
|
15
|
+
abort "cue: #{subcommand} is not a cue command."
|
16
|
+
end
|
data/bin/cue-add
ADDED
data/bin/cue-clear
ADDED
data/bin/cue-delete
ADDED
data/bin/cue-list
ADDED
data/bin/cue-show
ADDED
data/bin/cue-toggle
ADDED
data/lib/colorfool.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module ColorFool
|
2
|
+
COLORS = %i(green red)
|
3
|
+
|
4
|
+
CODES = {
|
5
|
+
black: 30,
|
6
|
+
red: 31,
|
7
|
+
green: 32,
|
8
|
+
yellow: 33,
|
9
|
+
blue: 34,
|
10
|
+
magenta: 35,
|
11
|
+
cyan: 36,
|
12
|
+
white: 37
|
13
|
+
}
|
14
|
+
|
15
|
+
def code(color)
|
16
|
+
CODES[color]
|
17
|
+
end
|
18
|
+
|
19
|
+
def colorize(color, &block)
|
20
|
+
code = code(color)
|
21
|
+
content = block.call.to_s
|
22
|
+
|
23
|
+
"\e[#{code}m#{content}\e[0m"
|
24
|
+
end
|
25
|
+
end
|
data/lib/cue.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'cue/command/base'
|
2
|
+
require 'cue/command/utils/editor'
|
3
|
+
|
4
|
+
module Cue
|
5
|
+
module Command
|
6
|
+
class Add < Base
|
7
|
+
def initialize(args)
|
8
|
+
super(args)
|
9
|
+
|
10
|
+
if @args.size == 1
|
11
|
+
@content = @args.first
|
12
|
+
else
|
13
|
+
@content = Utils::Editor.new.read
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
item = Cue::Item.new(@content)
|
19
|
+
item.save(options.store)
|
20
|
+
end
|
21
|
+
|
22
|
+
def nargs
|
23
|
+
(0..1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'cue/store/redis'
|
2
|
+
require 'json'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
module Cue
|
7
|
+
module Command
|
8
|
+
class Base
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
def initialize(args)
|
12
|
+
@options = OpenStruct.new.tap do |opts|
|
13
|
+
opts.store = Cue::Store::Redis.new
|
14
|
+
end
|
15
|
+
|
16
|
+
@args = parser.parse(args)
|
17
|
+
configure_store
|
18
|
+
check_num_arguments
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_key(key_prefix)
|
22
|
+
keys = options.store.keys
|
23
|
+
item_ids = keys.select { |id| id.start_with?(key_prefix) }
|
24
|
+
|
25
|
+
if item_ids.size > 1
|
26
|
+
parser.abort "#{key_prefix} is too ambiguous."
|
27
|
+
end
|
28
|
+
|
29
|
+
item_id = item_ids.first
|
30
|
+
|
31
|
+
parser.abort("Couldn't find item #{key_prefix}.") unless item_id
|
32
|
+
item_id
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_item(key_prefix)
|
36
|
+
options.store.read(find_key(key_prefix))
|
37
|
+
end
|
38
|
+
|
39
|
+
def nargs
|
40
|
+
0
|
41
|
+
end
|
42
|
+
|
43
|
+
def parser
|
44
|
+
@parser ||= OptionParser.new do |opts|
|
45
|
+
opts.banner = "Usage: #{File.basename($0)} [options]"
|
46
|
+
|
47
|
+
opts.on('-s', '--store [STORE]', 'Specify the store adapter') do |store|
|
48
|
+
begin
|
49
|
+
require "cue/store/#{store}"
|
50
|
+
klass = store.split('_').map(&:capitalize).join
|
51
|
+
options.store = Cue.const_get("Store::#{klass}").new
|
52
|
+
rescue LoadError
|
53
|
+
opts.abort "Couldn't find the store adapter \"#{store}\"."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def check_num_arguments
|
62
|
+
case nargs
|
63
|
+
when Range
|
64
|
+
parser.abort('Invalid number of arguments.') unless nargs.cover?(@args.size)
|
65
|
+
else
|
66
|
+
parser.abort('Invalid number of arguments.') if @args.size != nargs
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def config_path
|
71
|
+
File.join(ENV['HOME'], '.cue', 'config.json')
|
72
|
+
end
|
73
|
+
|
74
|
+
def configure_redis
|
75
|
+
get_config(:redis) do |config|
|
76
|
+
Cue::Store::Redis.configure do |rconfig|
|
77
|
+
opts = config.select { |k,_| [:host, :port, :password].include?(k) }
|
78
|
+
rconfig.redis = Redis.new(opts)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def configure_store
|
84
|
+
case options.store
|
85
|
+
when Cue::Store::Redis
|
86
|
+
configure_redis
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_config(key=nil, &block)
|
91
|
+
return unless File.exists?(config_path)
|
92
|
+
|
93
|
+
json = JSON.parse(File.read(config_path), symbolize_names: true)
|
94
|
+
key.nil? ? yield(json) : yield(json[key])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'cue/command/base'
|
2
|
+
|
3
|
+
module Cue
|
4
|
+
module Command
|
5
|
+
class Delete < Base
|
6
|
+
def initialize(args)
|
7
|
+
super(args)
|
8
|
+
@sha = @args.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
item_key = find_key(@sha)
|
13
|
+
options.store.delete(item_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def nargs
|
17
|
+
1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'cue/command/base'
|
2
|
+
|
3
|
+
module Cue
|
4
|
+
module Command
|
5
|
+
class Show < Base
|
6
|
+
def initialize(args)
|
7
|
+
super(args)
|
8
|
+
@sha = @args.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
item = find_item(@sha)
|
13
|
+
puts item
|
14
|
+
end
|
15
|
+
|
16
|
+
def nargs
|
17
|
+
1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'cue/command/base'
|
2
|
+
|
3
|
+
module Cue
|
4
|
+
module Command
|
5
|
+
class Toggle < Base
|
6
|
+
def initialize(args)
|
7
|
+
super(args)
|
8
|
+
@sha = @args.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
find_item(@sha).tap do |item|
|
13
|
+
item.toggle!
|
14
|
+
item.save(options.store)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def nargs
|
19
|
+
1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module Cue
|
4
|
+
module Command
|
5
|
+
module Utils
|
6
|
+
class Editor
|
7
|
+
def basename
|
8
|
+
'cue'
|
9
|
+
end
|
10
|
+
|
11
|
+
def command
|
12
|
+
ENV['EDITOR'] || default_editor
|
13
|
+
end
|
14
|
+
|
15
|
+
def read
|
16
|
+
Process.wait(spawn([command, path].join(' ')))
|
17
|
+
content = File.read(path)
|
18
|
+
|
19
|
+
File.delete(path)
|
20
|
+
|
21
|
+
content
|
22
|
+
end
|
23
|
+
|
24
|
+
def dir
|
25
|
+
'/tmp'
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
@path ||= File.expand_path(Dir::Tmpname::make_tmpname(basename, nil), dir)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def default_editor
|
35
|
+
'vi'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/cue/group.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'colorfool'
|
2
|
+
|
3
|
+
module Cue
|
4
|
+
class Indicator
|
5
|
+
include ColorFool
|
6
|
+
|
7
|
+
attr_reader :state
|
8
|
+
|
9
|
+
def initialize(state)
|
10
|
+
@state = state
|
11
|
+
end
|
12
|
+
|
13
|
+
def color
|
14
|
+
case state
|
15
|
+
when :complete
|
16
|
+
:green
|
17
|
+
when :incomplete
|
18
|
+
:red
|
19
|
+
else
|
20
|
+
:none
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def symbol
|
25
|
+
case state
|
26
|
+
when :complete
|
27
|
+
'✓'
|
28
|
+
when :incomplete
|
29
|
+
'✗'
|
30
|
+
else
|
31
|
+
'?'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
colorize(color) { symbol }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/cue/item.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'colorfool'
|
2
|
+
require 'cue/indicator'
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Cue
|
6
|
+
class Item
|
7
|
+
include ColorFool
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_accessor :content
|
11
|
+
attr_reader :created_at, :state
|
12
|
+
|
13
|
+
def initialize(content, attrs={})
|
14
|
+
self.content = content
|
15
|
+
|
16
|
+
attrs.each do |k, v|
|
17
|
+
setter = "#{k}="
|
18
|
+
if respond_to?(setter, true)
|
19
|
+
send(setter, v)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
self.state = self.state || :incomplete
|
24
|
+
end
|
25
|
+
|
26
|
+
def <=>(other)
|
27
|
+
created_at <=> other.created_at
|
28
|
+
end
|
29
|
+
|
30
|
+
def ==(other)
|
31
|
+
content == other.content
|
32
|
+
end
|
33
|
+
|
34
|
+
def complete!
|
35
|
+
self.state = :complete
|
36
|
+
end
|
37
|
+
|
38
|
+
def complete?
|
39
|
+
self.state == :complete
|
40
|
+
end
|
41
|
+
|
42
|
+
def hash
|
43
|
+
Digest::SHA1.hexdigest(content)
|
44
|
+
end
|
45
|
+
|
46
|
+
def in_store?(store)
|
47
|
+
!store.read(hash).nil?
|
48
|
+
end
|
49
|
+
|
50
|
+
def incomplete!
|
51
|
+
self.state = :incomplete
|
52
|
+
end
|
53
|
+
|
54
|
+
def incomplete?
|
55
|
+
self.state == :incomplete
|
56
|
+
end
|
57
|
+
|
58
|
+
def save(store)
|
59
|
+
self.created_at = Time.now unless in_store?(store)
|
60
|
+
store.write(hash, self)
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
i = Indicator.new(state)
|
66
|
+
h = colorize(:cyan) { hash[0..6] }
|
67
|
+
"#{h} #{i} #{content}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def toggle!
|
71
|
+
complete? ? incomplete! : complete!
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
attr_writer :created_at, :state
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'cue/item'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
module Cue
|
6
|
+
module Store
|
7
|
+
class File
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(root_path=nil)
|
11
|
+
@root_path = root_path
|
12
|
+
FileUtils.mkdir_p(items_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
keys.each { |key| delete(key) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(key)
|
20
|
+
FileUtils.rm_r(item_path(key))
|
21
|
+
key_dir = dir_for(key)
|
22
|
+
FileUtils.rm_r(key_dir) if Dir[::File.join(key_dir, '*')].empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
items = keys.map(&method(:read)).sort
|
27
|
+
yield(items)
|
28
|
+
end
|
29
|
+
|
30
|
+
def keys
|
31
|
+
keys = []
|
32
|
+
|
33
|
+
Dir.glob(::File.join(items_path, '*')).each do |prefix_dir|
|
34
|
+
prefix = ::File.basename(prefix_dir)
|
35
|
+
Dir.glob(::File.join(prefix_dir, '*')).each do |suffix_file|
|
36
|
+
suffix = ::File.basename(suffix_file)
|
37
|
+
keys << (prefix + suffix)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def read(key)
|
45
|
+
return nil unless ::File.exists?(item_path(key))
|
46
|
+
|
47
|
+
data = nil
|
48
|
+
file_for_reading(item_path(key)) do |file|
|
49
|
+
data = uncompress(file.read)
|
50
|
+
end
|
51
|
+
return data if data.nil?
|
52
|
+
|
53
|
+
deserialize(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def write(key, item)
|
57
|
+
FileUtils.mkdir_p(dir_for(key))
|
58
|
+
file_for_writing(item_path(key)) do |file|
|
59
|
+
data = compress(serialize(item))
|
60
|
+
file.write(data)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def root_path
|
67
|
+
@root_path ||= ::File.join(ENV['HOME'], '.cue')
|
68
|
+
end
|
69
|
+
|
70
|
+
def compress(data)
|
71
|
+
Zlib::Deflate.deflate(data)
|
72
|
+
end
|
73
|
+
|
74
|
+
def deserialize(data)
|
75
|
+
created_at, state, content = data.unpack('Z*Z*Z*')
|
76
|
+
Cue::Item.new(content, created_at: created_at, state: state.to_sym)
|
77
|
+
end
|
78
|
+
|
79
|
+
def dir_for(key)
|
80
|
+
::File.join(items_path, prefix(key))
|
81
|
+
end
|
82
|
+
|
83
|
+
def file_for_writing(path, &block)
|
84
|
+
::File.open(path, mode_for_writing) { |file| yield(file) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def file_for_reading(path, &block)
|
88
|
+
::File.open(path, mode_for_reading) { |file| yield(file) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def item_path(key)
|
92
|
+
::File.join(dir_for(key), suffix(key))
|
93
|
+
end
|
94
|
+
|
95
|
+
def items_path
|
96
|
+
@items_path ||= ::File.join(root_path, 'items')
|
97
|
+
end
|
98
|
+
|
99
|
+
def keys_path
|
100
|
+
@keys_path ||= ::File.join(root_path, 'keys')
|
101
|
+
end
|
102
|
+
|
103
|
+
def mode_for_reading
|
104
|
+
::File::RDONLY
|
105
|
+
end
|
106
|
+
|
107
|
+
def mode_for_writing
|
108
|
+
::File::CREAT | ::File::WRONLY | ::File::TRUNC
|
109
|
+
end
|
110
|
+
|
111
|
+
def prefix(key)
|
112
|
+
key[0..1]
|
113
|
+
end
|
114
|
+
|
115
|
+
def serialize(item)
|
116
|
+
data = [item.created_at.to_i.to_s, item.state.to_s, item.content.to_s]
|
117
|
+
data.pack('Z*Z*Z*')
|
118
|
+
end
|
119
|
+
|
120
|
+
def suffix(key)
|
121
|
+
key[2...(key.size)]
|
122
|
+
end
|
123
|
+
|
124
|
+
def uncompress(data)
|
125
|
+
Zlib::Inflate.inflate(data)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Cue
|
2
|
+
module Store
|
3
|
+
class Memory
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def clear
|
7
|
+
@store = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete(key)
|
11
|
+
store.delete(key)
|
12
|
+
end
|
13
|
+
|
14
|
+
def each
|
15
|
+
items = keys.map(&method(:read)).sort
|
16
|
+
yield(items)
|
17
|
+
end
|
18
|
+
|
19
|
+
def keys
|
20
|
+
store.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def read(key)
|
24
|
+
store[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(key, item)
|
28
|
+
store[key] = item.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def store
|
34
|
+
@store ||= {}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'cue/item'
|
2
|
+
require 'redis'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Cue
|
6
|
+
module Store
|
7
|
+
class Redis
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_writer :namespace, :redis
|
12
|
+
|
13
|
+
def configure(&block)
|
14
|
+
self.tap { |klass| yield(klass) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def namespace
|
18
|
+
@namespace ||= 'cue'
|
19
|
+
end
|
20
|
+
|
21
|
+
def redis
|
22
|
+
@redis ||= ::Redis.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear
|
27
|
+
item_keys = redis.smembers(redis_key('keys'))
|
28
|
+
keys_key = redis_key('keys')
|
29
|
+
|
30
|
+
redis.del(item_keys, keys_key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(key)
|
34
|
+
item_key = item_key(key)
|
35
|
+
|
36
|
+
redis.del(item_key)
|
37
|
+
redis.srem(redis_key('keys'), item_key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def each
|
41
|
+
items = keys.map(&method(:read)).sort
|
42
|
+
yield(items)
|
43
|
+
end
|
44
|
+
|
45
|
+
def keys
|
46
|
+
redis.smembers(redis_key('keys')).map do |redis_key|
|
47
|
+
redis_key.split(':').last
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def read(key)
|
52
|
+
item_hash = redis.hgetall(item_key(key))
|
53
|
+
return nil if item_hash.empty?
|
54
|
+
|
55
|
+
deserialize(item_hash)
|
56
|
+
end
|
57
|
+
|
58
|
+
def write(key, item)
|
59
|
+
add_key(key)
|
60
|
+
add_item(key, item)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def add_item(key, item)
|
66
|
+
redis.mapped_hmset(item_key(key), serialize(item))
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_key(key)
|
70
|
+
redis.sadd(redis_key('keys'), item_key(key))
|
71
|
+
end
|
72
|
+
|
73
|
+
def deserialize(item_hash)
|
74
|
+
content = item_hash['content']
|
75
|
+
created_at = Time.parse(item_hash['created_at']) if item_hash['created_at']
|
76
|
+
state = item_hash['state'].to_sym
|
77
|
+
|
78
|
+
Cue::Item.new(content, created_at: created_at, state: state)
|
79
|
+
end
|
80
|
+
|
81
|
+
def item_key(key)
|
82
|
+
redis_key('item', key)
|
83
|
+
end
|
84
|
+
|
85
|
+
def namespace
|
86
|
+
self.class.namespace
|
87
|
+
end
|
88
|
+
|
89
|
+
def redis
|
90
|
+
self.class.redis
|
91
|
+
end
|
92
|
+
|
93
|
+
def redis_key(*components)
|
94
|
+
key = *components
|
95
|
+
key.unshift(namespace)
|
96
|
+
|
97
|
+
key.join(':')
|
98
|
+
end
|
99
|
+
|
100
|
+
def serialize(item)
|
101
|
+
{
|
102
|
+
'state' => item.state.to_s,
|
103
|
+
'content' => item.content
|
104
|
+
}.tap do |hash|
|
105
|
+
hash['created_at'] = item.created_at.to_s if item.created_at
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/cue/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Doe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Cue is a simple command line tool for keeping track of stuff.
|
42
|
+
email: arcsum42@gmail.com
|
43
|
+
executables:
|
44
|
+
- cue
|
45
|
+
- cue-add
|
46
|
+
- cue-clear
|
47
|
+
- cue-delete
|
48
|
+
- cue-list
|
49
|
+
- cue-show
|
50
|
+
- cue-toggle
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- README.md
|
55
|
+
- LICENSE.md
|
56
|
+
- lib/colorfool.rb
|
57
|
+
- lib/cue/command/add.rb
|
58
|
+
- lib/cue/command/base.rb
|
59
|
+
- lib/cue/command/clear.rb
|
60
|
+
- lib/cue/command/delete.rb
|
61
|
+
- lib/cue/command/list.rb
|
62
|
+
- lib/cue/command/show.rb
|
63
|
+
- lib/cue/command/toggle.rb
|
64
|
+
- lib/cue/command/utils/editor.rb
|
65
|
+
- lib/cue/group.rb
|
66
|
+
- lib/cue/indicator.rb
|
67
|
+
- lib/cue/item.rb
|
68
|
+
- lib/cue/store/file.rb
|
69
|
+
- lib/cue/store/memory.rb
|
70
|
+
- lib/cue/store/redis.rb
|
71
|
+
- lib/cue/version.rb
|
72
|
+
- lib/cue.rb
|
73
|
+
- bin/cue
|
74
|
+
- bin/cue-add
|
75
|
+
- bin/cue-clear
|
76
|
+
- bin/cue-delete
|
77
|
+
- bin/cue-list
|
78
|
+
- bin/cue-show
|
79
|
+
- bin/cue-toggle
|
80
|
+
homepage: https://github.com/arcsum/cue
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.0.0
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: keep track of stuff
|
104
|
+
test_files: []
|