geturl 0.1.0

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.
@@ -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: []