hackercli 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b99813316a49a7f734835aaf0d913f0e7f318fe5
4
- data.tar.gz: 7ef92b17ffc21396553663a3ec8471f0220ef1a6
3
+ metadata.gz: 75c4a562ea1421cb25e6e931f248fe548dca1455
4
+ data.tar.gz: 5eae9a7fae225f5466ce8c3e821cc3a438d72fad
5
5
  SHA512:
6
- metadata.gz: 9317754f66a17e8e6158aa0f61adaab78ed833959fd042f362f8964eb20ffb3c86eb4b171685a12657d7bd982c04b977ee099b3f48f779f1c7bbf54566ac70db
7
- data.tar.gz: cf74fa1d7c328e73bd433f1d4cae0857dab48270368899ebda3f9adf9b389861ac7d35af257472b555958cac2899b2e9cbb8afd38ddf5a500c19dc1d8d399096
6
+ metadata.gz: ecaef63610e4e8337bd71b3073c71f979c50b00e2629c3528125844ca2c3bf4b0fc5dd7744b63855ec4315e8ac937f831cb1b48764dcd5e96289eca07a080648
7
+ data.tar.gz: 0016e69972886d8ec5ed937019b48e326b2f56cfa3105bb31014ceab1c4e9b7d3538b5ef39e7402e90f5d843daabaab259b59d2329520f406e3589ab27afe837
data/README.md CHANGED
@@ -6,51 +6,94 @@ provided. May be used as a filter for other commands.
6
6
  This is a single file and so may be placed anywhere in the path. Also, one may include it in a project
7
7
  and get the array of hashes for each article and use as per requirements.
8
8
 
9
- This is NOT dependent on any other gem as it uses and parses the simple RSS feed using +String.scan+ only.
9
+ This is NOT dependent on any other gem as it uses and parses the simple RSS feed using `String.scan` only.
10
10
 
11
11
  The feed used for Hacker News is:
12
12
 
13
- https://news.ycombinator.com/bigrss
13
+ https://news.ycombinator.com/bigrss
14
14
 
15
15
  The feed used for Reddit News is (replace ruby with any other subreddit):
16
16
 
17
17
  http://www.reddit.com/r/ruby/.rss
18
18
 
19
19
  Please click these links and check if they are working if there is any problem.
20
+ To see current options:
20
21
 
21
- hackercli.rb --help
22
+ hackercli.rb --help
22
23
 
23
24
 
24
25
  ## Installation
25
26
 
26
- Or install it yourself as:
27
-
28
27
  $ gem install hackercli
29
28
 
30
29
  ## Usage
31
30
 
32
31
  hackercli.rb --help
33
32
 
34
- To view hacker news titles and info.
33
+ To view hacker news titles and urls
35
34
 
36
35
  hackercli.rb
36
+
37
+ hackercli.rb slashdot
38
+
39
+ Pipe to other commands (default separatar is a TAB)
40
+
41
+ hackercli.rb | cut -f1,2 | nl | sort -n -r
37
42
 
38
43
 
39
44
  To view only titles:
40
45
 
41
46
  hackercli.rb -t
42
47
 
48
+
49
+ To view description column also (this is normally not printed since it can be long):
50
+
51
+ hackercli.rb -v
52
+
43
53
  To view reddit ruby:
44
54
 
45
- hackercli.rb -s ruby
55
+ hackercli.rb ruby
56
+
57
+ hackercli.rb <subreddit>
46
58
 
47
59
  Present the URL of some other RSS feed:
48
60
 
49
- hackercli.rb -u https://someurl.com/.rss
61
+ hackercli.rb -u http://feeds.arstechnica.com/arstechnica/index
62
+
63
+ hackercli.rb -u http://feeds.boingboing.net/boingboing/iBag
64
+
65
+ Save output to a YML file:
66
+
67
+ hackercli.rb -y ruby.yml ruby
68
+
69
+ Please try the `--help` option to see current feeds supported, and latest options.
50
70
 
51
71
  NOTE: this has been tested only with Hackernews bigrss and reddit news' RSS, so I cannot gaurantee
52
72
  how it will behave with others. Some tags such as item, title and link are expected to be present.
53
73
 
74
+ ## Changes
75
+
76
+ ### 0.0.2
77
+
78
+ - Earlier the `-s` option was used to specify subforum. Now, it is not an option. subforum is passed
79
+ as an argument.
80
+
81
+ - Save to YML. I would prefer to use this in a client program rather than use tabbed output.
82
+ The YML file contains descriptive field which some callers may require.
83
+ The tabbed output does not contain description.
84
+
85
+ - Several other RSS feeds have been checked out such as **slashdot** and **arstechnica**.
86
+ You may pass "*ars:open-source*" or "*ars:software*" as an argument.
87
+ Check `--help` for latest options and features.
88
+
89
+ ## Upcoming
90
+
91
+ I've added a curses frontend to the generated yml files in bin named `hackman`, which depends on the
92
+ curses library `canis`. I am making it key-binding compliant with `corvus`, so one may switch from one
93
+ to the other without problems.
94
+
95
+ Should store the files in given location, read up forum list and other things from an info file.
96
+ (Perhaps share with corvusinfo).
54
97
 
55
98
  ## Testing and debugging
56
99
 
@@ -62,11 +105,17 @@ the program.
62
105
 
63
106
  ## See Also:
64
107
 
108
+ ### rubygems
109
+
110
+ https://rubygems.org/gems/hackercli
111
+
65
112
  ### hacker-curse
66
113
 
67
- This uses nokogiri to parse the actual Hackernews (or reddit) page, and can print on the CLI as well as
114
+ hacker-curse uses nokogiri to parse the actual Hackernews (or reddit) page, and can print on the CLI as well as
68
115
  be used as a library for another application. hacker-curse also provides a curses interface for viewing titles
69
- and comments, and launching the article or comments page in the GUI browser.
116
+ and comments, and launching the article or comments page in the GUI browser. Currently, the frontend is being
117
+ developed. The library is ready and is on github. It contains a non-curses client named `corvus` which you should
118
+ try.
70
119
 
71
120
  ## Contributing
72
121
 
@@ -75,3 +124,6 @@ and comments, and launching the article or comments page in the GUI browser.
75
124
  3. Commit your changes (`git commit -am 'Add some feature'`)
76
125
  4. Push to the branch (`git push origin my-new-feature`)
77
126
  5. Create a new Pull Request
127
+
128
+ Please let me know if you find this useful, or write a front-end for this. I'd like to know how to make this more
129
+ useful, and include a link to your repo.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # ----------------------------------------------------------------------------- #
3
- # File: bigrss.rb
3
+ # File: hackercli.rb
4
4
  # Description: reads Hacker News bigrss feed and prints out
5
5
  # Also works with reddit's rss feed
6
6
  #
@@ -8,15 +8,18 @@
8
8
  # titles and connecting to the page. HN's rss does not provide any info
9
9
  # such as points/age etc. Reddit provides a little more but has to be parsed.
10
10
  #
11
- # Currently saves downloaded file as "last.rss" so you may rerun queries on it
11
+ # Currently saves downloaded file as "<forum>.rss" so you may rerun queries on it
12
12
  # using the "-u" flag.
13
13
  #
14
+ # Caveats: fails if newlines in rss feed as in case of arstechnical
15
+ # We need to join the lines first so that scan can work
16
+ #
14
17
  # Author: j kepler http://github.com/mare-imbrium/canis/
15
18
  # Date: 2014-07-20 - 11:37
16
19
  # License: MIT
17
20
  # Last update: 2014-07-21 17:50
18
21
  # ----------------------------------------------------------------------------- #
19
- # bigrss.rb Copyright (C) 2012-2014 j kepler
22
+ # hackercli.rb Copyright (C) 2012-2014 j kepler
20
23
 
21
24
  require 'open-uri'
22
25
  require 'cgi'
@@ -31,16 +34,28 @@ class Bigrss
31
34
  #instance_eval &block if block_given?
32
35
  end
33
36
 
37
+ # # returns a hash containing :articles array, and one or two outer fields such as page_url and time
38
+ # returns an array containing a hash for each article
39
+ # The hash contains :title, :url, :comments_url and in some case :pubdata and comments_count
34
40
  def run
41
+ page = {}
42
+
35
43
  resp = []
36
44
  filename = @options[:url]
45
+ page[:page_url] = filename
46
+ now = Time.now
47
+ page[:create_time_seconds] = now.to_i
48
+ page[:create_time] = now.to_s
49
+ page[:articles] = resp
37
50
  f = open(filename)
38
- content = f.read
51
+ outfile = @options[:subreddit] || "last"
52
+ # ars technical sends in new lines
53
+ content = f.read.delete("\n")
39
54
  content.gsub!('&#x2F;',"/")
40
55
  content.gsub!('&#x27;',"'")
41
56
  content.gsub!('&#x34;','"')
42
57
  content = CGI.unescapeHTML(content)
43
- File.open("last.rss","w") {|ff| ff.write(content) }
58
+ File.open("#{outfile}.rss","w") {|ff| ff.write(content) }
44
59
  items = content.scan(/<item>(.*?)<\/item>/)
45
60
  items.each_with_index do |e,i|
46
61
  e = e.first
@@ -48,6 +63,8 @@ class Bigrss
48
63
  title = e.scan(/<title>(.*?)<\/title/).first.first
49
64
  h[:title] = title
50
65
  url = e.scan(/<link>(.*?)<\/link/).first.first
66
+ # in the case of hacker news this is the article url
67
+ # In the case of reddit, this is the comment url along with comment url
51
68
  h[:url] = url
52
69
  comment_url = ""
53
70
  # HN has comments links in a tag
@@ -73,7 +90,7 @@ class Bigrss
73
90
  end
74
91
  #puts " #{title}#{sep}#{url}#{sep}#{comment_url}"
75
92
  end
76
- return resp unless block_given?
93
+ return page unless block_given?
77
94
  end
78
95
  def extract_part e, tag, hash
79
96
  if e.index("<#{tag}>")
@@ -103,6 +120,12 @@ end
103
120
 
104
121
 
105
122
 
123
+ def to_yml outfile, arr
124
+ require 'yaml'
125
+ File.open(outfile, 'w' ) do |f|
126
+ f << YAML::dump(arr)
127
+ end
128
+ end
106
129
 
107
130
 
108
131
 
@@ -111,11 +134,40 @@ end
111
134
  if true
112
135
  begin
113
136
  url = nil
137
+ ymlfile = nil
114
138
  # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
115
139
  require 'optparse'
116
140
  options = {}
141
+ appname = File.basename $0
117
142
  OptionParser.new do |opts|
118
- opts.banner = "Usage: #{$0} [options]"
143
+ opts.banner =
144
+ %Q{
145
+ Usage: #{$0} [options]
146
+ Examples:
147
+ Display Hacker News titles and urls:
148
+ #{appname}
149
+ #{appname} hacker
150
+ Display subreddits from reddit.com:
151
+ #{appname} programming
152
+ #{appname} ruby
153
+ Display info from other sites:
154
+ #{appname} ars # index from arstechnica
155
+ #{appname} ars:software # software from arstechnica
156
+ #{appname} ars:open-source # open-source from arstechnica
157
+ #{appname} slashdot
158
+ Display info from a saved rss file:
159
+ #{appname} -u ruby.rss
160
+ Save info from reddit ruby to a YML file:
161
+ #{appname} -y ruby.yml ruby
162
+ Display info from another url:
163
+ #{appname} -u http://feeds.boingboing.net/boingboing/iBag
164
+ #{appname} -u http://www.lifehacker.co.in/rss_tag_section_feeds.cms?query=productivity
165
+ #{appname} -u http://rss.slashdot.org/Slashdot/slashdot
166
+ #{appname} -u http://feeds.arstechnica.com/arstechnica/open-source
167
+ #{appname} -u http://feeds.arstechnica.com/arstechnica/software
168
+
169
+
170
+ }
119
171
 
120
172
  opts.on("-v", "--[no-]verbose", "Print description also") do |v|
121
173
  options[:verbose] = v
@@ -129,10 +181,13 @@ if true
129
181
  opts.on("-d SEP", String,"--delimiter", "Delimit columns with SEP") do |v|
130
182
  options[:delimiter] = v
131
183
  end
132
- opts.on("-s SUBREDDIT", String,"--subreddit", "Get articles from subreddit named SUBREDDIT") do |v|
133
- options[:subreddit] = v
134
- url = "http://www.reddit.com/r/#{v}/.rss"
184
+ opts.on("-y yml path", String,"--yml-path", "save as YML file") do |v|
185
+ ymlfile = v
135
186
  end
187
+ #opts.on("-s SUBREDDIT", String,"--subreddit", "Get articles from subreddit named SUBREDDIT") do |v|
188
+ #options[:subreddit] = v
189
+ #url = "http://www.reddit.com/r/#{v}/.rss"
190
+ #end
136
191
  opts.on("-u URL", String,"--url", "Get articles from URL/file") do |v|
137
192
  url = v
138
193
  end
@@ -141,11 +196,43 @@ if true
141
196
  #p options
142
197
  #p ARGV
143
198
 
144
- #filename=ARGV[0];
145
- url ||= "https://news.ycombinator.com/bigrss"
199
+ v = ARGV[0];
200
+ if v
201
+ case v
202
+ when "news", "hacker", "hn"
203
+ url = "https://news.ycombinator.com/bigrss"
204
+ options[:subreddit] = "hacker"
205
+ when "slashdot"
206
+ url = "http://rss.slashdot.org/Slashdot/slashdot"
207
+ options[:subreddit] = "slashdot"
208
+ when "ars"
209
+ url = "http://feeds.arstechnica.com/arstechnica/index"
210
+ options[:subreddit] = "arstechnica"
211
+ else
212
+ if v.index("ars:") == 0
213
+ subf = v.split(":")
214
+ url = "http://feeds.arstechnica.com/arstechnica/#{subf.last}"
215
+ options[:subreddit] = subf.join("_")
216
+ else
217
+ url = "http://www.reddit.com/r/#{v}/.rss"
218
+ options[:subreddit] = v
219
+ end
220
+ end
221
+ end
222
+ unless url
223
+ url ||= "https://news.ycombinator.com/bigrss"
224
+ options[:subreddit] = "hacker"
225
+ end
226
+ #$stderr.puts "url is: #{url} "
227
+
146
228
  options[:url] = url
147
229
  klass = Bigrss.new options
148
- arr = klass.run
230
+ page = klass.run
231
+ if ymlfile
232
+ to_yml ymlfile, page
233
+ exit
234
+ end
235
+ arr = page[:articles]
149
236
  titles_only = options[:titles]
150
237
  sep = options[:delimiter] || "\t"
151
238
  limit = options[:number] || arr.count
@@ -0,0 +1,531 @@
1
+ #!/usr/bin/env ruby
2
+ # ----------------------------------------------------------------------------- #
3
+ # File: hackman.rb
4
+ # Description: curses frontend for rss feeds generated by hackercli
5
+ # Author: j kepler http://github.com/mare-imbrium/canis/
6
+ # Date: 2014-08-09 - 10:12
7
+ # License: MIT
8
+ # Last update: 2014-08-11 21:30
9
+ # ----------------------------------------------------------------------------- #
10
+ # hackman.rb Copyright (C) 2012-2014 j kepler
11
+ # encoding: utf-8
12
+ require 'canis/core/util/app'
13
+ require 'canis/core/util/rcommandwindow'
14
+ require 'fileutils'
15
+ require 'pathname'
16
+ require 'canis/core/include/defaultfilerenderer'
17
+ require 'canis/core/include/appmethods'
18
+
19
+ # TODO :
20
+ # - add to forum list, remove, save
21
+ # - specify gui browser and text browser, and on commandline use same keys as corvus
22
+ # - create a class and put stuff in there, these methods are going into global, and can conflict
23
+ # - we should have same mechanism for key bindings as corvus, something that can even be loaded?
24
+ #
25
+ # - Use ' as bookmark etc just as in cetus and corvus, keep to same keys
26
+ # - multibuffers so user can backspace and do M-n etc
27
+ #
28
+ VERSION="0.0.2"
29
+ COLOR_SCHEMES=[
30
+ [20,19,17, 18, :white], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status, 4 fg color body
31
+ [17,19,18, 20, :white], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
32
+ [236,236,232, 234,:white], # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
33
+ [236,236,244, 234, :black] # 0 band in header, 1 - menu bgcolor. 2 - bgcolor of main screen, 3 - status
34
+ ]
35
+ $color_scheme = COLOR_SCHEMES[0]
36
+ $toggle_titles_only = false
37
+ $fg = :white
38
+ $forumlist = %w{ hacker ruby programming scifi science haskell java scala cpp c_programming d_language golang vim emacs unix linux bash zsh commandline vimplugins python ars slashdot }
39
+ def choose_forum
40
+ # scrollable filterable list
41
+ str = display_list $forumlist, :title => "Select a forum"
42
+ return unless str
43
+ return if str == ""
44
+ $current_forum = str
45
+ forum = str
46
+ get_data forum if forum
47
+ end
48
+ def next_forum
49
+ index = $forumlist.index($current_forum)
50
+ index = index >= $forumlist.count - 1 ? 0 : index + 1
51
+ get_data $forumlist[index]
52
+ end
53
+ def prev_forum
54
+ index = $forumlist.index($current_forum)
55
+ index = index == 0? $forumlist.count - 1 : index - 1
56
+ get_data $forumlist[index]
57
+ end
58
+ # if components have some commands, can we find a way of passing the command to them
59
+ # method_missing gave a stack overflow.
60
+ def execute_this(meth, *args)
61
+ alert " #{meth} not found ! "
62
+ $log.debug "app email got #{meth} " if $log.debug?
63
+ cc = @form.get_current_field
64
+ [cc].each do |c|
65
+ if c.respond_to?(meth, true)
66
+ c.send(meth, *args)
67
+ return true
68
+ end
69
+ end
70
+ false
71
+ end
72
+ def open_url url
73
+ shell_out "elinks #{url}"
74
+ #Window.refresh_all
75
+ end
76
+
77
+ ##
78
+ # Menu creator which displays a menu and executes methods based on keys.
79
+ # In some cases, we call this and then do a case statement on either key or binding.
80
+ # @param String title
81
+ # @param hash of keys and methods to call
82
+ # @return key pressed, and binding (if found, and responded)
83
+ #
84
+ def menu title, hash, config={}, &block
85
+ raise ArgumentError, "Nil hash received by menu" unless hash
86
+ list = []
87
+ hash.each_pair { |k, v| list << " #[fg=yellow, bold] #{k} #[/end] #[fg=green] #{v} #[/end]" }
88
+ # s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
89
+ config[:title] = title
90
+ config[:width] = hash.values.max_by(&:length).length + 13
91
+ ch = padpopup list, config, &block
92
+ return unless ch
93
+ if ch.size > 1
94
+ # could be a string due to pressing enter
95
+ # but what if we format into multiple columns
96
+ ch = ch.strip[0]
97
+ end
98
+
99
+ binding = hash[ch]
100
+ binding = hash[ch.to_sym] unless binding
101
+ if binding
102
+ if respond_to?(binding, true)
103
+ send(binding)
104
+ end
105
+ end
106
+ return ch, binding
107
+ end
108
+ # pops up a list, taking a single key and returning if it is in range of 33 and 126
109
+ # Called by menu, print_help, show_marks etc
110
+ # You may pass valid chars or ints so it only returns on pressing those.
111
+ #
112
+ # @param Array of lines to print which may be formatted using :tmux format
113
+ # @return character pressed (ch.chr)
114
+ # @return nil if escape or C-q pressed
115
+ #
116
+ def padpopup list, config={}, &block
117
+ max_visible_items = config[:max_visible_items]
118
+ row = config[:row] || 5
119
+ col = config[:col] || 5
120
+ # format options are :ansi :tmux :none
121
+ fmt = config[:format] || :tmux
122
+ config.delete :format
123
+ relative_to = config[:relative_to]
124
+ if relative_to
125
+ layout = relative_to.form.window.layout
126
+ row += layout[:top]
127
+ col += layout[:left]
128
+ end
129
+ config.delete :relative_to
130
+ # still has the formatting in the string so length is wrong.
131
+ #longest = list.max_by(&:length)
132
+ width = config[:width] || 60
133
+ if config[:title]
134
+ width = config[:title].size + 2 if width < config[:title].size
135
+ end
136
+ height = config[:height]
137
+ height ||= [max_visible_items || 25, list.length+2].min
138
+ #layout(1+height, width+4, row, col)
139
+ layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
140
+ window = Canis::Window.new(layout)
141
+ form = Canis::Form.new window
142
+
143
+ ## added 2013-03-13 - 18:07 so caller can be more specific on what is to be returned
144
+ valid_keys_int = config.delete :valid_keys_int
145
+ valid_keys_char = config.delete :valid_keys_char
146
+
147
+ listconfig = config[:listconfig] || {}
148
+ #listconfig[:list] = list
149
+ listconfig[:width] = width
150
+ listconfig[:height] = height
151
+ listconfig[:bgcolor] = $color_scheme[1]
152
+ #listconfig[:selection_mode] ||= :single
153
+ listconfig.merge!(config)
154
+ listconfig.delete(:row);
155
+ listconfig.delete(:col);
156
+ # trying to pass populists block to listbox
157
+ lb = Canis::TextPad.new form, listconfig, &block
158
+ if fmt == :none
159
+ lb.text(list)
160
+ else
161
+ lb.text(list, fmt)
162
+ end
163
+ #
164
+ #window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
165
+ form.repaint
166
+ Ncurses::Panel.update_panels
167
+ if valid_keys_int.nil? && valid_keys_char.nil?
168
+ # changed 32 to 33 so space can scroll list
169
+ valid_keys_int = (33..126)
170
+ end
171
+
172
+ begin
173
+ while((ch = window.getchar()) != 999 )
174
+
175
+ # if a char range or array has been sent, check if the key is in it and send back
176
+ # else just stay here
177
+ if valid_keys_char
178
+ if ch > 32 && ch < 127
179
+ chr = ch.chr
180
+ return chr if valid_keys_char.include? chr
181
+ end
182
+ end
183
+
184
+ # if the user specified an array or range of ints check against that
185
+ # therwise use the range of 33 .. 126
186
+ return ch.chr if valid_keys_int.include? ch
187
+
188
+ case ch
189
+ when ?\C-q.getbyte(0)
190
+ break
191
+ else
192
+ if ch == 13 || ch == 10
193
+ s = lb.current_value.to_s # .strip #if lb.selection_mode != :multiple
194
+ return s
195
+ end
196
+ # close if escape or double escape
197
+ if ch == 27 || ch == 2727
198
+ return nil
199
+ end
200
+ lb.handle_key ch
201
+ form.repaint
202
+ end
203
+ end
204
+ ensure
205
+ window.destroy
206
+ end
207
+ return nil
208
+ end
209
+ # main options, invokable on backtick.
210
+ # TODO add selection of browser
211
+ def main_menu
212
+ h = {
213
+ :f => :choose_forum,
214
+ :c => :color_scheme_select,
215
+ :s => :sort_menu,
216
+ :F => :filter_menu,
217
+ :x => :extras
218
+ }
219
+ ch, binding = menu "Main Menu", h
220
+ #alert "Menu got #{ch}, #{binding}" if ch
221
+ end
222
+ def toggle_menu
223
+ h = {
224
+ "t" => :toggle_titles_only,
225
+ :x => :extras
226
+ }
227
+ ch, binding = menu "Main Menu", h
228
+ #alert "Menu got #{ch}, #{binding}" if ch
229
+ end
230
+ def color_scheme_select ch=nil
231
+ unless ch
232
+ h = {
233
+ "0" => 'dark blue body',
234
+ "1" => 'medium blue body',
235
+ "2" => 'black body',
236
+ "3" => 'grey body',
237
+ "b" => 'change body color',
238
+ "f" => 'change body fg color',
239
+ "c" => 'cycle body color'
240
+ }
241
+ ch, binding = menu "Color Menu", h
242
+ end
243
+ case ch
244
+ when "1", "2", "0", "3"
245
+ $color_scheme = COLOR_SCHEMES[ch.to_i] || COLOR_SCHEMES.first
246
+ $fg = $color_scheme[4]
247
+ when "b"
248
+ n = get_string "Enter a number for background color (0..255): "
249
+ n = n.to_i
250
+ $color_scheme[2] = n
251
+ when "4", "f"
252
+ n = get_string "Enter a number for fg color (0..255) : "
253
+ $fg = n.to_i
254
+ when "c"
255
+ # increment bg color
256
+ n = $color_scheme[2]
257
+ n += 1
258
+ n = 0 if n > 255
259
+ $color_scheme[2] = n
260
+ when "C"
261
+ # decrement bg color
262
+ n = $color_scheme[2]
263
+ n -= 1
264
+ n = 255 if n < 0
265
+ $color_scheme[2] = n
266
+ end
267
+
268
+ h = @form.by_name["header"]
269
+ tv = @form.by_name["tv"]
270
+ sl = @form.by_name["sl"]
271
+ tv.bgcolor = $color_scheme[2]
272
+ #tv.color = 255
273
+ tv.color = $fg
274
+ sl.color = $color_scheme[3]
275
+ h.bgcolor = $color_scheme[0]
276
+ message "bgcolor is #{$color_scheme[2]}. :: #{$color_scheme.join(",")}, CP:#{tv.color_pair}=#{tv.color} / #{tv.bgcolor} "
277
+ refresh
278
+ end
279
+ def refresh
280
+ show $current_file
281
+ end
282
+
283
+ def toggle_titles_only
284
+ $toggle_titles_only = !$toggle_titles_only
285
+ show $current_file
286
+ end
287
+ App.new do
288
+ @startdir ||= File.expand_path("..")
289
+ @hash = nil
290
+ def get_item_for_line line
291
+ index = (line - @hash[:first]) / @hash[:diff]
292
+ @hash[:articles][index]
293
+ end
294
+ def title_right text
295
+ w = @form.by_name["header"]
296
+ w.text_right text
297
+ end
298
+ def title text
299
+ w = @form.by_name["header"]
300
+ w.text_center text
301
+ end
302
+ def color_line(fg,bg,attr,text)
303
+ a = "#["
304
+ a = []
305
+ a << "fg=#{fg}" if fg
306
+ a << "bg=#{bg}" if bg
307
+ a << "#{attr}" if attr
308
+ str = "#[" + a.join(",") + "]#{text}#[end]"
309
+ end
310
+ def goto_article n=$multiplier
311
+ i = ((n-1) * @hash[:diff]) + @hash[:first]
312
+ w = @form.by_name["tv"]
313
+ w.goto_line i
314
+ end
315
+
316
+ def OLDshow file
317
+ w = @form.by_name["tv"]
318
+ if File.directory? file
319
+ lines = Dir.entries(file)
320
+ w.text lines
321
+ w.title "[ #{file} ]"
322
+ elsif File.exists? file
323
+ lines = File.open(file,'r').readlines
324
+ w.text lines
325
+ w.title "[ #{file} ]"
326
+ end
327
+ end
328
+ # display the given yml file.
329
+ # Converts the yml object to an array for textpad
330
+ def display_yml file
331
+ w = @form.by_name["tv"]
332
+
333
+ obj = YAML::load( File.open( file ) )
334
+ lines = Array.new
335
+ url = obj[:page_url]
336
+ host = nil
337
+ if url.index("reddit")
338
+ host = "reddit"
339
+ elsif url.index("ycombinator")
340
+ host = "hacker"
341
+ elsif url.index("ars")
342
+ host = "ars"
343
+ elsif url.index("slashdot")
344
+ host = "slashdot"
345
+ else
346
+ alert "Host not known: #{url} "
347
+ end
348
+ articles = obj[:articles]
349
+ count = articles.count
350
+ #lines << color_line(:red,COLOR_SCHEME[1],nil,"#{file} #{obj[:page_url]} | #{count} articles | fetched #{obj[:create_time]}")
351
+ #lines << ("-" * lines.last.size )
352
+ @hash = Hash.new
353
+ @hash[:first] = lines.size
354
+ @hash[:articles] = articles
355
+
356
+ articles.each_with_index do |a, i|
357
+ bg = i
358
+ bg = 0 if i > 255
359
+ line = "%3s %s " % [i+1 , a[:title] ]
360
+ #lines << color_line($fg, bg, nil, line)
361
+ lines << line
362
+ if !$toggle_titles_only
363
+ url = a[:article_url] || a[:url]
364
+ l = " %s | %s" % [url, a[:comments_url] ]
365
+ l = "#[fg=green, underline]" + l + "#[end]"
366
+ lines << l
367
+ detail = []
368
+ if a.key? :comment_count
369
+ detail << a[:comment_count]
370
+ end
371
+ if a.key? :pubdate
372
+ detail << a[:pubdate]
373
+ end
374
+ unless detail.empty?
375
+ l = "#[fg=green]" + " " + detail.join(" | ") + "#[end]"
376
+ lines << l
377
+ end
378
+ end
379
+ @hash[:diff] ||= lines.size - @hash[:first]
380
+ end
381
+ w.text(lines, :content_type => :tmux)
382
+ w.title "[ #{file} ]"
383
+
384
+ i = @hash[:first] || 1
385
+ w.goto_line i
386
+ $current_file = file
387
+ $current_forum = file.sub(File.extname(file),"")
388
+ title "#{$current_forum} (#{count} articles) "
389
+ title_right obj[:create_time]
390
+ end
391
+ alias :show :display_yml
392
+ def get_data forum
393
+ file = forum + ".yml"
394
+ if File.exists? file and fresh(file)
395
+ else
396
+ progress_dialog :color_pair => $reversecolor do |sw|
397
+ #sw.printstring 0,1, "Fetching #{forum} ..."
398
+ sw.print "Fetching #{forum} ..."
399
+ system("hackercli.rb -y #{forum}.yml #{forum}")
400
+ end
401
+ end
402
+ display_yml file
403
+ end
404
+ # return true if younger than one hour
405
+ def fresh file
406
+ f = File.stat(file)
407
+ now = Time.now
408
+ return (( now - f.mtime) < 7200)
409
+ end
410
+ def show_links art
411
+ links = {}
412
+ keys = %w{a b c d e f}
413
+ i = 0
414
+ art.each_pair do |k, p|
415
+ if p.index("http") == 0
416
+ links[keys[i]] = p
417
+ i += 1
418
+ end
419
+ end
420
+ ch, binding = menu "Links Menu", links
421
+ #alert "is #{index}: #{art[:title]} #{ch}:#{binding} "
422
+ if binding
423
+ open_url binding
424
+ end
425
+ end
426
+ ht = 24
427
+ borderattrib = :reverse
428
+ @header = app_header "hackman #{VERSION}", :text_center => "RSS Reader", :name => "header",
429
+ :text_right =>"Press =", :color => :white, :bgcolor => $color_scheme[0]
430
+ message "Press F10 (or qq) to exit, F1 Help, ` for Menu "
431
+
432
+
433
+
434
+ # commands that can be mapped to or executed using M-x
435
+ # however, commands of components aren't yet accessible.
436
+ def get_commands
437
+ %w{ choose_forum next_forum prev_forum }
438
+ end
439
+ def help_text
440
+ <<-eos
441
+ rCommandLine HELP
442
+
443
+ These are some features for either getting filenames from user
444
+ at the bottom of the window like vim and others do, or filtering
445
+ from a list (like ControlP plugin). Or seeing a file at bottom
446
+ of screen for a quick preview.
447
+
448
+ : - Command mode
449
+ F1 - Help
450
+ F10 - Quit application
451
+ qq - Quit application
452
+ = - file selection (interface like Ctrl-P, very minimal)
453
+
454
+ Some commands for using bottom of screen as vim and emacs do.
455
+ These may be selected by pressing ':'
456
+
457
+ testchoosedir - filter directory list as you type
458
+ '>' to step into a dir, '<' to go up.
459
+ testchoosefile - filter file list as you type
460
+ ENTER to select, C-c or Esc-Esc to quit
461
+ testdir - vim style, tabbing completes matching files
462
+ testnumberedmenu - use menu indexes to select options
463
+ choose_forum - display a list at bottom of screen
464
+ Press <ENTER> to select, arrow keys to traverse,
465
+ and characters to filter list.
466
+ testdisplaytext - display text at bottom (current file contents)
467
+ Press <ENTER> when done.
468
+
469
+ The file/dir selection options are very minimally functional. Improvements
470
+ and thorough testing are required. I've only tested them out gingerly.
471
+
472
+ testchoosedir and file were earlier like Emacs/memacs with TAB completion
473
+ but have now moved to the much faster and friendlier ControlP plugin like
474
+ 'filter as you type' format.
475
+
476
+ -----------------------------------------------------------------------
477
+ :n or Alt-n for general help.
478
+ eos
479
+ end
480
+
481
+ #install_help_text help_text
482
+
483
+ def app_menu
484
+ @curdir ||= Dir.pwd
485
+ Dir.chdir(@curdir) if Dir.pwd != @curdir
486
+ require 'canis/core/util/promptmenu'
487
+ menu = PromptMenu.new self do
488
+ item :f, :choose_forum
489
+ item :t, :testdisplay_text
490
+ end
491
+ menu.display_new :title => "Menu"
492
+ end
493
+ # BINDING SECTION
494
+ @form.bind_key(?:, "App Menu") { app_menu; }
495
+ @form.bind_key(?`, "Main Menu") { main_menu; }
496
+ @form.bind_key(FFI::NCurses::KEY_F2, "Main Menu") { choose_forum; }
497
+ @form.bind_key(FFI::NCurses::KEY_F3, "Cycle bgcolor") { color_scheme_select "c"; }
498
+ @form.bind_key(FFI::NCurses::KEY_F4, "Cycle bgcolor") { color_scheme_select "C"; }
499
+ @form.bind_key($kh_int["S-F3"], "Cycle bgcolor") { color_scheme_select "C"; }
500
+ @form.bind_key(?=, "Toggle Menu") {
501
+ toggle_menu;
502
+ }
503
+ @form.bind_key(?<, "Previous Forum") { prev_forum; }
504
+ @form.bind_key(?>, "Next Forum") { next_forum; }
505
+
506
+ stack :margin_top => 1, :margin_left => 0, :width => :expand , :height => FFI::NCurses.LINES-2 do
507
+ tv = textpad :height_pc => 100, :width_pc => 100, :name => "tv", :suppress_borders => true,
508
+ :bgcolor => $color_scheme[2], :color => 255, :attr => NORMAL
509
+ #tv.renderer ruby_renderer
510
+ tv.bind(:PRESS) {|ev|
511
+ index = ev.current_index
512
+ art = get_item_for_line index
513
+ show_links art
514
+ }
515
+ tv.bind_key(?z) { goto_article }
516
+ tv.bind_key(?o) {
517
+ # if multiplier is 0, use current line
518
+ art = @hash[:articles][$multiplier - 1]
519
+ if $multiplier == 0
520
+ index = tv.current_index
521
+ art = get_item_for_line index
522
+ end
523
+ show_links art
524
+ }
525
+ tv.text_patterns[:articles] = Regexp.new(/^ *\d+ /)
526
+ tv.bind_key(KEY_TAB, "goto article") { tv.next_regex(:articles) }
527
+ end # stack
528
+
529
+ sl = status_line :row => Ncurses.LINES-1, :bgcolor => :yellow, :color => $color_scheme[3]
530
+ choose_forum
531
+ end # app
@@ -1,3 +1,3 @@
1
1
  module Hackercli
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hackercli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - kepler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-21 00:00:00.000000000 Z
11
+ date: 2014-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,6 +44,7 @@ email:
44
44
  - githubkepler.50s@gishpuppy.com
45
45
  executables:
46
46
  - hackercli.rb
47
+ - hackman.rb
47
48
  extensions: []
48
49
  extra_rdoc_files: []
49
50
  files:
@@ -53,6 +54,7 @@ files:
53
54
  - README.md
54
55
  - Rakefile
55
56
  - bin/hackercli.rb
57
+ - bin/hackman.rb
56
58
  - hackercli.gemspec
57
59
  - lib/hackercli.rb
58
60
  - lib/hackercli/version.rb