aef-launchy-opensearch 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,43 @@
1
+ === 1.2.1 / 2009-04-05
2
+
3
+ * 1 minor bugfix
4
+
5
+ * Loading path problem fixed
6
+
7
+ === 1.2.0 / 2009-04-05
8
+
9
+ * 1 major enhancement
10
+
11
+ * Namespaced into module Aef
12
+
13
+ * 5 minor enhancements
14
+
15
+ * Better metadata for gem
16
+ * Test refactoring
17
+ * Minor refactorings
18
+ * Improved documentation
19
+ * Added test for --version command
20
+ * Renamed gem and executable from launchy_opensearch to launchy-opensearch
21
+
22
+ * 1 minor bugfix
23
+
24
+ * Fixed some bugs related to Ruby 1.8.6
25
+
26
+ === 1.1.0 / 2009-03-08
27
+
28
+ * 1 major enhancements
29
+
30
+ * Better commandline outputs
31
+ * Now compatibile with ruby 1.9.1
32
+
33
+ * 3 minor enhancement
34
+
35
+ * Added a --version command to output version and licensing information
36
+ * Added automated tests
37
+ * Some more comments
38
+
39
+ === 1.0.0 / 2009-02-24
40
+
41
+ * 1 major enhancement
42
+
43
+ * Birthday!
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ COPYING.txt
5
+ Rakefile
6
+ bin/launchy-opensearch
7
+ lib/launchy_opensearch.rb
8
+ lib/launchy_opensearch/launchy_opensearch.rb
9
+ spec/launchy_opensearch_spec.rb
10
+ spec/fixtures/launchy.ini
11
+ spec/fixtures/discogs.xml
12
+ spec/fixtures/secure-wikipedia-english.xml
13
+ spec/fixtures/youtube.xml
data/README.rdoc ADDED
@@ -0,0 +1,164 @@
1
+ = LaunchyOpenSearch
2
+
3
+ * Project: https://rubyforge.org/projects/aef/
4
+ * RDoc: http://aef.rubyforge.org/launchy-opensearch/
5
+ * Github: http://github.com/aef/launchy-opensearch/
6
+
7
+ == DESCRIPTION:
8
+
9
+ LaunchyOpenSearch is a Ruby library and commandline tool that allows to parse
10
+ OpenSearch XML files and include them as search engines in the Weby plugin of
11
+ the keystroke app launcher Launchy by editing Launchy's ini config file.
12
+
13
+ == FEATURES/PROBLEMS:
14
+
15
+ * Usable as library and commandline tool
16
+ * Tested and fully working on:
17
+ * Ubuntu Linux 8.10 i386_64 (Ruby 1.8.7 and 1.9.1p0)
18
+ * Debian GNU/Linux 4.0 i386 (Ruby 1.8.6)
19
+ * Windows XP i386 (Ruby 1.8.6)
20
+ * The commandline tool doesn't work with Ruby 1.9.x because the user-choices gem
21
+ is not yet updated. A patch is available here:
22
+ https://rubyforge.org/tracker/index.php?func=detail&aid=24307&group_id=4192&atid=16176
23
+
24
+ == SYNOPSIS:
25
+
26
+ === Commandline
27
+
28
+ Launchy should be closed while using OpenSearchLaunchy
29
+
30
+ launchy-opensearch youtube.xml
31
+
32
+ === Library
33
+
34
+ Notice that the library is written with an underscore instead of the dash used
35
+ in the gem's and commandline tool's name
36
+
37
+ require 'launchy_opensearch'
38
+
39
+ Determine launchy ini path
40
+
41
+ config_path = Aef::LaunchyOpenSearch.launchy_config_path
42
+
43
+ Parse an OpenSearch engine into a variable
44
+
45
+ new_engine = Aef::LaunchyOpenSearch.parse_opensearch_file('youtube.xml')
46
+
47
+ Read the launchy ini file into a variable
48
+
49
+ config = Aef::LaunchyOpenSearch.read_config_hash(config_path)
50
+
51
+ Extract hash with current search engines
52
+
53
+ current_engines = Aef::LaunchyOpenSearch.extract_config_hash(config)
54
+
55
+ Add new engine to the engines hash
56
+
57
+ new_engines = current_engines + new_engine
58
+
59
+ Patch the engines hash into the config hash
60
+
61
+ Aef::LaunchyOpenSearch.patch_config_hash(config, new_engines)
62
+
63
+ Write the config hash as ini back to the disk
64
+
65
+ Aef::LaunchyOpenSearch.write_config_hash(config, config_path)
66
+
67
+ == REQUIREMENTS:
68
+
69
+ * rubygems
70
+ * hpricot
71
+ * facets
72
+ * sys-uname
73
+
74
+ === Additional for commandline
75
+ * user-choices
76
+
77
+ === Additional for automated testing
78
+ * rspec
79
+
80
+ == INSTALL:
81
+
82
+ === Normal
83
+
84
+ gem install launchy-opensearch
85
+
86
+ Additionally for the commandline tool:
87
+
88
+ gem install user-choices
89
+
90
+ === High security (recommended)
91
+
92
+ There is a high security installation option available through rubygems. It is
93
+ highly recommended over the normal installation, although it may be a bit less
94
+ comfortable. To use the installation method, you will need my public key, which
95
+ I use for cryptographic signatures on all my gems. You can find the public key
96
+ and more detailed verification information in the aef-certificates section of my
97
+ rubyforge project[https://rubyforge.org/frs/?group_id=7890&release_id=31749]
98
+
99
+ Add the key to your rubygems' trusted certificates by the following command:
100
+
101
+ gem cert --add aef.pem
102
+
103
+ Now you can install the gem while automatically verifying it's signature by the
104
+ following command:
105
+
106
+ gem install launchy-opensearch --ignore-dependencies -P HighSecurity
107
+
108
+ Please notice that you will need other keys for dependent libraries, so you may
109
+ have to install dependencies manually.
110
+
111
+ === Automated testing
112
+
113
+ You can test this package through rspec on your system. First find the path
114
+ where the gem was installed to:
115
+
116
+ gem which launchy-opensearch
117
+
118
+ Go into the root directory of the installed gem and run the following command
119
+ to start the test runner:
120
+
121
+ rake spec
122
+
123
+ On Windows systems you have to run the following instead:
124
+
125
+ spec spec/**/*_spec.rb
126
+
127
+ If something goes wrong you should be noticed through failing examples.
128
+
129
+ == DEVELOPMENT:
130
+
131
+ This software is developed in the source code management system git hosted
132
+ at github.com. You can download the most recent sourcecode through the following
133
+ command:
134
+
135
+ git clone git://github.com/aef/launchy-opensearch.git
136
+
137
+ Help on making this software better is always very appreciated. If you want your
138
+ changes to be included in the official release, please send me a patch through
139
+ the project's tracker[https://rubyforge.org/tracker/?group_id=7890] at
140
+ rubyforge.org. You can generate a patch-file by the following command:
141
+
142
+ git diff > patch.diff
143
+
144
+ Please make sure to write tests for your changes and notice that I can't promise
145
+ to include your changes before reviewing them.
146
+
147
+ == LICENSE:
148
+
149
+ Copyright 2009 Alexander E. Fischer <aef@raxys.net>
150
+
151
+ This file is part of LaunchyOpenSearch.
152
+
153
+ LaunchyOpenSearch is free software: you can redistribute it and/or modify
154
+ it under the terms of the GNU General Public License as published by
155
+ the Free Software Foundation, either version 3 of the License, or
156
+ (at your option) any later version.
157
+
158
+ This program is distributed in the hope that it will be useful,
159
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
160
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
161
+ GNU General Public License for more details.
162
+
163
+ You should have received a copy of the GNU General Public License
164
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/launchy_opensearch.rb'
6
+
7
+ Hoe.new('launchy-opensearch', Aef::LaunchyOpenSearch::VERSION) do |p|
8
+ p.rubyforge_name = 'aef'
9
+ p.developer('Alexander E. Fischer', 'aef@raxys.net')
10
+ p.extra_deps = %w{facets hpricot sys-uname}
11
+ p.extra_dev_deps = %w{user-choices}
12
+ p.url = 'https://rubyforge.org/projects/aef/'
13
+ p.readme_file = 'README.rdoc'
14
+ p.extra_rdoc_files = %w{README.rdoc}
15
+ p.spec_extras = {
16
+ :rdoc_options => ['--main', 'README.rdoc', '--inline-source', '--line-numbers', '--title', 'LaunchyOpenSearch']
17
+ }
18
+ end
19
+
20
+ # vim: syntax=Ruby
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright 2009 Alexander E. Fischer <aef@raxys.net>
4
+ #
5
+ # This file is part of LaunchyOpenSearch.
6
+ #
7
+ # LaunchyOpenSearch is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ # TODO: If user-choices patch gets accepted, use :one_way => true for --version
21
+
22
+ # If library is not locally accessible, use gem to include it.
23
+ begin
24
+ require 'lib/launchy_opensearch'
25
+ rescue LoadError
26
+ require 'rubygems'
27
+ require 'launchy_opensearch'
28
+ end
29
+
30
+ # User friendly message if user-choices is not available
31
+ begin
32
+ require 'user-choices'
33
+ rescue LoadError
34
+ warn "This command needs the user-choices gem to be installed.\n\nSolution: gem install user-choices"; exit false
35
+ end
36
+
37
+ class Aef::LaunchyOpenSearch::Application < UserChoices::Command
38
+ include UserChoices
39
+
40
+ MODES = %w{append replace}
41
+
42
+ # Prepare configuration sources
43
+ def add_sources(builder)
44
+ builder.add_source(
45
+ CommandLineSource, :usage,
46
+ "Usage: #$PROGRAM_NAME [options] opensearch-files\n\n",
47
+ "Import OpenSearch XML files into the Weby plugin of the keystroke application launcher Launchy\n"
48
+ )
49
+ end
50
+
51
+ # Define configuration options
52
+ def add_choices(builder)
53
+ builder.add_choice(:config_path, :default => Aef::LaunchyOpenSearch.launchy_config_path) do |cli|
54
+ cli.uses_option('-c', '--config FILE',
55
+ 'Launchy ini file to modify. Uses the current user\'s config file by default.')
56
+ end
57
+
58
+ builder.add_choice(:mode, :default => MODES.first, :type => MODES ) do |cli|
59
+ cli.uses_option('-m', '--mode MODE',
60
+ "Insert mode. Possible settings: #{MODES.join(', ')}. Default is #{MODES.first}.")
61
+ end
62
+
63
+ builder.add_choice(:version, :default => false, :type => :boolean) do |cli|
64
+ cli.uses_switch('-v', '--version', 'Display version and licensing information')
65
+ end
66
+
67
+ builder.add_choice(:filenames) {|cli| cli.uses_arglist }
68
+ end
69
+
70
+ # Manual option post processing
71
+ def postprocess_user_choices
72
+ version if @user_choices[:version]
73
+ end
74
+
75
+ # Version and licensing information output
76
+ def version
77
+ name = 'LaunchyOpenSearch'
78
+ puts "#{name} #{Aef::LaunchyOpenSearch::VERSION}"
79
+ puts
80
+ puts 'Project: https://rubyforge.org/projects/aef/'
81
+ puts "RDoc: http://aef.rubyforge.org/launchy-opensearch/"
82
+ puts "Github: http://github.com/aef/launchy-opensearch/"
83
+ puts
84
+ puts 'Copyright 2009 Alexander E. Fischer <aef@raxys.net>'
85
+ # Read and print licensing information from the top of this file
86
+ if Gem::Version.new(RUBY_VERSION) <= Gem::Version.new('1.8.6')
87
+ puts File.read(__FILE__).map{|line| line[2..-1]}[5..17]
88
+ else
89
+ puts File.read(__FILE__).lines.map{|line| line[2..-1]}[5..17]
90
+ end
91
+ exit
92
+ end
93
+
94
+ # Main program
95
+ def execute
96
+ if @user_choices[:filenames].empty?
97
+ warn 'No OpenSearch files specified'; exit false
98
+ end
99
+
100
+ @user_choices[:filenames].each do |filename|
101
+ warn "Ignoring #{filename}. Not readable or missing." unless File.readable?(filename)
102
+ end
103
+
104
+ new_engines = Aef::LaunchyOpenSearch.parse_opensearch_files(@user_choices[:filenames])
105
+ count = new_engines.size
106
+ config = Aef::LaunchyOpenSearch.read_config_hash(@user_choices[:config_path])
107
+
108
+ if @user_choices[:mode] == 'append'
109
+ current_engines = Aef::LaunchyOpenSearch.extract_config_hash(config)
110
+ new_engines = current_engines + new_engines
111
+ end
112
+
113
+ Aef::LaunchyOpenSearch.patch_config_hash(config, new_engines)
114
+ Aef::LaunchyOpenSearch.write_config_hash(config, @user_choices[:config_path])
115
+
116
+ puts "#{count} search engines installed."
117
+ end
118
+ end
119
+
120
+ S4tUtils.with_pleasant_exceptions {Aef::LaunchyOpenSearch::Application.new.execute}
@@ -0,0 +1,30 @@
1
+ # Copyright 2009 Alexander E. Fischer <aef@raxys.net>
2
+ #
3
+ # This file is part of LaunchyOpenSearch.
4
+ #
5
+ # LaunchyOpenSearch is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program 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
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Namespace for projects of Alexander E. Fischer <aef@raxys.net>
19
+ #
20
+ # If you want to be able to simply type Example instead of Aef::Example to
21
+ # address classes in this namespace simply write the following before using the
22
+ # classes:
23
+ #
24
+ # include Aef
25
+ module Aef
26
+
27
+ end
28
+
29
+ libdir = File.dirname(__FILE__)
30
+ require File.join(libdir, 'launchy_opensearch/launchy_opensearch')
@@ -0,0 +1,167 @@
1
+ # Copyright 2009 Alexander E. Fischer <aef@raxys.net>
2
+ #
3
+ # This file is part of LaunchyOpenSearch.
4
+ #
5
+ # LaunchyOpenSearch is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program 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
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'tempfile'
19
+ require 'uri'
20
+
21
+ require 'rubygems'
22
+ require 'hpricot'
23
+ require 'facets/ini'
24
+ require 'sys/uname'
25
+
26
+ # Offers static methods for all steps in parsing usefull information out of the
27
+ # OpenSearch XML format and modifying the configuration of Launchy's Weby plugin
28
+ module Aef::LaunchyOpenSearch
29
+
30
+ VERSION = '1.2.1'
31
+
32
+ # Determines the location of Launchy's configuration Ini file on different
33
+ # platforms
34
+ def self.launchy_config_path
35
+ if Sys::Uname.sysname.downcase.include?("windows")
36
+ File.join(ENV['APPDATA'], 'Launchy', 'Launchy.ini')
37
+ else
38
+ File.join(ENV['HOME'], '.launchy', 'launchy.ini')
39
+ end
40
+ end
41
+
42
+ # Parses an OpenSearch XML document
43
+ #
44
+ # Returns a Hash with the keys :name, :base, :query and :default
45
+ def self.parse_opensearch(content)
46
+ opensearch = Hpricot.XML(content)
47
+
48
+ uri = URI.parse(opensearch.at('os:Url').get_attribute('template').gsub(/\{searchTerms\}/, ':PLACEHOLDER'))
49
+
50
+ {
51
+ :name => opensearch.at('os:ShortName').inner_html,
52
+ :base => "#{uri.scheme}://#{uri.host}/",
53
+ :query => "\"#{uri.path[1..-1]}?#{uri.query}\"".gsub(/:PLACEHOLDER/, '%s'),
54
+ :default => 'false'
55
+ }
56
+ end
57
+
58
+ # Reads and parses a single OpenSearch file from the filesystem.
59
+ #
60
+ # Returns a Hash with the keys :name, :base, :query and :default
61
+ def self.parse_opensearch_file(file)
62
+ if file.is_a?(String)
63
+ content = File.read(file)
64
+ elsif file.respond_to?(:read)
65
+ content = file.read
66
+ else
67
+ raise "Expected file path as string or an object responding to read. Got #{file.class.name}"
68
+ end
69
+
70
+ parse_opensearch(content)
71
+ end
72
+
73
+ # Reads multiple OpenSearch files from filesystem
74
+ #
75
+ # Returns an Array of Hashes with the keys :name, :base, :query and :default
76
+ def self.parse_opensearch_files(file_list)
77
+ engines = []
78
+ file_list.each do |file|
79
+ if File.readable?(file)
80
+ engines << parse_opensearch_file(file)
81
+ end
82
+ end
83
+ engines
84
+ end
85
+
86
+ # Reads an Ini file from filesystem into a launchy config hash.
87
+ def self.read_config_hash(path)
88
+ # Ini class doesn't like empty lines and can only read from files.
89
+ original = File.read(path)
90
+
91
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9')
92
+ cleaned = original.lines.map{|line| line.chomp.squeeze(' ')}.reject{|line| line == ''}.join("\n")
93
+ else
94
+ cleaned = original.map{|line| line.chomp.squeeze(' ')}.reject{|line| line == ''}.join("\n")
95
+ end
96
+
97
+ temp_file = Tempfile.open('launchy')
98
+ temp_file.write(cleaned)
99
+ temp_file.close
100
+
101
+ config = Ini.read_from_file(temp_file.path)
102
+
103
+ temp_file.unlink
104
+
105
+ config
106
+ end
107
+
108
+ # Reads relevant parts out of the weby section of a launchy config hash.
109
+ #
110
+ # Returns an Array of Hashes with the keys :name, :base, :query and :default
111
+ def self.extract_config_hash(config_hash)
112
+ engines = {}
113
+ config_hash['weby'].each do |entry, value|
114
+ entry.match(/^sites\\([0-9]{1,2})\\(.*)$/)
115
+ if $1 and $2
116
+ engines[$1.to_i] ||= {}
117
+ engines[$1.to_i][$2.to_sym] = value
118
+ end
119
+ end
120
+
121
+ engines_array = []
122
+ engines.sort.each do |key, content|
123
+ engines_array << content
124
+ end
125
+ engines_array
126
+ end
127
+
128
+ # Replaces the site entries of the Weby section of a launchy config hash with
129
+ # an engines array as returned by extract_config_hash
130
+ #
131
+ # Attention: This method modifies the config hash given as argument. It is
132
+ # returned as result only for convenience.
133
+ def self.patch_config_hash(config_hash, engines)
134
+ new_section = {}
135
+ config_hash['weby'].each do |entry, value|
136
+ unless entry.match(/^sites\\/)
137
+ new_section[entry] = value
138
+ end
139
+ end
140
+ engines.each_with_index do |settings, i|
141
+ settings.each {|key, value|
142
+ new_section["sites\\#{i + 1}\\#{key}"] = value
143
+ }
144
+ end
145
+ new_section['sites\\size'] = engines.size.to_s
146
+ config_hash['weby'] = new_section
147
+ config_hash
148
+ end
149
+
150
+ # Writes a launchy config hash back to filesystem in INI format
151
+ def self.write_config_hash(config_hash, path)
152
+ if File.exists?(path) and File.readable?(path) and File.file?(path)
153
+ original = File.read(path)
154
+
155
+ File.open("#{path}.bak", 'w') do |f|
156
+ f.write(original)
157
+ end
158
+ end
159
+
160
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9')
161
+ # Facets ini parser doesn't seems to use the .lines enumerator yet
162
+ Ini.write_to_file(path, config_hash)
163
+ else
164
+ Ini.write_to_file(path, config_hash, "Written by LaunchyOpenSearch #{Time.now}\n")
165
+ end
166
+ end
167
+ end