diff --git a/.ruby-version b/.ruby-version
index bff6ce5c..ef538c28 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-ruby-2.7.1
+3.1.2
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 00000000..6da6866f
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1,2 @@
+ruby 3.1.2
+postgres 14.4
diff --git a/Gemfile b/Gemfile
index 0eb15ca0..513ca7fd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,23 +1,52 @@
source 'https://rubygems.org'
-ruby '2.7.1'
-gem 'byebug', platforms: [:mri, :mingw, :x64_mingw], group: [:development, :test]
-gem 'capybara', group: [:development, :test]
+ruby File.read('.ruby-version').chomp
+
+# gem 'byebug', platforms: [:mri, :mingw, :x64_mingw], group: [:development, :test]
+# gem 'capybara', group: [:development, :test]
gem 'coffee-rails'
gem 'devise'
-gem 'haml'
gem 'jbuilder'
gem 'listen', group: :development
gem 'pg'
gem 'pry-rails'
gem 'puma'
-gem 'rails'
+gem 'rails', '~> 7.0.3'
gem 'rspec-rails'
gem 'sass-rails'
gem 'selenium-webdriver', group: [:development, :test]
gem 'spring', group: :development
-gem 'spring-watcher-listen', group: :development
gem 'turbolinks'
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'uglifier'
gem 'web-console', group: :development
+gem 'rest-client'
+gem 'dotenv-rails'
+gem 'faraday'
+gem 'bcrypt'
+
+group :development, :test do
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+ # Adds support for Capybara system testing and webdrivers
+ gem 'capybara'
+ gem 'webdrivers'
+ gem 'better_errors'
+ gem 'binding_of_caller'
+ gem 'meta_request'
+ gem 'hirb'
+ gem 'faker'
+ gem 'populator'
+ gem 'factory_bot_rails'
+ gem 'simplecov'
+ gem 'shoulda'
+ gem 'shoulda-matchers', '2.8.0'
+ gem 'minitest'
+ gem 'minitest-rails'
+ gem 'minitest-reporters'
+ gem 'rails-controller-testing'
+ gem 'database_cleaner', '1.8.5'
+ gem 'launchy', '2.5.0'
+ gem 'bigdecimal', '1.3.0'
+ end
+
diff --git a/Gemfile.lock b/Gemfile.lock
index 677b6269..f3d291a9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,80 +1,94 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (6.1.4)
- actionpack (= 6.1.4)
- activesupport (= 6.1.4)
+ actioncable (7.0.5)
+ actionpack (= 7.0.5)
+ activesupport (= 7.0.5)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.1.4)
- actionpack (= 6.1.4)
- activejob (= 6.1.4)
- activerecord (= 6.1.4)
- activestorage (= 6.1.4)
- activesupport (= 6.1.4)
+ actionmailbox (7.0.5)
+ actionpack (= 7.0.5)
+ activejob (= 7.0.5)
+ activerecord (= 7.0.5)
+ activestorage (= 7.0.5)
+ activesupport (= 7.0.5)
mail (>= 2.7.1)
- actionmailer (6.1.4)
- actionpack (= 6.1.4)
- actionview (= 6.1.4)
- activejob (= 6.1.4)
- activesupport (= 6.1.4)
+ net-imap
+ net-pop
+ net-smtp
+ actionmailer (7.0.5)
+ actionpack (= 7.0.5)
+ actionview (= 7.0.5)
+ activejob (= 7.0.5)
+ activesupport (= 7.0.5)
mail (~> 2.5, >= 2.5.4)
+ net-imap
+ net-pop
+ net-smtp
rails-dom-testing (~> 2.0)
- actionpack (6.1.4)
- actionview (= 6.1.4)
- activesupport (= 6.1.4)
- rack (~> 2.0, >= 2.0.9)
+ actionpack (7.0.5)
+ actionview (= 7.0.5)
+ activesupport (= 7.0.5)
+ rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (6.1.4)
- actionpack (= 6.1.4)
- activerecord (= 6.1.4)
- activestorage (= 6.1.4)
- activesupport (= 6.1.4)
+ actiontext (7.0.5)
+ actionpack (= 7.0.5)
+ activerecord (= 7.0.5)
+ activestorage (= 7.0.5)
+ activesupport (= 7.0.5)
+ globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (6.1.4)
- activesupport (= 6.1.4)
+ actionview (7.0.5)
+ activesupport (= 7.0.5)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (6.1.4)
- activesupport (= 6.1.4)
+ activejob (7.0.5)
+ activesupport (= 7.0.5)
globalid (>= 0.3.6)
- activemodel (6.1.4)
- activesupport (= 6.1.4)
- activerecord (6.1.4)
- activemodel (= 6.1.4)
- activesupport (= 6.1.4)
- activestorage (6.1.4)
- actionpack (= 6.1.4)
- activejob (= 6.1.4)
- activerecord (= 6.1.4)
- activesupport (= 6.1.4)
- marcel (~> 1.0.0)
+ activemodel (7.0.5)
+ activesupport (= 7.0.5)
+ activerecord (7.0.5)
+ activemodel (= 7.0.5)
+ activesupport (= 7.0.5)
+ activestorage (7.0.5)
+ actionpack (= 7.0.5)
+ activejob (= 7.0.5)
+ activerecord (= 7.0.5)
+ activesupport (= 7.0.5)
+ marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (6.1.4)
+ activesupport (7.0.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
- zeitwerk (~> 2.3)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
- bcrypt (3.1.16)
+ addressable (2.8.4)
+ public_suffix (>= 2.0.2, < 6.0)
+ ansi (1.5.0)
+ bcrypt (3.1.18)
+ better_errors (2.10.0)
+ erubi (>= 1.0.0)
+ rack (>= 0.9.0)
+ rouge (>= 1.0.0)
+ bigdecimal (1.3.0)
bindex (0.8.1)
+ binding_of_caller (1.0.0)
+ debug_inspector (>= 0.0.1)
builder (3.2.4)
byebug (11.1.3)
- capybara (3.35.3)
+ capybara (3.39.1)
addressable
+ matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
- childprocess (3.0.0)
coderay (1.1.3)
coffee-rails (5.0.0)
coffee-script (>= 2.2.0)
@@ -83,110 +97,179 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- concurrent-ruby (1.1.9)
+ concurrent-ruby (1.2.2)
crass (1.0.6)
- devise (4.8.0)
+ database_cleaner (1.8.5)
+ date (3.3.3)
+ debug_inspector (1.1.0)
+ devise (4.9.2)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
- diff-lcs (1.4.4)
- erubi (1.10.0)
+ diff-lcs (1.5.0)
+ docile (1.4.0)
+ domain_name (0.5.20190701)
+ unf (>= 0.0.5, < 1.0.0)
+ dotenv (2.8.1)
+ dotenv-rails (2.8.1)
+ dotenv (= 2.8.1)
+ railties (>= 3.2)
+ erubi (1.12.0)
execjs (2.8.1)
- ffi (1.15.3)
- globalid (0.4.2)
- activesupport (>= 4.2.0)
- haml (5.2.1)
- temple (>= 0.8.0)
- tilt
- i18n (1.8.10)
+ factory_bot (6.2.1)
+ activesupport (>= 5.0.0)
+ factory_bot_rails (6.2.0)
+ factory_bot (~> 6.2.0)
+ railties (>= 5.0.0)
+ faker (3.2.0)
+ i18n (>= 1.8.11, < 2)
+ faraday (2.7.5)
+ faraday-net_http (>= 2.0, < 3.1)
+ ruby2_keywords (>= 0.0.4)
+ faraday-net_http (3.0.2)
+ ffi (1.15.5)
+ globalid (1.1.0)
+ activesupport (>= 5.0)
+ hirb (0.7.3)
+ http-accept (1.7.0)
+ http-cookie (1.0.5)
+ domain_name (~> 0.5)
+ i18n (1.13.0)
concurrent-ruby (~> 1.0)
- jbuilder (2.11.2)
+ jbuilder (2.11.5)
+ actionview (>= 5.0.0)
activesupport (>= 5.0.0)
- listen (3.5.1)
+ launchy (2.5.0)
+ addressable (~> 2.7)
+ listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
- loofah (2.10.0)
+ loofah (2.21.3)
crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.1)
+ nokogiri (>= 1.12.0)
+ mail (2.8.1)
mini_mime (>= 0.1.1)
- marcel (1.0.1)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.2)
+ matrix (0.4.2)
+ meta_request (0.7.4)
+ rack-contrib (>= 1.1, < 3)
+ railties (>= 3.0.0, < 7.1)
method_source (1.0.0)
- mini_mime (1.1.0)
- mini_portile2 (2.5.3)
- minitest (5.14.4)
- nio4r (2.5.8)
- nokogiri (1.11.7)
- mini_portile2 (~> 2.5.0)
+ mime-types (3.4.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2023.0218.1)
+ mini_mime (1.1.2)
+ mini_portile2 (2.8.2)
+ minitest (5.18.0)
+ minitest-rails (7.0.0)
+ minitest (~> 5.10)
+ railties (~> 7.0.0)
+ minitest-reporters (1.6.0)
+ ansi
+ builder
+ minitest (>= 5.0)
+ ruby-progressbar
+ net-imap (0.3.4)
+ date
+ net-protocol
+ net-pop (0.1.2)
+ net-protocol
+ net-protocol (0.2.1)
+ timeout
+ net-smtp (0.3.3)
+ net-protocol
+ netrc (0.11.0)
+ nio4r (2.5.9)
+ nokogiri (1.15.2)
+ mini_portile2 (~> 2.8.2)
racc (~> 1.4)
orm_adapter (0.5.0)
- pg (1.2.3)
- pry (0.14.1)
+ pg (1.5.3)
+ populator (1.0.0)
+ pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
- public_suffix (4.0.6)
- puma (5.6.2)
+ public_suffix (5.0.1)
+ puma (6.2.2)
nio4r (~> 2.0)
- racc (1.5.2)
- rack (2.2.3)
- rack-test (1.1.0)
- rack (>= 1.0, < 3)
- rails (6.1.4)
- actioncable (= 6.1.4)
- actionmailbox (= 6.1.4)
- actionmailer (= 6.1.4)
- actionpack (= 6.1.4)
- actiontext (= 6.1.4)
- actionview (= 6.1.4)
- activejob (= 6.1.4)
- activemodel (= 6.1.4)
- activerecord (= 6.1.4)
- activestorage (= 6.1.4)
- activesupport (= 6.1.4)
+ racc (1.6.2)
+ rack (2.2.7)
+ rack-contrib (2.3.0)
+ rack (~> 2.0)
+ rack-test (2.1.0)
+ rack (>= 1.3)
+ rails (7.0.5)
+ actioncable (= 7.0.5)
+ actionmailbox (= 7.0.5)
+ actionmailer (= 7.0.5)
+ actionpack (= 7.0.5)
+ actiontext (= 7.0.5)
+ actionview (= 7.0.5)
+ activejob (= 7.0.5)
+ activemodel (= 7.0.5)
+ activerecord (= 7.0.5)
+ activestorage (= 7.0.5)
+ activesupport (= 7.0.5)
bundler (>= 1.15.0)
- railties (= 6.1.4)
- sprockets-rails (>= 2.0.0)
+ railties (= 7.0.5)
+ rails-controller-testing (1.0.5)
+ actionpack (>= 5.0.1.rc1)
+ actionview (>= 5.0.1.rc1)
+ activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.3.0)
- loofah (~> 2.3)
- railties (6.1.4)
- actionpack (= 6.1.4)
- activesupport (= 6.1.4)
+ rails-html-sanitizer (1.5.0)
+ loofah (~> 2.19, >= 2.19.1)
+ railties (7.0.5)
+ actionpack (= 7.0.5)
+ activesupport (= 7.0.5)
method_source
- rake (>= 0.13)
+ rake (>= 12.2)
thor (~> 1.0)
- rake (13.0.3)
- rb-fsevent (0.11.0)
+ zeitwerk (~> 2.5)
+ rake (13.0.6)
+ rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
- regexp_parser (2.1.1)
- responders (3.0.1)
- actionpack (>= 5.0)
- railties (>= 5.0)
- rspec-core (3.10.1)
- rspec-support (~> 3.10.0)
- rspec-expectations (3.10.1)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.10.0)
- rspec-mocks (3.10.2)
- diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.10.0)
- rspec-rails (5.0.1)
+ regexp_parser (2.8.0)
+ responders (3.1.0)
actionpack (>= 5.2)
- activesupport (>= 5.2)
railties (>= 5.2)
- rspec-core (~> 3.10)
- rspec-expectations (~> 3.10)
- rspec-mocks (~> 3.10)
- rspec-support (~> 3.10)
- rspec-support (3.10.2)
- rubyzip (2.3.0)
+ rest-client (2.1.0)
+ http-accept (>= 1.7.0, < 2.0)
+ http-cookie (>= 1.0.2, < 2.0)
+ mime-types (>= 1.16, < 4.0)
+ netrc (~> 0.8)
+ rexml (3.2.5)
+ rouge (4.1.1)
+ rspec-core (3.12.2)
+ rspec-support (~> 3.12.0)
+ rspec-expectations (3.12.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-mocks (3.12.5)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-rails (6.0.2)
+ actionpack (>= 6.1)
+ activesupport (>= 6.1)
+ railties (>= 6.1)
+ rspec-core (~> 3.12)
+ rspec-expectations (~> 3.12)
+ rspec-mocks (~> 3.12)
+ rspec-support (~> 3.12)
+ rspec-support (3.12.0)
+ ruby-progressbar (1.13.0)
+ ruby2_keywords (0.0.5)
+ rubyzip (2.3.2)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
@@ -197,71 +280,109 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
- selenium-webdriver (3.142.7)
- childprocess (>= 0.5, < 4.0)
- rubyzip (>= 1.2.2)
- spring (2.1.1)
- spring-watcher-listen (2.0.1)
- listen (>= 2.7, < 4.0)
- spring (>= 1.2, < 3.0)
- sprockets (4.0.2)
+ selenium-webdriver (4.9.1)
+ rexml (~> 3.2, >= 3.2.5)
+ rubyzip (>= 1.2.2, < 3.0)
+ websocket (~> 1.0)
+ shoulda (3.5.0)
+ shoulda-context (~> 1.0, >= 1.0.1)
+ shoulda-matchers (>= 1.4.1, < 3.0)
+ shoulda-context (1.2.2)
+ shoulda-matchers (2.8.0)
+ activesupport (>= 3.0.0)
+ simplecov (0.22.0)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.4)
+ spring (4.1.1)
+ sprockets (4.2.0)
concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.2)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
+ rack (>= 2.2.4, < 4)
+ sprockets-rails (3.4.2)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
sprockets (>= 3.0.0)
- temple (0.8.2)
- thor (1.1.0)
- tilt (2.0.10)
+ thor (1.2.2)
+ tilt (2.1.0)
+ timeout (0.3.2)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
- tzinfo (2.0.4)
+ tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.8.2)
warden (1.2.9)
rack (>= 2.0.9)
- web-console (4.1.0)
+ web-console (4.2.0)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
+ webdrivers (5.2.0)
+ nokogiri (~> 1.6)
+ rubyzip (>= 1.3.0)
+ selenium-webdriver (~> 4.0)
+ websocket (1.2.9)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.4.2)
+ zeitwerk (2.6.8)
PLATFORMS
ruby
DEPENDENCIES
+ bcrypt
+ better_errors
+ bigdecimal (= 1.3.0)
+ binding_of_caller
byebug
capybara
coffee-rails
+ database_cleaner (= 1.8.5)
devise
- haml
+ dotenv-rails
+ factory_bot_rails
+ faker
+ faraday
+ hirb
jbuilder
+ launchy (= 2.5.0)
listen
+ meta_request
+ minitest
+ minitest-rails
+ minitest-reporters
pg
+ populator
pry-rails
puma
- rails
+ rails (~> 7.0.3)
+ rails-controller-testing
+ rest-client
rspec-rails
sass-rails
selenium-webdriver
+ shoulda
+ shoulda-matchers (= 2.8.0)
+ simplecov
spring
- spring-watcher-listen
turbolinks
tzinfo-data
uglifier
web-console
+ webdrivers
RUBY VERSION
- ruby 2.7.1p83
+ ruby 3.1.2p20
BUNDLED WITH
- 2.2.16
+ 2.3.22
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1c07694e..3f10acdb 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,3 +1,19 @@
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
+
+ before_action :configure_permitted_parameters, if: :devise_controller?
+
+ protected
+
+ def configure_permitted_parameters
+ # registration_params = [:first_name, :last_name, :email, :password, ]
+
+ devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :email, :password])
+
+ # if params[:action] == 'create'
+ # devise_parameter_sanitizer.for(:sign_up) do
+ # |u| u.permit(registration_params)
+ # end
+ # end end
+ end
end
diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb
new file mode 100644
index 00000000..db32eeb7
--- /dev/null
+++ b/app/controllers/articles_controller.rb
@@ -0,0 +1,39 @@
+class ArticlesController < ApplicationController
+
+ def index
+ @articles = Rails.cache.fetch(["top_stories_details"], expires_in: 4.hours) do
+ HackerNews.get_top_stories()
+ end
+
+ @liked_articles = (Article.has_likes.alphabetical.to_a).sort_by{|art| art.user_articles.count}.reverse
+ end
+
+ def liked_articles
+ # sort liked articles by how many likes they have
+ @articles = (Article.has_likes.alphabetical.to_a).sort_by{|art| art.user_articles.count}.reverse
+ end
+
+
+ def show
+ @article = Article.find_by(:article_id => params[:id])
+ if @article.nil? # the article has not been liked and therefore is not saved
+ @article = HackerNews.get_details(params[:id])
+ #raise "here"
+ else
+ #raise "here"
+ #@article = Article.find_by(:article_id => params[:id])
+ # set up @users_toggle for whether to show the like button to user (user cannot like same article multiple times)
+ if !@article.user_articles.empty?
+ @users_toggle = @article.user_articles.map{ |ua| ua.user }
+ end
+ end
+ end
+
+ def new
+ @article = Article.new
+ end
+
+ def edit
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/services/base.rb b/app/controllers/services/base.rb
new file mode 100644
index 00000000..e3ee74fd
--- /dev/null
+++ b/app/controllers/services/base.rb
@@ -0,0 +1,7 @@
+class Article
+ attr_accessor :id, :title, :author
+
+ def initialize(id)
+ @id = id
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/user_articles_controller.rb b/app/controllers/user_articles_controller.rb
new file mode 100644
index 00000000..65c35da2
--- /dev/null
+++ b/app/controllers/user_articles_controller.rb
@@ -0,0 +1,45 @@
+class UserArticlesController < ApplicationController
+
+ def like
+ @user_article = UserArticle.new()
+
+ # only save article if it hasn't been liked before
+ @article = Article.find_by(:article_id => params[:id])
+
+ if @article.nil?
+ @article = HackerNews.get_details(params[:id])
+ if @article.save
+ #flash[:notice] = "Successfully liked the article"
+ @user_article.article = @article
+ @user_article.user = current_user
+ if @user_article.save
+ flash[:notice] = "Successfully liked the article"
+ redirect_to article_path(@article.article_id)
+ else
+ redirect_to action: "index"
+ end
+ end
+ else
+ # if the article already exists
+ @user_article.article = @article
+ @user_article.user = current_user
+ if @user_article.save
+ flash[:notice] = "Successfully liked the article"
+ redirect_to article_path(@article.article_id)
+ else
+ redirect_to action: "index"
+ end
+ end
+
+ end
+
+ def unlike
+ @article = Article.find_by(:article_id => params[:id])
+ @user_article = UserArticle.find_by(:user => current_user, :article => @article)
+ @user_article.destroy
+ flash[:notice] = "Successfully unliked article"
+ redirect_to article_path(@article.article_id)
+ end
+
+
+end
\ No newline at end of file
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
new file mode 100644
index 00000000..02205ace
--- /dev/null
+++ b/app/controllers/users_controller.rb
@@ -0,0 +1,16 @@
+# require 'dotenv'
+# require 'json'
+# Dotenv.load
+
+class UsersController < ApplicationController
+
+ def destroy
+ @user = User.find(params[:id])
+ @user.destroy
+
+ if @user.destroy
+ redirect_to root_url, notice: "User deleted."
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/article.rb b/app/models/article.rb
new file mode 100644
index 00000000..d9f59fbb
--- /dev/null
+++ b/app/models/article.rb
@@ -0,0 +1,19 @@
+class Article < ApplicationRecord
+ # Relationships
+ has_many :user_articles
+ has_many :users, through: :user_articles
+
+ # Scopes
+ scope :alphabetical, -> { order('title') }
+ scope :has_likes, -> { Article.includes(:user_articles).where.not(user_articles: { article_id: nil }) }
+
+ # Validations
+ validates_presence_of :article_id, :title, :author, :score, :url
+ validates_numericality_of :score
+
+ def to_sym
+ :read_only
+ end
+
+
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index b2091f9a..e58523a8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -3,4 +3,13 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
+
+ # Relationships
+ has_many :user_articles
+ has_many :articles, through: :user_articles
+
+ # Validations
+ validates_presence_of :first_name, :last_name, :email, :password
+
end
+
diff --git a/app/models/user_article.rb b/app/models/user_article.rb
new file mode 100644
index 00000000..feea1fb5
--- /dev/null
+++ b/app/models/user_article.rb
@@ -0,0 +1,6 @@
+class UserArticle < ApplicationRecord
+ belongs_to :user
+ belongs_to :article
+
+ validates_presence_of :user_id, :article_id, uniqueness: true
+end
diff --git a/app/services/hacker_news.rb b/app/services/hacker_news.rb
new file mode 100644
index 00000000..4b78011a
--- /dev/null
+++ b/app/services/hacker_news.rb
@@ -0,0 +1,25 @@
+require 'json'
+
+class HackerNews
+ def self.get_top_stories
+ url = "https://hacker-news.firebaseio.com/v0/topstories.json"
+ response = RestClient.get(url)
+ articles_ids = JSON.parse(RestClient.get("https://hacker-news.firebaseio.com/v0/topstories.json")).slice(0, 50)
+ res = articles_ids.map { |article_id| get_details(article_id) }.to_a
+ # sort top stories by score
+ res.sort_by {|art| art.score }.reverse
+ end
+
+ def self.get_details(article_id)
+ response = RestClient.get("https://hacker-news.firebaseio.com/v0/item/" + article_id.to_s + ".json")
+ @article = Article.new()
+ @article.title = JSON.parse(response)['title']
+ @article.author = JSON.parse(response)['by']
+ @article.score = JSON.parse(response)['score']
+ @article.url = JSON.parse(response)['url']
+ @article.article_id = article_id
+
+ return @article
+ end
+
+end
\ No newline at end of file
diff --git a/app/views/articles/_articles.html.erb b/app/views/articles/_articles.html.erb
new file mode 100644
index 00000000..92f53448
--- /dev/null
+++ b/app/views/articles/_articles.html.erb
@@ -0,0 +1,37 @@
+
Welcome to Top News
+
+
+
+ Title
+ Author
+ Score
+ Like?
+
+
+<% for article in @articles %>
+
+
+
+ <%= link_to article.title, article_path(article.article_id) %>
+
+ <%= article.author %>
+
+ <%= article.score %>
+
+
+ <% if user_signed_in? %>
+ <%if !article.user_articles&.present?%>
+ <%= link_to 'like',like_path(id:article.article_id), method: :patch%>
+ <%else%>
+ <%= link_to 'unlike',unlike_path(id:article.article_id), method: :patch%>
+ <% end -%>
+ <%else%>
+ please sign in to like articles
+ <% end -%>
+
+
+
+
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/articles/_liked_articles.html.erb b/app/views/articles/_liked_articles.html.erb
new file mode 100644
index 00000000..8a489365
--- /dev/null
+++ b/app/views/articles/_liked_articles.html.erb
@@ -0,0 +1,32 @@
+Liked Articles
+
+
+
+ Title
+ Author
+ Score
+ Likes
+
+<% for article in @liked_articles %>
+
+
+
+ <%= link_to article.title, article_path(article.article_id) %>
+
+ <%= article.author %>
+
+ <%= article.score %>
+
+ <% article.user_articles %>
+ <%if !article.user_articles&.present?%>
+ <%= 0 %>
+ <%else%>
+ <%= article.user_articles.map { |ua| ua.user }.count %>
+ <% end -%>
+
+
+
+
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/articles/index.html.erb b/app/views/articles/index.html.erb
new file mode 100644
index 00000000..6fdc276e
--- /dev/null
+++ b/app/views/articles/index.html.erb
@@ -0,0 +1,6 @@
+<%= render partial: "partials/structure1", locals: {model_name: "article",
+ primary: "articles",
+ secondary: "liked_articles",
+ sidebar: nil} %>
+
+
diff --git a/app/views/articles/liked_articles.html.erb b/app/views/articles/liked_articles.html.erb
new file mode 100644
index 00000000..d2f177e9
--- /dev/null
+++ b/app/views/articles/liked_articles.html.erb
@@ -0,0 +1,35 @@
+Welcome to Top News
+Liked Articles
+
+<%= link_to "top stories", articles_url%>
+
+
+
+ Title \
+ Author
+ Score
+ Likes
+
+<% for article in @articles %>
+
+
+
+ <%= link_to article.title, article_path(article.article_id) %>
+
+ <%= article.author %>
+
+ <%= article.score %>
+
+ <% article.user_articles %>
+ <%if !article.user_articles&.present?%>
+ <%= 0 %>
+ <%else%>
+ <%= article.user_articles.map { |ua| ua.user }.count %>
+ <% end -%>
+
+
+
+
+
+ <% end %>
+
diff --git a/app/views/articles/show.html.erb b/app/views/articles/show.html.erb
new file mode 100644
index 00000000..5d689170
--- /dev/null
+++ b/app/views/articles/show.html.erb
@@ -0,0 +1,31 @@
+<%= @article.title %>
+
+
+<%= link_to 'read the article', @article.url%>
+
+<% if user_signed_in? %>
+ <% if !(@users_toggle.nil?) %>
+ <% if !(@users_toggle.include?(current_user)) %>
+ <%= link_to 'like',like_path(id:@article.article_id), method: :patch%>
+ <%else%>
+ <%= link_to 'unlike',unlike_path(id:@article.article_id), method: :patch%>
+ <% end %>
+ <%else%>
+ <%= link_to 'like',like_path(id:@article.article_id), method: :patch%>
+ <% end %>
+<%else%>
+ please sign in to like articles
+ <% end %>
+
+
+
+ <% if !@article.user_articles.empty? %>
+ Users who liked this article
+ <% for user_article in @article.user_articles %>
+ <%= user_article.user.first_name + " " + user_article.user.last_name%>
+ <% end %>
+ <%else%>
+ No Likes
+ <% end -%>
+
+
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
new file mode 100644
index 00000000..b12dd0cb
--- /dev/null
+++ b/app/views/devise/confirmations/new.html.erb
@@ -0,0 +1,16 @@
+Resend confirmation instructions
+
+<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
+
+
+
+ <%= f.submit "Resend confirmation instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
new file mode 100644
index 00000000..dc55f64f
--- /dev/null
+++ b/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -0,0 +1,5 @@
+Welcome <%= @email %>!
+
+You can confirm your account email through the link below:
+
+<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb
new file mode 100644
index 00000000..32f4ba80
--- /dev/null
+++ b/app/views/devise/mailer/email_changed.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @email %>!
+
+<% if @resource.try(:unconfirmed_email?) %>
+ We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
+<% else %>
+ We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
+<% end %>
diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb
new file mode 100644
index 00000000..b41daf47
--- /dev/null
+++ b/app/views/devise/mailer/password_change.html.erb
@@ -0,0 +1,3 @@
+Hello <%= @resource.email %>!
+
+We're contacting you to notify you that your password has been changed.
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb
new file mode 100644
index 00000000..f667dc12
--- /dev/null
+++ b/app/views/devise/mailer/reset_password_instructions.html.erb
@@ -0,0 +1,8 @@
+Hello <%= @resource.email %>!
+
+Someone has requested a link to change your password. You can do this through the link below.
+
+<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
+
+If you didn't request this, please ignore this email.
+Your password won't change until you access the link above and create a new one.
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb
new file mode 100644
index 00000000..41e148bf
--- /dev/null
+++ b/app/views/devise/mailer/unlock_instructions.html.erb
@@ -0,0 +1,7 @@
+Hello <%= @resource.email %>!
+
+Your account has been locked due to an excessive number of unsuccessful sign in attempts.
+
+Click the link below to unlock your account:
+
+<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb
new file mode 100644
index 00000000..5fbb9ff0
--- /dev/null
+++ b/app/views/devise/passwords/edit.html.erb
@@ -0,0 +1,25 @@
+Change your password
+
+<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+ <%= f.hidden_field :reset_password_token %>
+
+
+ <%= f.label :password, "New password" %>
+ <% if @minimum_password_length %>
+ (<%= @minimum_password_length %> characters minimum)
+ <% end %>
+ <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :password_confirmation, "Confirm new password" %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.submit "Change my password" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb
new file mode 100644
index 00000000..9b486b81
--- /dev/null
+++ b/app/views/devise/passwords/new.html.erb
@@ -0,0 +1,16 @@
+Forgot your password?
+
+<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.submit "Send me reset password instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
new file mode 100644
index 00000000..b82e3365
--- /dev/null
+++ b/app/views/devise/registrations/edit.html.erb
@@ -0,0 +1,43 @@
+Edit <%= resource_name.to_s.humanize %>
+
+<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
+ Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %>
+
+
+ <%= f.label :password %> (leave blank if you don't want to change it)
+ <%= f.password_field :password, autocomplete: "new-password" %>
+ <% if @minimum_password_length %>
+
+ <%= @minimum_password_length %> characters minimum
+ <% end %>
+
+
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :current_password %> (we need your current password to confirm your changes)
+ <%= f.password_field :current_password, autocomplete: "current-password" %>
+
+
+
+ <%= f.submit "Update" %>
+
+<% end %>
+
+Cancel my account
+
+Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>
+
+<%= link_to "Back", :back %>
diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb
new file mode 100644
index 00000000..cf8e2ca7
--- /dev/null
+++ b/app/views/devise/registrations/new.html.erb
@@ -0,0 +1,39 @@
+Sign up
+
+<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :first_name %>
+ <%= f.text_field :first_name, autofocus: true, autocomplete: "last name" %>
+
+
+
+ <%= f.label :last_name %>
+ <%= f.text_field :last_name, autofocus: true, autocomplete: "last name" %>
+
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.label :password %>
+ <% if @minimum_password_length %>
+ (<%= @minimum_password_length %> characters minimum)
+ <% end %>
+ <%= f.password_field :password, autocomplete: "new-password" %>
+
+
+
+ <%= f.label :password_confirmation %>
+ <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
+
+
+
+ <%= f.submit "Sign up" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
new file mode 100644
index 00000000..5ede9648
--- /dev/null
+++ b/app/views/devise/sessions/new.html.erb
@@ -0,0 +1,26 @@
+Log in
+
+<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.label :password %>
+ <%= f.password_field :password, autocomplete: "current-password" %>
+
+
+ <% if devise_mapping.rememberable? %>
+
+ <%= f.check_box :remember_me %>
+ <%= f.label :remember_me %>
+
+ <% end %>
+
+
+ <%= f.submit "Log in" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/devise/shared/_error_messages.html.erb b/app/views/devise/shared/_error_messages.html.erb
new file mode 100644
index 00000000..cabfe307
--- /dev/null
+++ b/app/views/devise/shared/_error_messages.html.erb
@@ -0,0 +1,15 @@
+<% if resource.errors.any? %>
+
+
+ <%= I18n.t("errors.messages.not_saved",
+ count: resource.errors.count,
+ resource: resource.class.model_name.human.downcase)
+ %>
+
+
+ <% resource.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+<% end %>
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
new file mode 100644
index 00000000..7a75304b
--- /dev/null
+++ b/app/views/devise/shared/_links.html.erb
@@ -0,0 +1,25 @@
+<%- if controller_name != 'sessions' %>
+ <%= link_to "Log in", new_session_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
+ <%= link_to "Sign up", new_registration_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
+ <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
+ <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
+ <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+<% end %>
+
+<%- if devise_mapping.omniauthable? %>
+ <%- resource_class.omniauth_providers.each do |provider| %>
+ <%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %>
+ <% end %>
+<% end %>
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
new file mode 100644
index 00000000..ffc34de8
--- /dev/null
+++ b/app/views/devise/unlocks/new.html.erb
@@ -0,0 +1,16 @@
+Resend unlock instructions
+
+<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
+ <%= render "devise/shared/error_messages", resource: resource %>
+
+
+ <%= f.label :email %>
+ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+
+
+ <%= f.submit "Resend unlock instructions" %>
+
+<% end %>
+
+<%= render "devise/shared/links" %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
new file mode 100644
index 00000000..dee81226
--- /dev/null
+++ b/app/views/layouts/application.html.erb
@@ -0,0 +1,20 @@
+
+
+
+
+ Top News
+ <%= csrf_meta_tags %>
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
+ <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
+
+
+ <%= render :partial => "partials/nav" %>
+
+ <%= notice %>
+
+
+ <%= alert %>
+
+ <%= yield %>
+
+
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
deleted file mode 100644
index bfdcda24..00000000
--- a/app/views/layouts/application.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-!!!
-%html
- %head
- %meta{content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
- %title Top News
- = csrf_meta_tags
- = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
- = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
- %body
- %p.notice= notice
- %p.alert= alert
- = yield
diff --git a/app/views/liked_articles/index.html.erb b/app/views/liked_articles/index.html.erb
new file mode 100644
index 00000000..c79458db
--- /dev/null
+++ b/app/views/liked_articles/index.html.erb
@@ -0,0 +1,36 @@
+Welcome to Top News
+Liked Articles
+
+
+ Title \
+ Author
+ Likes
+ Like?
+
+<% for article in @articles %>
+
+
+
+ <%= link_to article.title, article_path(article.article_id) %>
+
+ <%= article.author %>
+
+ <% article.user_articles %>
+ <%if !article.user_articles&.present?%>
+ <%= 0 %>
+ <%else%>
+ <%= article.user_articles.map { |ua| ua.user }.count %>
+ <% end -%>
+
+ <%if !(current_user in article.user_articles)%>
+ <%= link_to 'like',like_path(id:article.article_id), method: :patch%>
+ <%else%>
+ <%= link_to 'deactivate',unlike_path(id:article.article_id), method: :patch%>
+ <% end -%>
+
+
+
+
+
+ <% end %>
+
diff --git a/app/views/pages/home.html.erb b/app/views/pages/home.html.erb
new file mode 100644
index 00000000..0b409157
--- /dev/null
+++ b/app/views/pages/home.html.erb
@@ -0,0 +1,9 @@
+Welcome to Top News
+
+
+
+ <% for article in @articles %>
+ <%= article %>
+ <% end %>
+
+
diff --git a/app/views/pages/home.html.haml b/app/views/pages/home.html.haml
deleted file mode 100644
index dfe79e9b..00000000
--- a/app/views/pages/home.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-%h1 Welcome to Top News
diff --git a/app/views/partials/_add_new_object.html.erb b/app/views/partials/_add_new_object.html.erb
new file mode 100644
index 00000000..20afd496
--- /dev/null
+++ b/app/views/partials/_add_new_object.html.erb
@@ -0,0 +1,4 @@
+
+<%= link_to(eval("new_#{model_name}_path"), class: "waves-effect waves-light btn") do %>
+ Add a New <%= model_name.capitalize %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/partials/_flash.html.erb b/app/views/partials/_flash.html.erb
new file mode 100644
index 00000000..c1ab49aa
--- /dev/null
+++ b/app/views/partials/_flash.html.erb
@@ -0,0 +1,6 @@
+<% flash.each do |name, msg| %>
+ ">
+
+ <%= msg %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/partials/_footer.html.erb b/app/views/partials/_footer.html.erb
new file mode 100644
index 00000000..c25773d9
--- /dev/null
+++ b/app/views/partials/_footer.html.erb
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/app/views/partials/_nav.html.erb b/app/views/partials/_nav.html.erb
new file mode 100644
index 00000000..64c7f856
--- /dev/null
+++ b/app/views/partials/_nav.html.erb
@@ -0,0 +1,20 @@
+
+
+
+ <%= link_to 'Top Stores', articles_url %>
+
+ <% if user_signed_in? %>
+
+ <%= link_to 'Sign Out', destroy_user_session_path, method: :delete, class: 'nav-link' %>
+
+ <% else %>
+
+ <%= link_to 'Sign In', new_user_session_path, class: 'nav-link' %>
+
+
+ <%= link_to 'Register', new_user_registration_path, class: 'nav-link' %>
+
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/partials/_object_options.html.erb b/app/views/partials/_object_options.html.erb
new file mode 100644
index 00000000..f6c6c3a3
--- /dev/null
+++ b/app/views/partials/_object_options.html.erb
@@ -0,0 +1,17 @@
+<% opt = "#{object.respond_to?(:name) ? object.name : model_name.capitalize }" %>
+
+
+ <%= link_to(eval("#{model_name.pluralize}_path"), class: 'waves-effect waves-light btn') do %>
+ Display All <%# model_name.pluralize.capitalize %>
+ <% end %>
+
+
+ <%= link_to(eval("edit_#{model_name}_path(object)"), class: 'waves-effect waves-light btn') do %>
+ <%= "Edit #{opt}" %>
+ <% end %>
+
+
+ <%= link_to(eval("#{model_name}_path(object)"), method: 'delete', data: {confirm: "Are you sure you want to permanently delete #{opt}"}, class: 'waves-effect waves-light btn btn-alert') do %>
+ <%= "Delete #{opt}" %>
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/partials/_quick_links.html.erb b/app/views/partials/_quick_links.html.erb
new file mode 100644
index 00000000..8be20268
--- /dev/null
+++ b/app/views/partials/_quick_links.html.erb
@@ -0,0 +1,11 @@
+
+
+
Quick Links
+
+ <%= link_to 'About', about_path, :class => "collection-item" %>
+ <%= link_to 'Contact', contracted_books_path, :class => "collection-item" %>
+ <%= link_to 'New Proposals', proposed_books_path, :class => "collection-item" %>
+ <%= link_to 'Add a New Book', new_book_path, :class => "collection-item" %>
+
+
+
diff --git a/app/views/partials/_structure1.html.erb b/app/views/partials/_structure1.html.erb
new file mode 100644
index 00000000..4d697a7c
--- /dev/null
+++ b/app/views/partials/_structure1.html.erb
@@ -0,0 +1,15 @@
+
+
+
+ <%= render partial: "#{primary}" %>
+
+ <% unless secondary.nil? %>
+
+ <%= render partial: "#{secondary}" %>
+ <% end %>
+
+
+
+ <%= render partial: "#{sidebar}" unless sidebar.blank? %>
+
+
\ No newline at end of file
diff --git a/app/views/partials/_structure2.html.erb b/app/views/partials/_structure2.html.erb
new file mode 100644
index 00000000..9cd724fc
--- /dev/null
+++ b/app/views/partials/_structure2.html.erb
@@ -0,0 +1,18 @@
+
+
+
+ <%= render partial: "#{primary}" %>
+
+ <% unless secondary.nil? %>
+
+ <%= render partial: "#{secondary}" %>
+ <% end %>
+
+
+
+ <%= render partial: "#{sidebar}" unless sidebar.blank? %>
+
+
+
+ <%= render :partial => "partials/object_options", locals: {model_name: model_name, object: object} %>
+
\ No newline at end of file
diff --git a/app/views/partials/_topbar.html.erb b/app/views/partials/_topbar.html.erb
new file mode 100644
index 00000000..12675bbe
--- /dev/null
+++ b/app/views/partials/_topbar.html.erb
@@ -0,0 +1,11 @@
+
+
+ <%= link_to(image_tag('logos/science_logo_3.png'), home_path) %>
+
+
+
ScienceQuiz
+
+
+
+
+
diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb
new file mode 100644
index 00000000..94635c2d
--- /dev/null
+++ b/app/views/users/_user.html.erb
@@ -0,0 +1,6 @@
+
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
new file mode 100644
index 00000000..76cff339
--- /dev/null
+++ b/app/views/users/index.html.erb
@@ -0,0 +1,10 @@
+
+
+ USERS DIRECTORY
+ <%= render @users %>
+
+
+
+ <%= will_paginate @users %>
+
+
\ No newline at end of file
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 5187e221..ebd0808d 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -4,7 +4,7 @@
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
- config.cache_classes = false
+ config.cache_classes = true
# Do not eager load code on boot.
config.eager_load = false
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 8e5cbde5..7b6dc4c0 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -5,7 +5,7 @@
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
- config.cache_classes = true
+ config.cache_classes = false
# Do not eager load code on boot. This avoids loading your whole application
# just for the purpose of running a single test. If you are using a tool that
diff --git a/config/routes.rb b/config/routes.rb
index c12ef082..f5a8f3f8 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,31 @@
Rails.application.routes.draw do
+ # devise_for :users
+ root to: "articles#index"
+
+ patch 'user_articles/:id/like', to:'user_articles#like' , as: :like
+ patch 'user_articles/:id/unlike', to:'user_articles#unlike' , as: :unlike
+
devise_for :users
- root to: 'pages#home'
+ devise_scope :user do
+ # root to: "articles#index"
+ get '/users/sign_out' => 'devise/sessions#destroy'
+ post '/users/sign_up' => 'devise/registrations#create'
+ match '/sessions/user', to: 'devise/sessions#create', via: :post
+ end
+
+ # get '/articles', to: "articles#index"
+ resources :articles
+
+ get '/liked_articles', to: "articles#liked_articles"
+
+ resources :users
+ resources :sessions
+
+
+
+ # get 'user/edit' => 'users#edit', :as => :edit_current_user
+ # get 'signup' => 'users#new', :as => :signup
+ # get 'login' => 'sessions#new', :as => :login
+ # get 'logout' => 'sessions#destroy', :as => :logout
+
end
diff --git a/db/migrate/20230524183141_create_articles.rb b/db/migrate/20230524183141_create_articles.rb
new file mode 100644
index 00000000..f806fb81
--- /dev/null
+++ b/db/migrate/20230524183141_create_articles.rb
@@ -0,0 +1,13 @@
+class CreateArticles < ActiveRecord::Migration[7.0]
+ def change
+ create_table :articles do |t|
+ t.integer :article_id
+ t.string :title
+ t.string :author
+ t.integer :score
+ t.string :url
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20230524184051_create_user_articles.rb b/db/migrate/20230524184051_create_user_articles.rb
new file mode 100644
index 00000000..357ae65e
--- /dev/null
+++ b/db/migrate/20230524184051_create_user_articles.rb
@@ -0,0 +1,10 @@
+class CreateUserArticles < ActiveRecord::Migration[7.0]
+ def change
+ create_table :user_articles do |t|
+ t.references :user, null: false, foreign_key: true
+ t.references :article, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index df5f0df7..5feeaed5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,28 +10,48 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2018_02_28_212101) do
-
+ActiveRecord::Schema[7.0].define(version: 2023_05_24_184051) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "articles", force: :cascade do |t|
+ t.integer "article_id"
+ t.string "title"
+ t.string "author"
+ t.integer "score"
+ t.string "url"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "user_articles", force: :cascade do |t|
+ t.bigint "user_id", null: false
+ t.bigint "article_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["article_id"], name: "index_user_articles_on_article_id"
+ t.index ["user_id"], name: "index_user_articles_on_user_id"
+ end
+
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
- t.datetime "reset_password_sent_at"
- t.datetime "remember_created_at"
+ t.datetime "reset_password_sent_at", precision: nil
+ t.datetime "remember_created_at", precision: nil
t.integer "sign_in_count", default: 0, null: false
- t.datetime "current_sign_in_at"
- t.datetime "last_sign_in_at"
+ t.datetime "current_sign_in_at", precision: nil
+ t.datetime "last_sign_in_at", precision: nil
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
+ add_foreign_key "user_articles", "articles"
+ add_foreign_key "user_articles", "users"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 76b85394..231b90ad 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -1,16 +1,16 @@
-User.find_or_initialize_by(email: 'DonaldGMiller@example.com').update_attributes({
+User.find_or_initialize_by(email: 'DonaldGMiller@example.com').update({
first_name: 'Donald',
last_name: 'Miller',
password: 'eeMaev2shai'
})
-User.find_or_initialize_by(email: 'LawrenceWGrant@example.com').update_attributes({
+User.find_or_initialize_by(email: 'LawrenceWGrant@example.com').update({
first_name: 'Lawrence',
last_name: 'Grant',
password: 'ahR7iecai'
})
-User.find_or_initialize_by(email: 'MargeRWilliams@example.com').update_attributes({
+User.find_or_initialize_by(email: 'MargeRWilliams@example.com').update({
first_name: 'Marge',
last_name: 'Williams',
password: 'Aechugh1ie'
diff --git a/lib/hackernews/connection.rb b/lib/hackernews/connection.rb
new file mode 100644
index 00000000..88b6196e
--- /dev/null
+++ b/lib/hackernews/connection.rb
@@ -0,0 +1,14 @@
+require 'faraday'
+require 'json'
+
+class Connection
+ BASE = 'https://hacker-news.firebaseio.com/v0/'
+
+ def self.api
+ Faraday.new(url: BASE) do |faraday|
+ faraday.response :logger
+ faraday.adapter Faraday.default_adapter
+ # faraday.headers['Content-Type'] = 'application/json'
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/hackernews/request.rb b/lib/hackernews/request.rb
new file mode 100644
index 00000000..6d4308b1
--- /dev/null
+++ b/lib/hackernews/request.rb
@@ -0,0 +1,31 @@
+class Request
+ class << self
+ def where(resource_path, cache, query = {}, options = {})
+ response, status = get_json(resource_path, cache, query)
+ status == 200 ? response : errors(response)
+ end
+
+ def get(id, cache)
+ response, status = get_json(id, cache)
+ status == 200 ? response : errors(response)
+ end
+
+ def errors(response)
+ error = { errors: { status: response["status"], message: response["message"] } }
+ response.merge(error)
+ end
+
+ def get_json(root_path, cache, query = {})
+ query_string = query.map{|k,v| "#{k}=#{v}"}.join("&")
+ path = query.empty?? root_path : "#{root_path}?#{query_string}"
+ response = Rails.cache.fetch(path, expires_in: cache[:expires_in], force: cache[:force]) do
+ api.get(path)
+ end
+ [JSON.parse(response.body), response.status]
+ end
+
+ def api
+ Connection.api
+ end
+ end
+ end
\ No newline at end of file
diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb
new file mode 100644
index 00000000..632e8564
--- /dev/null
+++ b/spec/models/article_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Article, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/user_article_spec.rb b/spec/models/user_article_spec.rb
new file mode 100644
index 00000000..a2dd0eee
--- /dev/null
+++ b/spec/models/user_article_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe UserArticle, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index bbe1ba57..e1108c2f 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -5,6 +5,8 @@
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
+require 'shoulda/matchers'
+require 'rspec/autorun'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
diff --git a/test/contexts.rb b/test/contexts.rb
new file mode 100644
index 00000000..9728669f
--- /dev/null
+++ b/test/contexts.rb
@@ -0,0 +1,23 @@
+# require needed files
+require './test/sets/users'
+require './test/sets/articles'
+require './test/sets/user_articles'
+
+
+module Contexts
+ # explicitly include all sets of contexts used for testing
+ include Contexts::Users
+ include Contexts::Articles
+ include Contexts::UserArticles
+
+ def create_all
+ # Kernel.puts "Building context..."
+ create_users
+ # Kernel.puts "Created users"
+ create_articles
+ # Kernel.puts "Created articles"
+ create_user_articles
+ # Kernel.puts "Registered students on user_articles"
+ end
+
+end
\ No newline at end of file
diff --git a/test/controllers/articles_controller_test.rb b/test/controllers/articles_controller_test.rb
new file mode 100644
index 00000000..9e9a2756
--- /dev/null
+++ b/test/controllers/articles_controller_test.rb
@@ -0,0 +1,23 @@
+require "test_helper"
+
+class ArticlesControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @article = FactoryBot.create(:article)
+ end
+
+ test "should get index" do
+ get articles_path
+ assert_response :success
+ end
+
+ test "should get liked articles" do
+ get liked_articles_path
+ assert_response :success
+ end
+
+ test "should show organization" do
+ get article_path(@article.article_id)
+ assert_response :success
+ end
+
+end
\ No newline at end of file
diff --git a/test/factories/articles.rb b/test/factories/articles.rb
new file mode 100644
index 00000000..90218037
--- /dev/null
+++ b/test/factories/articles.rb
@@ -0,0 +1,10 @@
+FactoryBot.define do
+ factory :article do
+ title { "How to Make it Big" }
+ author { "John Doe" }
+ score { 12345 }
+ url { "nytimes.com" }
+ article_id { 8863 }
+ end
+ end
+
\ No newline at end of file
diff --git a/test/factories/user_articles.rb b/test/factories/user_articles.rb
new file mode 100644
index 00000000..c033f2e3
--- /dev/null
+++ b/test/factories/user_articles.rb
@@ -0,0 +1,7 @@
+FactoryBot.define do
+ factory :user_article do
+ association :user
+ association :article
+ end
+ end
+
\ No newline at end of file
diff --git a/test/factories/users.rb b/test/factories/users.rb
new file mode 100644
index 00000000..89022f98
--- /dev/null
+++ b/test/factories/users.rb
@@ -0,0 +1,10 @@
+FactoryBot.define do
+ factory :user do
+ first_name { "Jane" }
+ last_name { "doe" }
+ email { "doe@gmail.com" }
+ password { "test123" }
+ password_confirmation { "test123" }
+ end
+ end
+
\ No newline at end of file
diff --git a/test/models/article_test.rb b/test/models/article_test.rb
new file mode 100644
index 00000000..bb07ef29
--- /dev/null
+++ b/test/models/article_test.rb
@@ -0,0 +1,32 @@
+require 'test_helper'
+
+class ArticleTest < ActiveSupport::TestCase
+ # Matchers
+ should have_many(:user_articles)
+ should have_many(:users).through(:user_articles)
+
+ should validate_presence_of(:title)
+ should validate_presence_of(:author)
+ should validate_presence_of(:score)
+ should validate_presence_of(:url)
+
+ should validate_numericality_of(:score)
+
+ # Context
+ context "Given context" do
+ setup do
+ create_articles
+ create_users
+ create_user_articles
+ end
+
+ should "list articles alphabetically" do
+ assert_equal [@nytimes, @washington_post, @time], Article.alphabetical
+ end
+
+ should "list liked articles" do
+ assert_equal 2, Article.has_likes.count
+ assert_equal [@nytimes, @washington_post], Article.has_likes.alphabetical
+ end
+ end
+end
diff --git a/test/models/user_article_test.rb b/test/models/user_article_test.rb
new file mode 100644
index 00000000..864f3c0f
--- /dev/null
+++ b/test/models/user_article_test.rb
@@ -0,0 +1,10 @@
+require "test_helper"
+
+class UserArticleTest < ActiveSupport::TestCase
+ # Matchers
+ should belong_to(:user)
+ should belong_to(:article)
+
+# should validate_presence_of(:user_id)
+# should validate_presence_of(:article_id)
+end
\ No newline at end of file
diff --git a/test/models/user_test.rb b/test/models/user_test.rb
new file mode 100644
index 00000000..7210e847
--- /dev/null
+++ b/test/models/user_test.rb
@@ -0,0 +1,11 @@
+require 'test_helper'
+
+class UserTest < ActiveSupport::TestCase
+ # Matchers
+ should have_many(:user_articles)
+ should have_many(:articles).through(:user_articles)
+
+ should validate_presence_of(:first_name)
+ should validate_presence_of(:last_name)
+ should validate_presence_of(:password)
+end
diff --git a/test/sets/articles.rb b/test/sets/articles.rb
new file mode 100644
index 00000000..5764cfad
--- /dev/null
+++ b/test/sets/articles.rb
@@ -0,0 +1,17 @@
+module Contexts
+ module Articles
+
+ def create_articles
+ @nytimes = FactoryBot.create(:article)
+ @washington_post = FactoryBot.create(:article, title: "Top Story", author: "John Doe", score: 500, url: "washingtonpost.com")
+ @time = FactoryBot.create(:article, title: "Top Story 2", author: "Janet Doe", score: 400, url: "time.com")
+ end
+
+ def destroy_articles
+ @nytimes.destroy
+ @washington_post.destroy
+ @time.destroy
+ end
+
+ end
+end
diff --git a/test/sets/user_articles.rb b/test/sets/user_articles.rb
new file mode 100644
index 00000000..d4edbb1b
--- /dev/null
+++ b/test/sets/user_articles.rb
@@ -0,0 +1,20 @@
+module Contexts
+ module UserArticles
+
+ def create_user_articles
+
+ @jane_like1 = FactoryBot.create(:user_article, user: @jane, article: @nytimes)
+ @lily_like1 = FactoryBot.create(:user_article, user: @lily, article: @nytimes)
+ @frank_like1 = FactoryBot.create(:user_article, user: @frank, article: @washington_post)
+ end
+
+
+
+ def _user_articles
+ @jane_like1.destroy
+ @lily_like1.destroy
+ @frank_like1.destroy
+ end
+
+ end
+ end
\ No newline at end of file
diff --git a/test/sets/users.rb b/test/sets/users.rb
new file mode 100644
index 00000000..8dd14ea6
--- /dev/null
+++ b/test/sets/users.rb
@@ -0,0 +1,17 @@
+module Contexts
+ module Users
+
+ def create_users
+ @jane = FactoryBot.create(:user)
+ @lily = FactoryBot.create(:user, first_name: "Lily", last_name: "Cao", email: "lily@yahoo.com", password: "123456", password_confirmation: "123456")
+ @frank = FactoryBot.create(:user, first_name: "Frank", last_name: "Cao", email: "frank@yahoo.com", password: "123456", password_confirmation: "123456")
+ end
+
+ def destroy_users
+ @jane.destroy
+ @lily.destroy
+ @frank.destroy
+ end
+
+ end
+ end
\ No newline at end of file
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 92e39b2d..9029fd47 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,10 +1,15 @@
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
+require "minitest"
+require 'minitest/rails'
+require 'minitest/reporters'
+require 'contexts'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
+ include Contexts
# Add more helper methods to be used by all tests here...
end