askoverflow 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: eec69b2633bc533f53adcb6c8fb22786bf7d15e6d1bce934937bf3be3cd59c56
4
+ data.tar.gz: b9834531f8a885f31a78818b96b5eab946d47b4608fd259aada3abfaaf9741c8
5
+ SHA512:
6
+ metadata.gz: 1b103d21323949c7cf708b43d5cf2d0584874ac4ab04045123e74248e67e4bd31ea82e8739e79dfb5c3907804f6872626956a712f919832ee9ab1f2b37228303
7
+ data.tar.gz: 319f8666d71b292d195238f41fb4f40a1a4838005ca9063660972323b47280f461a4cffd06ea274d8a967901c99d6103d7b384f28e5b636894387db915ae1e1c
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .vscode
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.6
7
+ before_install: gem install bundler -v 1.17.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in askoverflow.gemspec
6
+ gemspec
@@ -0,0 +1,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ askoverflow (0.1.0)
5
+ nokogiri
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.3)
11
+ diff-lcs (1.3)
12
+ method_source (1.0.0)
13
+ mini_portile2 (2.4.0)
14
+ nokogiri (1.10.9-x64-mingw32)
15
+ mini_portile2 (~> 2.4.0)
16
+ pry (0.13.1)
17
+ coderay (~> 1.1)
18
+ method_source (~> 1.0)
19
+ rake (13.0.1)
20
+ rspec (3.9.0)
21
+ rspec-core (~> 3.9.0)
22
+ rspec-expectations (~> 3.9.0)
23
+ rspec-mocks (~> 3.9.0)
24
+ rspec-core (3.9.2)
25
+ rspec-support (~> 3.9.3)
26
+ rspec-expectations (3.9.2)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.9.0)
29
+ rspec-mocks (3.9.1)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.9.0)
32
+ rspec-support (3.9.3)
33
+
34
+ PLATFORMS
35
+ x64-mingw32
36
+
37
+ DEPENDENCIES
38
+ askoverflow!
39
+ bundler (~> 1.17)
40
+ pry (~> 0.13)
41
+ rake (~> 13.0)
42
+ rspec (~> 3.0)
43
+
44
+ BUNDLED WITH
45
+ 1.17.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 SVR
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ # Askoverflow
2
+
3
+ AskOverflow is a simple ruby CLI app that allows a user to quickly search stackoverflow, view a simplified results list in the terminal, select a
4
+ result and view it's full question and accepted answer, then return to the results page to read a different result or submit a different search.
5
+
6
+ ## Development
7
+
8
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
9
+
10
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
11
+
12
+ ## Contributing
13
+
14
+ Bug reports and pull requests are welcome on GitHub at https://github.com/SVRourke/askoverflow. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
15
+
16
+ ## License
17
+
18
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
19
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "askoverflow/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "askoverflow"
8
+ spec.version = Askoverflow::VERSION
9
+ spec.authors = ["SVR"]
10
+ spec.email = ["samuel@rourke.tech"]
11
+
12
+ spec.summary = %q{commandline tool to search stackoverflow.}
13
+ spec.description = %q{commandline tool to search stackoverflow quickly and simply.}
14
+ spec.homepage = "https://github.com/SVRourke/askoverflow"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "bin"
23
+ spec.executables = ["askoverflow"]
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.17"
27
+ spec.add_development_dependency "rake", "~> 13.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency "pry", "~> 0.13"
30
+
31
+ spec.add_runtime_dependency "nokogiri"
32
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler/setup'
3
+ require 'askoverflow'
4
+ # binding.pry
5
+
6
+ App.new.run
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "askoverflow"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ require "askoverflow/version"
2
+ require 'nokogiri'
3
+ require 'open-uri'
4
+ require 'pry'
5
+
6
+ require_relative "askoverflow/cli"
7
+ require_relative "askoverflow/result"
8
+ require_relative "askoverflow/scrape"
9
+ require_relative "askoverflow/app"
@@ -0,0 +1,72 @@
1
+ class App
2
+ # creates a new CLI and Scrape object upon initialization
3
+ def initialize
4
+ @ui = CLI.new
5
+ @scraper = Scrape.new
6
+ end
7
+ # formats the user's search string into the stackoverflow
8
+ # url query format
9
+ def f_query(raw)
10
+ fq = raw.gsub(" ", "+")
11
+ return "q=#{fq}"
12
+ end
13
+ # take the user's raw search string and return a url
14
+ def make_url(query)
15
+ base = 'https://stackoverflow.com/search?'
16
+ q = f_query(query)
17
+ answeredOnly = "+hasaccepted%3Atrue"
18
+ return "#{base}#{q}#{answeredOnly}"
19
+ end
20
+ # this is the main program control
21
+ def run
22
+ # greets the user on start
23
+ @ui.greet
24
+ # main loop
25
+ running = true
26
+ while running
27
+ # preemptively clear Results for multiple searches
28
+ # prompt the user for their search terms
29
+ # exit if desired by user
30
+ # otherwise scrape results from query
31
+ Result.clear_results
32
+ @ui.prompt_search
33
+ query = gets.strip
34
+ break if query == 'exit'
35
+ url = make_url(query)
36
+ @scraper.scrape_results(url)
37
+ # "viewing loop"
38
+ # this loop allows the user to return to the results
39
+ # display or to try a different search
40
+ # displays scraped results, prompts the user for a
41
+ # specific result, to return to try another search
42
+ # or exit the program
43
+ viewing_results = true
44
+ while viewing_results
45
+ @ui.display_results
46
+ @ui.prompt_result
47
+ result_id = gets.strip
48
+ if result_id == 'exit'
49
+ running = false
50
+ break
51
+ end
52
+ break if result_id == 'back'
53
+ # Scrapes the specific result, and adds data
54
+ chosen_result = Result.find_by_id(result_id)
55
+ @scraper.scrape_specific(chosen_result)
56
+ # "Specific View Loop"
57
+ # this loop allows us to view a specific result
58
+ # and subsequently return to the results list
59
+ viewing_result = true
60
+ while viewing_result
61
+ @ui.display_question(chosen_result)
62
+ @ui.prompt_next
63
+ @ui.display_answer(chosen_result)
64
+ @ui.prompt_return
65
+ break
66
+ end
67
+ end
68
+ end
69
+ @ui.goodbye
70
+ end
71
+ end
72
+ # binding.pry
@@ -0,0 +1,73 @@
1
+ class CLI
2
+ # greet, called at program start, greets the user
3
+ def greet
4
+ puts "askoverflow"
5
+ end
6
+ # displays a thankyou message for the user after exit
7
+ def goodbye
8
+ 10.times {visual_spacer}
9
+ puts "Thanks for using AskOverflow-"
10
+ puts "I hope we helped"
11
+ visual_spacer
12
+ end
13
+ # Helper function used to simplify displaying messages
14
+ # in a visually pleasing way
15
+ def visual_spacer
16
+ 3.times {puts ""}
17
+ end
18
+ # prompt functions used to print requests for user input
19
+ # asks the user to search or exit
20
+ def prompt_search
21
+ puts "please enter a search or exit: "
22
+ end
23
+ # asks user for the id number of their desired result
24
+ def prompt_result
25
+ puts "enter the id of a result to read further, back to do a new search or exit to quit"
26
+ end
27
+ # asks user to press enter to return to the results page
28
+ def prompt_return
29
+ visual_spacer
30
+ puts "+++enter to return to results+++"
31
+ visual_spacer
32
+ gets
33
+ end
34
+ # after displaying the full question text
35
+ # asks user to press enter to display the answer
36
+ def prompt_next
37
+ visual_spacer
38
+ puts "Press enter to read answer"
39
+ visual_spacer
40
+ gets
41
+ end
42
+ # Display functions, used to display data to the user
43
+ # display_results displays each result's basic info
44
+ def display_results
45
+ Result.all.each_with_index do |r, i|
46
+ puts "++++++++++++++++++++++++++++++++++++++++++++++++++"
47
+ puts "|ID:#{i + 1}| #{r.question.slice(0,60)}..."
48
+ puts "|#{r.answer_count} answer(s) since: #{r.ask_date}"
49
+ puts "|Tags:\t#{r.tags}"
50
+ puts "++++++++++++++++++++++++++++++++++++++++++++++++++"
51
+ puts ""
52
+ end
53
+ end
54
+ # after a result is selected, this displays the result's full question
55
+ def display_question(result)
56
+ 10.times {visual_spacer}
57
+ puts "++++++++++++++++++++++++++"
58
+ puts "+++++++++QUESTION+++++++++"
59
+ puts "++++++++++++++++++++++++++"
60
+ puts "#{result.full_q}"
61
+ end
62
+ # after a result is selected and the question has been displayed,
63
+ # this displays the result's full answer.
64
+ def display_answer(result)
65
+ 4.times {visual_spacer}
66
+ puts "++++++++++++++++++++++++++"
67
+ puts "++++++++++ANSWER++++++++++"
68
+ puts "++++++++++++++++++++++++++"
69
+ puts "#{result.full_a}"
70
+ end
71
+ end
72
+
73
+
@@ -0,0 +1,43 @@
1
+ # This is a class used to store the data of
2
+ # each scraped result in a central, easy to
3
+ # access location
4
+ class Result
5
+ # Attribute Accessors declared for each
6
+ # piece of information collected from
7
+ # each result
8
+ attr_accessor :question, :sample, :tags, :ask_date, :answer_count, :author, :link, :all, :id, :full_q, :full_a
9
+ # class varible array used to store each
10
+ # result instance
11
+ @@all = Array.new
12
+ # uses metaprogramming and attribut accessors
13
+ # to assign the values of an initial hash to
14
+ # the instance's attributes, then shovels the
15
+ # instance onto @@all
16
+ def initialize(attributes)
17
+ attributes.each do |s, v|
18
+ self.send("#{s}=", v)
19
+ end
20
+ @@all << self
21
+ end
22
+ # allows the user to access the results via
23
+ # human friendly indexing ie 1->10 not 0->9
24
+ def self.find_by_id(id)
25
+ @@all[id.to_i - 1]
26
+ end
27
+ # allows a result's scraped full Q&A to be
28
+ # added to the instance
29
+ def add_full(content_hash)
30
+ content_hash.each do |s, v|
31
+ self.send("#{s}=", v)
32
+ end
33
+ end
34
+ # deletes all existing Result instances
35
+ # used when additional searches are performed
36
+ def self.clear_results
37
+ @@all.clear
38
+ end
39
+ # provides access to the @@all class variable
40
+ def self.all
41
+ @@all
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ class Scrape
2
+ # helper function used to request and load the document
3
+ def getDoc(url)
4
+ Nokogiri::HTML(open(url))
5
+ end
6
+ # preliminary scraping function
7
+ # accepts a url, loads the page, scrapes
8
+ # the results on the page and creates a new
9
+ # Result object for each scraped result
10
+ def scrape_results(url)
11
+ doc = getDoc(url)
12
+ doc.css("div.search-result").each do |result|
13
+ Result.new({
14
+ :question => result.css(".question-hyperlink").text.strip,
15
+ :sample => result.css("div.excerpt").text.strip,
16
+ :ask_date => result.css("span.relativetime").text.strip,
17
+ :tags => result.css("div.tags").text.strip,
18
+ :answer_count => result.css("div.status strong").text.strip,
19
+ :author => result.css("div.started a").text.strip,
20
+ :link => result.css("a.question-hyperlink").first['href']
21
+ })
22
+ end
23
+ end
24
+ # Secondary scraping function
25
+ # accepts a Result instance,
26
+ # bulids a url using the result's :link
27
+ # and scrapes the specific result page's
28
+ # full question and full answer
29
+ def scrape_specific(result)
30
+ doc = getDoc("https://stackoverflow.com/#{result.link}")
31
+ result.add_full({
32
+ :full_q => doc.css("div.question div.postcell div.post-text").text,
33
+ :full_a => doc.css("div.answer div.answercell div.post-text").text
34
+ })
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Askoverflow
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: askoverflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - SVR
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: commandline tool to search stackoverflow quickly and simply.
84
+ email:
85
+ - samuel@rourke.tech
86
+ executables:
87
+ - askoverflow
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - askoverflow.gemspec
100
+ - bin/askoverflow
101
+ - bin/console
102
+ - bin/setup
103
+ - lib/askoverflow.rb
104
+ - lib/askoverflow/app.rb
105
+ - lib/askoverflow/cli.rb
106
+ - lib/askoverflow/result.rb
107
+ - lib/askoverflow/scrape.rb
108
+ - lib/askoverflow/version.rb
109
+ homepage: https://github.com/SVRourke/askoverflow
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.0.3
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: commandline tool to search stackoverflow.
132
+ test_files: []