snipper 0.0.1

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.
data/bin/snipper ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'snipper'
4
+
5
+ @options = {:command => "new"}
6
+
7
+ OptionParser.new do |opts|
8
+ opts.on("--lang LANGUAGE", "-l", "Snippet Language") do |v|
9
+ @options[:syntax] = v
10
+ end
11
+
12
+ opts.on("-L","List Languages") do
13
+ @options[:list] = true
14
+ end
15
+ end.parse!
16
+
17
+ exec "pygmentize -L lexer" if @options[:list]
18
+
19
+ @options[:command] = ARGV[0] if ARGV.size >= 1
20
+
21
+ case @options[:command]
22
+ when "search", "s"
23
+ Snipper::Command::Search.new(@options)
24
+ when "delete", "d", "rm"
25
+ Snipper::Command::Delete.new(@options)
26
+ when "view", "v"
27
+ Snipper::Command::View.new(@options)
28
+ when "edit", "e"
29
+ puts Snipper::Command::Edit.new(@options).url
30
+ else
31
+ puts Snipper::Command::New.new(@options).url
32
+ end
data/lib/snipper.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'rubygems'
2
+ require 'pygments.rb'
3
+ require 'optparse'
4
+ require 'etc'
5
+ require 'snipper/config'
6
+ require 'snipper/snippet'
7
+ require 'snipper/command/new'
8
+ require 'snipper/command/edit'
9
+ require 'snipper/command/delete'
10
+ require 'snipper/command/search'
11
+ require 'snipper/command/view'
12
+ require 'snipper/version'
13
+ require 'snipper/output/pygments_output.rb'
14
+ require 'yaml'
15
+ require 'fileutils'
16
+
17
+ class Snipper
18
+ def initialize
19
+ config = File.join(Etc.getpwuid.dir, ".snipper", "config.yml")
20
+
21
+ if File.exist?(config)
22
+ Config.load(config)
23
+ else
24
+ Config.load({:config_file => config})
25
+ end
26
+
27
+ setup
28
+ end
29
+
30
+ def setup
31
+ FileUtils.mkdir_p(Config[:snippets_dir])
32
+
33
+ unless File.exist?(Config[:config_file])
34
+ File.open(Config[:config_file], "w") do |c|
35
+ c.print Config.to_yaml
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ class Array
42
+ def in_groups_of(chunk_size, padded_with=nil, &block)
43
+ arr = self.clone
44
+
45
+ # how many to add
46
+ padding = chunk_size - (arr.size % chunk_size)
47
+
48
+ # pad at the end
49
+ arr.concat([padded_with] * padding) unless padding == chunk_size
50
+
51
+ # how many chunks we'll make
52
+ count = arr.size / chunk_size
53
+
54
+ # make that many arrays
55
+ result = []
56
+ count.times {|s| result << arr[s * chunk_size, chunk_size]}
57
+
58
+ if block_given?
59
+ result.each_with_index do |a, i|
60
+ case block.arity
61
+ when 1
62
+ yield(a)
63
+ when 2
64
+ yield(a, (i == result.size - 1))
65
+ else
66
+ raise "Expected 1 or 2 arguments, got #{block.arity}"
67
+ end
68
+ end
69
+ else
70
+ result
71
+ end
72
+ end unless method_defined?(:in_groups_of)
73
+ end
@@ -0,0 +1,21 @@
1
+ class Snipper
2
+ class Command
3
+ class Delete
4
+ attr_reader :url, :snippet
5
+
6
+ def initialize(options)
7
+ @options = options
8
+
9
+ snipper = Snipper.new
10
+
11
+ raise "Please specify a snippet number" unless ARGV[1] =~ /^\d+$/
12
+
13
+ options[:snippet] = ARGV[1]
14
+
15
+ snippet = Snippet.new(nil, @options)
16
+
17
+ snippet.delete!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ class Snipper
2
+ class Command
3
+ class Edit
4
+ attr_reader :url, :snippet
5
+
6
+ def initialize(options)
7
+ @options = options
8
+
9
+ snipper = Snipper.new
10
+
11
+ raise "Please specify a snippet number" unless ARGV[1] =~ /^\d+$/
12
+
13
+ options[:snippet] = ARGV[1]
14
+
15
+ snippet = Snippet.new(nil, @options)
16
+ @url = snippet.url_for_snippet
17
+
18
+ files = snippet.files.map {|file| File.join(snippet.snippet_path, file)}
19
+
20
+ editor = ENV["EDITOR"] || "vi"
21
+
22
+ system "%s %s" % [editor, files.join(" ")]
23
+
24
+ files.each do |file|
25
+ unless File.size?(file)
26
+ puts "Removing #{file} from the snippet"
27
+ File.unlink(file)
28
+ end
29
+ end
30
+
31
+ snippet.update_html
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ class Snipper
2
+ class Command
3
+ class New
4
+ attr_reader :url, :snippet
5
+
6
+ def initialize(options)
7
+ @options = options
8
+
9
+ @snipper = Snipper.new
10
+
11
+ # if the first arg is a number and it isnt a file path
12
+ # then assume this is a snippet id we want to append to
13
+ if ARGV.first =~ /^\d+$/ && !File.exist?(ARGV.first)
14
+ options[:snippet] = ARGV.shift
15
+ end
16
+
17
+ if STDIN.tty?
18
+ abort "Please specify a filename or provide one on STDIN" if ARGV.empty?
19
+
20
+ txt = ARGV
21
+ else
22
+ txt = STDIN.read
23
+ end
24
+
25
+ @snippet = Snippet.new(txt, @options)
26
+ @url = @snippet.url_for_snippet
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ class Snipper
2
+ class Command
3
+ class Search
4
+ attr_reader :url, :snippet
5
+
6
+ def initialize(options)
7
+ @options = options
8
+
9
+ snipper = Snipper.new
10
+
11
+ raise "Please specify a search string" unless ARGV[1]
12
+
13
+ Dir.chdir(Config[:snippets_dir]) do
14
+ grep = Config[:grep].gsub("%Q%", ARGV[1].gsub("'", "\\'"))
15
+
16
+ system grep
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ class Snipper
2
+ class Command
3
+ class View
4
+ attr_reader :url, :snippet
5
+
6
+ def initialize(options)
7
+ @options = options
8
+
9
+ snipper = Snipper.new
10
+
11
+ raise "Please specify a snippet number" unless ARGV[1] =~ /^\d+$/
12
+
13
+ options[:snippet] = ARGV[1]
14
+
15
+ snippet = Snippet.new(nil, @options)
16
+
17
+ files = snippet.files.map {|file| File.join(snippet.snippet_path, file)}
18
+
19
+ pager = ENV["PAGER"] || "less"
20
+
21
+ system "cat %s|%s" % [files.join(" "), pager]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ ## description: shitty config class
2
+ class Snipper
3
+ class Config
4
+ class << self
5
+ def load(source)
6
+ @config = {:renderer => "ultraviolet",
7
+ :grep => "grep --color=auto -riHn -C 2 '%Q%' *",
8
+ :snipper_dir => File.join(Etc.getpwuid.dir, ".snipper"),
9
+ :snippets_dir => File.join(Etc.getpwuid.dir, ".snipper", "snippets"),
10
+ :config_file => File.join(Etc.getpwuid.dir, ".snipper", "config.yml")}
11
+
12
+ if source.is_a?(String)
13
+ raise "Config file #{source} not found" unless File.exist?(source)
14
+
15
+ config = YAML.load_file(source)
16
+ @config.merge! config if config
17
+
18
+ elsif source.is_a?(Hash)
19
+ @config.merge! source
20
+ end
21
+ end
22
+
23
+ def include?(key)
24
+ @config.include?(key)
25
+ end
26
+
27
+ def [](key)
28
+ @config[key]
29
+ end
30
+
31
+ def to_yaml
32
+ @config.to_yaml
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,75 @@
1
+ class Snipper
2
+ class Output
3
+ class Pygments_Output
4
+ def initialize(snippet)
5
+ @snippet = snippet
6
+ end
7
+
8
+ def self.list_langs
9
+ Pygments.lexers.map{|l| l[1][:aliases]}.flatten.sort
10
+ end
11
+
12
+ def save
13
+ theme = Config[:theme] || "default"
14
+
15
+ raise "Please set the :public_target_dir setting" unless Config[:public_target_dir]
16
+
17
+ path = @snippet.snippet_html_path
18
+
19
+ FileUtils.mkdir_p(path)
20
+
21
+ File.open(File.join(path, "index.html"), "w") do |html|
22
+ html.puts "<head>"
23
+ html.puts "<link rel='stylesheet' type='text/css' href='../css/#{theme}.css' media='all' />"
24
+ html.puts "<style type='text/css'>"
25
+ html.puts ".highlighttable { background-color: #253B76; width: 100%;}"
26
+ html.puts ".content { width: 100%;}"
27
+ html.puts ".linenos { text-align: right; color: white; width: 30px; }"
28
+
29
+ if Config[:dark_theme]
30
+ html.puts ".highlight { background-color: #0C1000 }"
31
+ else
32
+ html.puts ".highlight { background-color: white }"
33
+ end
34
+
35
+ html.puts "</style>"
36
+ html.puts "</head>"
37
+ html.puts "<body>"
38
+
39
+ html.puts "<table class='content'>"
40
+ html.puts "<tr><td>"
41
+
42
+ @snippet.each_file do |file, text, headers|
43
+ File.open(File.join(path, "#{File.basename(file)}.raw"), "w") do |f|
44
+ f.puts text
45
+ end
46
+
47
+ syntax = @snippet.options[:syntax]
48
+
49
+ unless syntax
50
+ lexer = Pygments::Lexer.find_by_extname(File.extname(file))
51
+
52
+ if lexer
53
+ syntax = lexer.name
54
+ else
55
+ syntax = Config[:default_syntax] || "ruby"
56
+ end
57
+ end
58
+
59
+ raise "Unknown syntax #{syntax}" unless self.class.list_langs.include?(syntax)
60
+
61
+ html.puts "<H2>%s</H2>" % [ headers["description"] ] if headers["description"]
62
+ html.puts Pygments.highlight(text, :lexer => syntax, :options => {:linenos => "table", :style => theme})
63
+ html.puts "syntax: %s download: <a href='%s.raw'>raw</a>" % [ syntax, File.basename(file) ]
64
+ html.puts "<br /><br />"
65
+ end
66
+
67
+ html.puts "</td></tr>"
68
+ html.puts "<tr align='right'><td><a href='%s'>Snipper</a></td></tr>" % [ "http://github.com/ripienaar/snipper" ]
69
+ html.puts "</table>"
70
+ html.puts "</body>"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,158 @@
1
+ class Snipper
2
+ class Snippet
3
+ attr_reader :options
4
+
5
+ def initialize(snippet, options = {:snippet => nil})
6
+ @options = options
7
+
8
+ if options[:snippet]
9
+ raise "Snippets ids must be numerical" unless options[:snippet] == options[:snippet].to_i.to_s
10
+ raise "Unknown snippet %s" % [ options[:snippet] ] unless File.exist?(snippet_path)
11
+ end
12
+
13
+ if snippet
14
+ # new snippet
15
+ begin
16
+ unless @options[:snippet]
17
+ @options[:snippet] = next_snippet_id
18
+
19
+ create_snippet_dir
20
+
21
+ append_snippet(snippet)
22
+
23
+ update_html
24
+ else # add to existing snippet
25
+ append_snippet(snippet)
26
+
27
+ update_html
28
+ end
29
+ rescue Exception => e
30
+ STDERR.puts "Could not save snippet: #{e.class}: #{e}"
31
+ # TODO: clear up half baked snippets
32
+
33
+ raise
34
+ end
35
+ end
36
+ end
37
+
38
+ def delete!
39
+ raise "Refusing to remove all snippets" if File.expand_path(snippet_path) == File.expand_path(Config[:public_target_dir])
40
+ raise "Snippet path #{snippet_path} looks suspect, refusing to delete" unless snippet_path.match(/\d+$/)
41
+
42
+ raise "Refusing to remove all snippets" if File.expand_path(snippet_html_path) == File.expand_path(Config[:public_target_dir])
43
+ raise "Snippet path #{snippet_html_path} looks suspect, refusing to delete" unless snippet_html_path.match(/\d+$/)
44
+
45
+ if File.directory?(snippet_path)
46
+ puts "Removing #{snippet_path}"
47
+ FileUtils.remove_entry_secure(snippet_path)
48
+ end
49
+
50
+ if File.directory?(snippet_html_path)
51
+ puts "Removing #{snippet_html_path}"
52
+ FileUtils.remove_entry_secure(snippet_html_path)
53
+ end
54
+ end
55
+
56
+ def append_snippet(snippet)
57
+ if snippet.is_a?(Array)
58
+ [snippet].flatten.each do |file|
59
+ save_next_file(snippet_file(File.read(file)))
60
+ end
61
+ else
62
+ save_next_file(snippet_file(snippet))
63
+ end
64
+ end
65
+
66
+ def update_html
67
+ Output::Pygments_Output.new(self).save
68
+ end
69
+
70
+ def url_for_snippet
71
+ "%s/%s" % [ Config[:baseurl], @options[:snippet] ]
72
+ end
73
+
74
+ def save_next_file(text)
75
+ file_name = File.join(snippet_path, next_file_id.to_s)
76
+
77
+ File.open(file_name, "w") do |file|
78
+ file.puts text
79
+ end
80
+
81
+ return file_name
82
+ end
83
+
84
+ def files
85
+ Dir.entries(snippet_path).grep(/^\d+$/).sort_by{|i| Integer(i)}
86
+ end
87
+
88
+ def each_file
89
+ files.each do |file|
90
+ headers, text = parse_file(File.read(File.join(snippet_path, file)))
91
+
92
+ yield File.join(snippet_path, file), text, headers
93
+ end
94
+ end
95
+
96
+ def parse_file(file)
97
+ headers = {}
98
+ text = ""
99
+ inheader = true
100
+
101
+ file.each_line do |line|
102
+ # skips the first blank line
103
+ if line == "\n" && inheader
104
+ inheader = false
105
+ next
106
+ end
107
+
108
+ if line.start_with?("##") && inheader
109
+ if line =~ /^## (\S+): (.+)/
110
+ headers[$1] = $2
111
+ elsif line =~ /## (.+)/
112
+ headers["description"] = $1
113
+ end
114
+ else
115
+ inheader = false
116
+ text += line
117
+ end
118
+ end
119
+
120
+ return headers, text
121
+ end
122
+
123
+ def snippet_html_path
124
+ File.expand_path(File.join(Config[:public_target_dir], @options[:snippet].to_s))
125
+ end
126
+
127
+ def snippet_path
128
+ File.join(Config[:snippets_dir], @options[:snippet].to_s)
129
+ end
130
+
131
+ def create_snippet_dir
132
+ FileUtils.mkdir(snippet_path)
133
+ end
134
+
135
+ def next_file_id
136
+ Integer(files[-1]) + 1
137
+ end
138
+
139
+ def next_snippet_id
140
+ snippets = Dir.entries(Config[:snippets_dir]).grep(/^\d+$/)
141
+ Integer(snippets.map{|s| s.to_i}.sort.max) + 1
142
+ end
143
+
144
+ def snippet_file(text)
145
+ snippet = StringIO.new
146
+
147
+ unless text.start_with?("##")
148
+ snippet.puts "## description: %s" % [ @options[:description] ] if @options[:description]
149
+ snippet.puts "## syntax: %s" % [ @options[:syntax] ] if @options[:syntax]
150
+ snippet.puts
151
+ end
152
+
153
+ snippet.puts text
154
+
155
+ snippet.string
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,3 @@
1
+ class Snipper
2
+ VERSION="0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snipper
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - R.I.Pienaar
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-26 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rdoc
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: pygments.rb
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: A Unix CLI centric snippet manager that produces static files
64
+ email: rip@devco.net
65
+ executables:
66
+ - snipper
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - bin/snipper
73
+ - lib/snipper/snippet.rb
74
+ - lib/snipper/config.rb
75
+ - lib/snipper/output/pygments_output.rb
76
+ - lib/snipper/version.rb
77
+ - lib/snipper/command/delete.rb
78
+ - lib/snipper/command/edit.rb
79
+ - lib/snipper/command/new.rb
80
+ - lib/snipper/command/search.rb
81
+ - lib/snipper/command/view.rb
82
+ - lib/snipper.rb
83
+ has_rdoc: true
84
+ homepage: http://devco.net/
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options: []
89
+
90
+ require_paths:
91
+ - lib
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.3.7
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Snipper
118
+ test_files: []
119
+