geturl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 411f93bbcd2d3d670c3690bfe90c7bcf90b2af1c4ccab49c79cb0c697245ebc4
4
+ data.tar.gz: 53246132b245beb548e09311cb291d8dbce585fcaef3b98d3b6356d24ad42353
5
+ SHA512:
6
+ metadata.gz: 2fb81625dc50caa479849145ebb598d980d5948237c90b9394291a2ed0fd5a8b609ca59d0d84f42eda6388f10e25192703c5e3e6beed3c727dff8f7b3551dd0c
7
+ data.tar.gz: f97fe1082a7a68ebdd9e1ba91a955d99767e773a9f23f0668db3c772c2e5545ea7d84831abc9be219c0c98a88af32b7c296ceb093c6c0cd0d017b4c26f5dba75
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Taco Jan Osinga
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cli-parser'
4
+ require_relative '../lib/geturl'
5
+ require 'mustache'
6
+
7
+ module GetUrlCli
8
+ extend self
9
+
10
+ # Reads the command line and runs the selected action.
11
+ def run
12
+ flags = %w(--save --delete --reload --sources --register --unregister --reload -v --show-all --color)
13
+ parameters = %w(--format --sources --tags --description --template)
14
+ @arguments, @options = CliParser.parse(flags, parameters)
15
+
16
+ if @options.include?('--save')
17
+ save
18
+ elsif @options.include?('--delete')
19
+ delete
20
+ elsif @options.include?('--reload')
21
+ reload
22
+ elsif @options.include?('--sources')
23
+ sources
24
+ elsif @options.include?('--register')
25
+ register
26
+ elsif @options.include?('--unregister')
27
+ unregister
28
+ elsif @options.include?('--show-all')
29
+ show_all
30
+ elsif !@arguments.empty?
31
+ search
32
+ else
33
+ show_usage
34
+ end
35
+ end
36
+
37
+ # Outputs how to use this tool.
38
+ def show_usage
39
+ puts %q{
40
+ # Searching
41
+ geturl keyword1 keyword2 ...
42
+
43
+ geturl --format (json|yaml|mustache) keyword1 keyword2
44
+ geturl --source [source] keyword1 keyword2
45
+
46
+ # Local bookmarked urls
47
+ geturl --save http://www.example.com "Example website" --tags tag1,tag2 --description "This is an example"
48
+ geturl --delete http://www.example.com
49
+
50
+ # Read-only bookmarks from external sources
51
+ geturl --sources
52
+ geturl --register [source-id] [file-or-url]
53
+ geturl --unregister [source-id]
54
+ geturl --show-all [source-id]
55
+ geturl --reload
56
+
57
+ }.gsub(/^ /, '')
58
+ end
59
+
60
+ # Stores a url locally.
61
+ def save
62
+ check_arguments(%w(url name), 'save')
63
+ url = @arguments[0]
64
+ name = @arguments[1].gsub(/^"/, "").gsub(/"$/, "")
65
+
66
+ GetUrl::LocalUrls.save_url(url,
67
+ name,
68
+ 'description' => @options['--description'].to_s.gsub(/^"/, "").gsub(/"$/, ""),
69
+ 'tags' => @options['--tags'].to_s.gsub(/^"/, "").gsub(/"$/, "").split(','))
70
+ end
71
+
72
+ # Deletes a locally stored url.
73
+ def delete
74
+ check_arguments(%w(url), 'delete')
75
+ url = @arguments[0]
76
+ GetUrl::LocalUrls.delete_url(url)
77
+ end
78
+
79
+ # Reloads all sources.
80
+ def reload
81
+ id = @arguments[0]
82
+ GetUrl::Sources.reload(id, verbose: true)
83
+ end
84
+
85
+ # Lists all sources.
86
+ def sources
87
+ output_data(GetUrl::Sources.get_sources)
88
+ end
89
+
90
+ # Registers the given source.
91
+ def register
92
+ check_arguments(%w(id url), 'register')
93
+ id = @arguments[0].downcase
94
+ url = @arguments[1]
95
+
96
+ if id == 'local'
97
+ puts "The source id 'local' is reserved."
98
+ exit 3
99
+ end
100
+
101
+ print "Registering #{id} ...\r"
102
+ GetUrl::Sources.register_source(id, url)
103
+ puts "Registered #{id}" + "".ljust(5)
104
+ GetUrl::Sources.reload(id, url, verbose: true)
105
+ end
106
+
107
+ # Checks whether the arguments fit the required size, based on given parameter names.
108
+ def check_arguments(names, action)
109
+ i = 0
110
+ blocked_names = ''
111
+ names.each {|name| blocked_names += "[#{name}] "}
112
+ names.each {|name|
113
+ argument = @arguments[i]
114
+ if argument.to_s.empty?
115
+ puts "Missing #{name}"
116
+ puts ''
117
+ puts "Usage: geturl --#{action} #{blocked_names}"
118
+ exit 1
119
+ end
120
+ i += 1;
121
+ }
122
+ end
123
+
124
+ # Unregister the given source.
125
+ def unregister
126
+ check_arguments(%w(id), 'unregister')
127
+
128
+ id = @arguments[0]
129
+ unless GetUrl::Sources.get_sources.keys.include?(id)
130
+ puts "Unknown id #{id}"
131
+ exit 2
132
+ end
133
+
134
+ GetUrl::Sources.unregister_source(id)
135
+ puts "Removed #{id}"
136
+ end
137
+
138
+ # Shows all urls.
139
+ def show_all
140
+ output_data(GetUrl::Searcher.all(@arguments))
141
+ end
142
+
143
+ # Searches for specific urls.
144
+ def search
145
+ ids = @option['--sources'].to_s.split(',') rescue nil
146
+ items = GetUrl::Searcher.search(@arguments, 'ids' => ids)
147
+ output_data(items)
148
+ end
149
+
150
+ # Outputs the given data object in the selected format.
151
+ def output_data(data)
152
+ format = @options['--format'].to_s.downcase
153
+ if format == 'json'
154
+ puts JSON.pretty_generate(data)
155
+ elsif format == 'yaml'
156
+ puts data.to_yaml
157
+ elsif format == 'mustache'
158
+ template = @options['--template'].to_s.gsub(/^"/, "").gsub(/"$/, "")
159
+ if template.empty?
160
+ puts 'Missing option: --template'
161
+ exit 4
162
+ end
163
+ data = {items: data} if data.is_a?(Array)
164
+ template = File.read(template.gsub(/^@/, '')) if template.start_with?('@')
165
+ puts Mustache.render(template, data)
166
+ elsif data.is_a?(Hash)
167
+ data.each {|key, value|
168
+ puts key.ljust(20) + value.to_s
169
+ }
170
+ elsif data.is_a?(Array)
171
+ data.each {|value|
172
+ if value.is_a?(Hash) && !value['url'].empty? && !value['name'].empty?
173
+ output_item(value)
174
+ else
175
+ puts value
176
+ end
177
+ }
178
+ else
179
+ puts data
180
+ end
181
+
182
+ end
183
+
184
+ # Outputs the given item as text.
185
+ def output_item(item)
186
+ puts item['name']
187
+ puts item['url']
188
+ puts item['description'] unless item['description'].to_s.empty?
189
+ puts 'Tags: ' + item['tags'].join(', ') unless item['tags'].empty?
190
+ puts ''
191
+ end
192
+
193
+ end
194
+
195
+ GetUrlCli.run
196
+
197
+
198
+
199
+
@@ -0,0 +1,5 @@
1
+ $:.unshift(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require_relative 'geturl/geturl-sources'
4
+ require_relative 'geturl/geturl-local-urls'
5
+ require_relative 'geturl/geturl-searcher'
@@ -0,0 +1,72 @@
1
+ require 'yaml'
2
+
3
+ module GetUrl
4
+
5
+ # The FileManager deals with the local (cached) files.
6
+ module FileManager
7
+ extend self
8
+
9
+ # Ensure the sources folder exists.
10
+ @sources_path = File.join(Dir.home, '.geturl', 'sources')
11
+ FileUtils.mkdir_p(@sources_path)
12
+
13
+ # Loads the items from the given local file.
14
+ #
15
+ # @param [String] file The path of the file
16
+ # @return [Array] The items
17
+ def load_items_from_yaml_file(file)
18
+ str = File.read(file) rescue ''
19
+ return load_items_from_yaml_string(str)
20
+ end
21
+
22
+
23
+ # Loads the items from the given string.
24
+ #
25
+ # @param [String] str The string
26
+ # @return [Array]
27
+ def load_items_from_yaml_string(str)
28
+ items = []
29
+ yaml = YAML.load(str).to_a || [] rescue []
30
+ yaml.each {|raw|
31
+ items << {
32
+ 'url' => raw['url'],
33
+ 'name' => raw['name'],
34
+ 'description' => raw['description'],
35
+ 'tags' => raw['tags'].to_a
36
+ } if (raw.include?('url')) && (raw.include?('name'))
37
+ }
38
+ return items
39
+ end
40
+
41
+ # Save the given items to the given file in Yaml.
42
+ #
43
+ # @param [Array] items An array of items
44
+ # @param [String] file The path of the file
45
+ # @return [Integer]
46
+ def save_items_to_yaml_file(items, file)
47
+ str = "# This file is generated by geturl.\n"
48
+ str += items.to_yaml
49
+ File.write(file, str)
50
+ end
51
+
52
+ # Loads the clean items in the given Yaml string and stores it in the given file
53
+ #
54
+ # @param [Object] str The Yaml string
55
+ # @param [Object] file The path of the file
56
+ # @return [Integer]
57
+ def clean_and_save_items_to_yaml_file(str, file)
58
+ cleaned_items = load_items_from_yaml_string(str)
59
+ save_items_to_yaml_file(cleaned_items, file)
60
+ end
61
+
62
+ # Returns the local file name for the given source id.
63
+ #
64
+ # @param [Object] id The source id.
65
+ # @return [String] The path of the local source file.
66
+ def get_local_source_file(id)
67
+ return File.join(@sources_path, "#{id}.yaml")
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,57 @@
1
+ module GetUrl
2
+
3
+ # The LocalUrls manages the local stored urls.
4
+ module LocalUrls
5
+ extend self
6
+
7
+ @items = nil
8
+ @private_source_id = 'local'
9
+ @local_urls_file = File.join(Dir.home, '.geturl', 'sources', 'local.yaml')
10
+
11
+ attr_reader :private_source_id
12
+
13
+ # Loads all urls items from the local storage.
14
+ def load_urls
15
+ @items = FileManager.load_items_from_yaml_file(@local_urls_file) rescue []
16
+ end
17
+
18
+ # Stores all urls ites to the local storage.
19
+ def save_urls
20
+ FileManager.save_items_to_yaml_file(@items, @local_urls_file)
21
+ return @items.size
22
+ end
23
+
24
+ # Adds or updates the data for the given url, and stores in the local storage.
25
+ #
26
+ # @param [String] url The url.
27
+ # @param [String] name The name.
28
+ # @param [Hash] options The options, which might contains the tags and description.
29
+ def save_url(url, name, options = {})
30
+ return if (url.to_s.empty?) || (name.to_s.empty?)
31
+
32
+ load_urls
33
+ @items.delete_if {|item| item['url'] == url}
34
+ options['tags'] ||= []
35
+ options['tags'].map! {|tag| tag.strip}
36
+
37
+ @items << {
38
+ 'url' => url,
39
+ 'source' => 'local',
40
+ 'name' => name,
41
+ 'description' => options['description'],
42
+ 'tags' => options['tags']
43
+ }
44
+ save_urls
45
+ end
46
+
47
+ # Deletes the given url from the local storage.
48
+ #
49
+ # @param [String] url The url.
50
+ def delete_url(url)
51
+ load_urls if @urls.nil?
52
+ @items.delete_if {|item| item['url'] == url}
53
+ save_urls
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,123 @@
1
+ require 'json'
2
+
3
+ require_relative 'geturl-file-manager'
4
+ require_relative 'geturl-local-urls'
5
+
6
+ module GetUrl
7
+
8
+ # The Searcher deals with collecting all urls and searching through them.
9
+ module Searcher
10
+ extend self
11
+
12
+ @items = []
13
+
14
+ # Loads the urls of the source with the given id.
15
+ # @param [String] id The id
16
+ def load_source(id)
17
+ file = FileManager.get_local_source_file(id)
18
+ source_items = FileManager.load_items_from_yaml_file(file)
19
+ source_items.each {|item|
20
+ item['source'] = id
21
+ @items << item
22
+ }
23
+ end
24
+
25
+ # Loads all the given source ids. If ids is empty, all sources will be loaded.
26
+ # @param [Array] ids An array of source ids.
27
+ def load_sources(ids = nil)
28
+ ids = Sources.get_sources.keys + ['local'] if ids.nil? || ids.empty?
29
+ @items.clear
30
+ load_source(LocalUrls.private_source_id) if ids.include?('local')
31
+ Sources.get_sources.keys.each {|id|
32
+ load_source(id) if ids.include?(id)
33
+ }
34
+ end
35
+
36
+ # Returns all urls of the given ids. If ids is empty, all urls of all sources will be loaded.
37
+ #
38
+ # @param [Array] ids The array of source ids.
39
+ # @return [Array] The array of url items.
40
+ def all(ids = nil)
41
+ load_sources(ids)
42
+ return @items
43
+ end
44
+
45
+ # Searches in all urls for those that matches one or more keywords.
46
+ # Each item gets a score on relevance.
47
+ # For specific ids, the options['ids'] can be given.
48
+ #
49
+ # @param [Array] keywords The keywords
50
+ # @param [Hash] options The options
51
+ # @return [Array] The array of items.
52
+ def search(keywords = [], options = {})
53
+ load_sources(options['ids'])
54
+
55
+ # Calculate score for each item, for each keyword.
56
+ @items.each {|item|
57
+ keywords.each {|keyword|
58
+ add_score(keyword, item)
59
+ }
60
+ # Not only individual keywords, but also check on all keywords combined (when mo)
61
+ if (item.size > 1)
62
+ full = keywords.join(' ')
63
+ add_score(full, item, 2)
64
+ end
65
+ }
66
+
67
+ # Removes all items that are not relevant (score of 0).
68
+ @items.delete_if {|item| item['score'] == 0}
69
+
70
+ # Sort by score
71
+ @items.sort {|x, y|
72
+ y['score'] <=> x['score'] rescue y <=> x
73
+ } if (@items.size > 1)
74
+
75
+ # Remove all score values
76
+ @items.each {|item|
77
+ item.delete('score')
78
+ }
79
+ return @items
80
+ end
81
+
82
+
83
+ # Adds a score for the given keyword on the given item. The given factor gives extra weight to the score.
84
+ #
85
+ # @param [String] keyword The keyword.
86
+ # @param [Hash] item The item.
87
+ # @param [FixNum] factor The weighing factor.
88
+ def add_score(keyword, item, factor = 1)
89
+ score = 0
90
+ begin
91
+ score += get_field_score(item['name'], keyword, 50, 25)
92
+ score += get_field_score(item['description'], keyword, 10, 2)
93
+ item['tags'].to_a.each {|tag|
94
+ score += get_field_score(tag, keyword, 30, 5)
95
+ }
96
+ rescue
97
+ end
98
+ item['score'] ||= 0
99
+ item['score'] += score * factor
100
+ end
101
+
102
+
103
+ # Returns the relevant score:
104
+ # - If the given value and keyword are equal, the given equal_score is returned.
105
+ # - Else if the given keyword is a substring of value, the contains_score is returned.
106
+ # - Otherwise, 0 is returned.
107
+ #
108
+ # @param [String] value The value.
109
+ # @param [String] keyword The keyword.
110
+ # @param [Integer] equal_score The score when value is equal to the keyword.
111
+ # @param [Integer] contains_score The score when value contains the keyword.
112
+ # @return [Integer]
113
+ def get_field_score(value, keyword, equal_score, contains_score)
114
+ v = value.downcase
115
+ k = keyword.downcase
116
+ return equal_score if (v == k)
117
+ return contains_score if v.include?(k)
118
+ return 0
119
+ end
120
+
121
+ end
122
+
123
+ end
@@ -0,0 +1,87 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+ require 'open-uri'
4
+
5
+ require_relative 'geturl-file-manager'
6
+
7
+ module GetUrl
8
+
9
+ # The Sources represents the external, read-only sources.
10
+ module Sources
11
+ extend self
12
+
13
+ @source_file = File.join(Dir.home, '.geturl', 'sources.yaml')
14
+
15
+ # Returns all the sources as a two dimensional Hash, with the id as key, and futher information as a child Hash.
16
+ #
17
+ # @return [Hash] The sources.
18
+ def get_sources
19
+ data = File.read(@source_file)
20
+ YAML.load(data).to_h rescue {}
21
+ end
22
+
23
+ # Registers the given sources. If the id already exists, it will be overwritten without warning.
24
+ #
25
+ # @param [String] id The id of the source.
26
+ # @param [String] url The url of the source.
27
+ # @param [Hash] options The options.
28
+ # @return [Integer] The size of the sources.
29
+ def register_source(id, url, options = {})
30
+ id.downcase!
31
+ raise ArgumentError.new('The source id \'local\' is reserved.') if (id == 'local')
32
+ sources = get_sources || {}
33
+ sources[id] = {
34
+ 'url' => url,
35
+ 'cert' => options['cert'],
36
+ 'key' => options['key'],
37
+ }
38
+ File.write(@source_file, sources.to_yaml)
39
+ return sources.size
40
+ end
41
+
42
+ # Unregisters the source with the given id. It also removes the locally cached file.
43
+ #
44
+ # @param [String] id The id of the source.
45
+ # @return [Integer] The size of the sources.
46
+ def unregister_source(id)
47
+ sources = get_sources || {}
48
+ sources.delete(id)
49
+ File.write(@source_file, sources.to_yaml)
50
+ File.unlink(FileManager.get_local_source_file(id)) rescue nil
51
+ return sources.size
52
+ end
53
+
54
+ # Reloads the yaml file from the source, and caches it locally.
55
+ # If the id is nil, all sources will be reloaded.
56
+ #
57
+ # @param [Object] id The id of the source to reload.
58
+ # @param [Object] options The options.
59
+ def reload(id = nil, options = {})
60
+ if id.nil?
61
+ get_sources.keys.each {|i|
62
+ reload(i, options) unless i.nil?
63
+ }
64
+ else
65
+ begin
66
+ source_data = get_sources[id]
67
+ print "Loading #{id} ...\r" if (options[:verbose])
68
+
69
+ if source_data.include?('cert') && source_data.include?('key')
70
+ curl_command = "curl -s #{source_data['url']} -E #{source_data['cert']} --key #{source_data['key']}"
71
+ data = `#{curl_command}`
72
+ else
73
+ data = open(source_data['url']).read
74
+ end
75
+ raise Exception.new('File is empty') if data.empty?
76
+ file = FileManager.get_local_source_file(id)
77
+ FileManager.clean_and_save_items_to_yaml_file(data, file)
78
+ puts "Loaded #{id}" + "".ljust(5) if (options[:verbose])
79
+ rescue Exception => e
80
+ puts "Failed to load #{id}: #{e.full_message}" if (options[:verbose])
81
+ end
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geturl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Taco Jan Osinga
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mustache
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: cli-parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.3
41
+ description: A simple command-line bookmarking tool, with support of external, read-only
42
+ sources.
43
+ email: info@osingasoftware.nl
44
+ executables:
45
+ - geturl
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - bin/geturl
51
+ - lib/geturl.rb
52
+ - lib/geturl/geturl-file-manager.rb
53
+ - lib/geturl/geturl-local-urls.rb
54
+ - lib/geturl/geturl-searcher.rb
55
+ - lib/geturl/geturl-sources.rb
56
+ homepage: https://gitlab.com/osisoft-gems/geturl
57
+ licenses:
58
+ - MIT
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.7.7
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: GetURL
80
+ test_files: []