clipcellar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS.md ADDED
@@ -0,0 +1,5 @@
1
+ # NEWS
2
+
3
+ ## 0.0.1: 2014-07-29
4
+
5
+ Initial release!
@@ -0,0 +1,70 @@
1
+ # Clipcellar
2
+
3
+ Clipcellar is a full-text searchable data store for clipboard.
4
+
5
+ Powered by [GTK+][] (via [Ruby/GTK2][]) and [Groonga][] (via [Rroonga][]).
6
+
7
+ [GTK+]:http://www.gtk.org/
8
+ [Ruby/GTK2]:http://ruby-gnome2.sourceforge.jp/
9
+ [Groonga]:http://groonga.org/
10
+ [Rroonga]:http://ranguba.org/
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'clipcellar'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install clipcellar
25
+
26
+ ## Usage
27
+
28
+ ### Show help
29
+
30
+ $ clipcellar
31
+
32
+ ### Add a text (or clipboard) to data store
33
+
34
+ $ clipcellar set [TEXT]
35
+
36
+ ### Add a text from files (or stdin)
37
+
38
+ $ clipcellar input [FILE]...
39
+
40
+ ### Show added texts in data store
41
+
42
+ $ clipcellar show [--gui]
43
+
44
+ ### Full-text search (and set a text to clipboard)
45
+
46
+ $ clipcellar search WORD... [--gui]
47
+
48
+ ### Watch clipboard (Ctrl+C to stop)
49
+
50
+ $ clipcellar watch
51
+
52
+ ### Delete data store and all added texts
53
+
54
+ $ clipcellar destroy
55
+
56
+ ## License
57
+
58
+ Copyright (c) 2014 Masafumi Yokoyama `<myokoym@gmail.com>`
59
+
60
+ LGPLv2.1 or later.
61
+
62
+ See LICENSE.txt or http://www.gnu.org/licenses/lgpl-2.1 for details.
63
+
64
+ ## Contributing
65
+
66
+ 1. Fork it
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
68
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
69
+ 4. Push to the branch (`git push origin my-new-feature`)
70
+ 5. Create new Pull Request
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "bundler/gem_tasks"
18
+
19
+ desc "Run test"
20
+ task :test do
21
+ Bundler::GemHelper.install_tasks
22
+ ruby("test/run-test.rb")
23
+ end
24
+
25
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ require "clipcellar/command"
20
+
21
+ Clipcellar::Command.start
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clipcellar/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clipcellar"
8
+ spec.version = Clipcellar::VERSION
9
+ spec.authors = ["Masafumi Yokoyama"]
10
+ spec.email = ["myokoym@gmail.com"]
11
+ spec.summary = %q{Full-Text Searchable Data Store for Clipboard}
12
+ spec.description = %q{Clipcellar is a full-text searchable data store for clipboard by GTK+ (via Ruby/GTK2) and Groonga (via Rroonga).}
13
+ spec.homepage = "https://github.com/myokoym/clipcellar"
14
+ spec.license = "LGPLv2.1 or later"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency("gtk2")
22
+ spec.add_runtime_dependency("rroonga")
23
+ spec.add_runtime_dependency("thor")
24
+
25
+ spec.add_development_dependency("test-unit")
26
+ spec.add_development_dependency("test-unit-notify")
27
+ spec.add_development_dependency("test-unit-rr")
28
+ spec.add_development_dependency("bundler", "~> 1.6")
29
+ spec.add_development_dependency("rake")
30
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "clipcellar/version"
18
+ require "clipcellar/groonga_database"
19
+ require "clipcellar/groonga_searcher"
20
+ require "clipcellar/clipboard"
21
+
22
+ module Clipcellar
23
+ end
@@ -0,0 +1,82 @@
1
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ begin
18
+ require "gtk2"
19
+ rescue Gtk::InitError
20
+ # a window system is unnecessary.
21
+ end
22
+
23
+ module Clipcellar
24
+ class Clipboard
25
+ class << self
26
+ def watch
27
+ current_text = get
28
+ GLib::Timeout.add(500) do
29
+ text = get
30
+ if current_text != text
31
+ yield(text)
32
+ current_text = text
33
+ end
34
+ true
35
+ end
36
+
37
+ begin
38
+ Gtk.main
39
+ rescue Interrupt => e
40
+ $stderr.puts(e.message)
41
+ end
42
+ end
43
+
44
+ def get
45
+ if /darwin/ =~ RUBY_PLATFORM
46
+ `pbpaste`
47
+ else
48
+ clipboard.wait_for_text
49
+ end
50
+ end
51
+
52
+ def set(text)
53
+ copy_to_clipboard(text)
54
+
55
+ # workaround for CLI
56
+ GLib::Timeout.add(1) do
57
+ Gtk.main_quit
58
+ end
59
+ Gtk.main
60
+ end
61
+
62
+ def copy_to_clipboard(text)
63
+ if /darwin/ =~ RUBY_PLATFORM
64
+ require "tempfile"
65
+ Tempfile.open(["clipcellar", "w"]) do |file|
66
+ text.each_line do |line|
67
+ file.puts(line)
68
+ end
69
+ file.flush
70
+ system("pbcopy < #{file.path}")
71
+ end
72
+ else
73
+ clipboard.text = text
74
+ end
75
+ end
76
+
77
+ def clipboard
78
+ Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,158 @@
1
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "thor"
18
+ require "fileutils"
19
+ require "clipcellar/version"
20
+ require "clipcellar/groonga_database"
21
+ require "clipcellar/groonga_searcher"
22
+ require "clipcellar/clipboard"
23
+ require "clipcellar/window"
24
+
25
+ module Clipcellar
26
+ class Command < Thor
27
+ map "-v" => :version
28
+ map "-a" => :set
29
+ map "-A" => :input
30
+ map "-s" => :show
31
+ map "-S" => :search
32
+ map "-W" => :watch
33
+
34
+ attr_reader :database_dir
35
+
36
+ def initialize(*args)
37
+ super
38
+ @base_dir = File.join(File.expand_path("~"), ".clipcellar")
39
+ @database_dir = File.join(@base_dir, "db")
40
+ end
41
+
42
+ desc "version", "Show version number."
43
+ def version
44
+ puts Clipcellar::VERSION
45
+ end
46
+
47
+ desc "set [TEXT]", "Add a text (or clipboard) to data store"
48
+ def set(text=nil)
49
+ text = Clipboard.get unless text
50
+ return unless text
51
+ Clipboard.set(text)
52
+ add(text)
53
+ end
54
+
55
+ desc "input [FILE]...", "Add a text from files (or stdin)"
56
+ def input(*files)
57
+ ARGV.shift
58
+ text = ""
59
+ while line = ARGF.gets
60
+ text << line
61
+ end
62
+ Clipboard.set(text)
63
+ add(text)
64
+ end
65
+
66
+ desc "watch", "Watch clipboard to add clipboard to data store"
67
+ def watch
68
+ $stderr.puts("Watching... (Ctrl+C to Stop)")
69
+ Clipboard.watch do |text|
70
+ add(text)
71
+ end
72
+ end
73
+
74
+ desc "show [--gui]", "Show added texts in data store"
75
+ option :gui, :type => :boolean, :desc => "GUI mode"
76
+ def show
77
+ records = []
78
+ GroongaDatabase.new.open(@database_dir) do |database|
79
+ database.clipboards.each do |clipboard|
80
+ record = {}
81
+ record[:key] = clipboard._key
82
+ record[:text] = clipboard.text
83
+ record[:time] = clipboard.created_at
84
+ records << record
85
+ end
86
+ end
87
+
88
+ records.sort_by! do |record|
89
+ record[:time]
90
+ end
91
+
92
+ if options[:gui]
93
+ records.reverse!
94
+ window = Window.new(records)
95
+ window.run
96
+ else
97
+ records.each do |record|
98
+ text = record[:text]
99
+ time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
100
+ formatted_text = text.gsub(/\n/, " ") if text
101
+ puts "#{time} #{formatted_text}"
102
+ end
103
+ end
104
+ end
105
+
106
+ desc "search WORD... [--gui]", "Search texts from data store"
107
+ option :gui, :type => :boolean, :desc => "GUI mode"
108
+ def search(required_word, *optional_words)
109
+ words = [required_word]
110
+ words += optional_words
111
+
112
+ records = []
113
+ GroongaDatabase.new.open(@database_dir) do |database|
114
+ reverse = options[:gui] ? true : false
115
+ sorted_clipboards = GroongaSearcher.search(database,
116
+ words,
117
+ :reverse => reverse)
118
+
119
+ sorted_clipboards.each do |clipboard|
120
+ record = {}
121
+ record[:key] = clipboard._key
122
+ record[:text] = clipboard.text
123
+ record[:time] = clipboard.created_at
124
+ records << record
125
+ end
126
+ end
127
+
128
+ if options[:gui]
129
+ window = Window.new(records)
130
+ window.run
131
+ else
132
+ text = nil
133
+ records.each do |record|
134
+ text = record[:text]
135
+ time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
136
+ formatted_text = text.gsub(/\n/, " ") if text
137
+ puts "#{time} #{formatted_text}"
138
+ end
139
+ Clipboard.set(text) if text
140
+ end
141
+ end
142
+
143
+ desc "destroy", "Delete data store and all added texts"
144
+ def destroy
145
+ FileUtils.rm(Dir.glob(File.join(@database_dir, "clipcellar.db*")))
146
+ FileUtils.rmdir(@database_dir)
147
+ end
148
+
149
+ private
150
+ def add(text)
151
+ time = Time.now
152
+ id = time.strftime("%Y%m%d%H%M%S%L")
153
+ GroongaDatabase.new.open(@database_dir) do |database|
154
+ database.add(id, text, time)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,100 @@
1
+ # Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "groonga"
18
+
19
+ module Clipcellar
20
+ class GroongaDatabase
21
+ def initialize
22
+ @database = nil
23
+ end
24
+
25
+ def open(base_path, encoding=:utf8)
26
+ reset_context(encoding)
27
+ path = File.join(base_path, "clipcellar.db")
28
+ if File.exist?(path)
29
+ @database = Groonga::Database.open(path)
30
+ populate_schema
31
+ else
32
+ FileUtils.mkdir_p(base_path)
33
+ populate(path)
34
+ end
35
+ if block_given?
36
+ begin
37
+ yield(self)
38
+ ensure
39
+ close unless closed?
40
+ end
41
+ end
42
+ end
43
+
44
+ def add(id, text, time)
45
+ clipboards.add(id,
46
+ {
47
+ :text => text,
48
+ :created_at => time,
49
+ })
50
+ end
51
+
52
+ def delete(id)
53
+ clipboards.delete(id)
54
+ end
55
+
56
+ def close
57
+ @database.close
58
+ @database = nil
59
+ end
60
+
61
+ def closed?
62
+ @database.nil? or @database.closed?
63
+ end
64
+
65
+ def clipboards
66
+ @clipboards ||= Groonga["Clipboards"]
67
+ end
68
+
69
+ def dump
70
+ Groonga::DatabaseDumper.dump
71
+ end
72
+
73
+ private
74
+ def reset_context(encoding)
75
+ Groonga::Context.default_options = {:encoding => encoding}
76
+ Groonga::Context.default = nil
77
+ end
78
+
79
+ def populate(path)
80
+ @database = Groonga::Database.create(:path => path)
81
+ populate_schema
82
+ end
83
+
84
+ def populate_schema
85
+ Groonga::Schema.define do |schema|
86
+ schema.create_table("Clipboards", :type => :hash) do |table|
87
+ table.text("text")
88
+ table.time("created_at")
89
+ end
90
+
91
+ schema.create_table("Terms",
92
+ :type => :patricia_trie,
93
+ :key_normalize => true,
94
+ :default_tokenizer => "TokenBigram") do |table|
95
+ table.index("Clipboards.text")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end