Linkit, a beginner link aggregator
Tags: webdev, portfolio, rails, beginners
Published: 2023-01-03 15:08:09


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tuz87h02mccwoxey83sh.png)
Linkit is a beginner project created using Rails 7, with extensive use of the Turbo technology, as well as the Devise gem for authentication. It also utilizes the handy [Link Thumbnailer](https://github.com/gottfrois/link_thumbnailer) and [Acts as Votable](https://github.com/ryanto/acts_as_votable) gems for generating thumbnails for link posts, and allowing votes on posts and comments, respectively. On the front end, Bootstrap is used to help make everything uniform, with some tweaking done with Sass. The logo and buttons (one being made with the help of a CC0 image), as well as the default avatar and thumbnails were all made by me, using GIMP!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhwb70pyucjgihkxsd9t.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6wedl7kfanly6aqarom.png)

On linkit, users can create new posts of four varieties: image, text, link, and video. Turbo is used to allow an inline form to replace the "new post" section with the respective form of each kind of post.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75sotyww39eeoun8odjw.gif)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jux0im2dexhys7cs7aoi.gif)

On any page that shows multiple posts, turbo frames are used to allow users to see a preview of the image used for a post:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yk80wgk6tniklqilq7w9.gif)

These posts can be tagged, and users can click on the individual tags to see all of the posts made with that tag. Users can follow the tags after their creation, and when a post with a followed tag is uploaded, users will be able to see it on their homepage. At any time, they can also simply see all posts from every tag by clicking "all posts" at the top of posts.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iebtr1z85xbrignul7s5.jpg)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/07990t09h8zhrem0j6lg.png)

Turbo is used once again when following a tag, the "follow" link is automatically replaced with an "unfollow", and the follower count is automatically updated in the sidebar:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i2h9lk7hul9h5m69kj99.gif)

Users can also vote on posts and leave comments on them. The comments themselves can also be voted upon, and comment replies can be left by other users as well.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3s48k867g70gmwyilypi.gif)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jaym7g0ocv06znc5pfmt.png)

Much of these interactions, and any user data related to them can found on the user's profile:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rwgcyl93tb4h3y5k60zv.png)

The user's posts, as well as comments they've left, can be seen on their profile. In the sidebar, their avatar and bio can be seen, as well as their accumulated likes for posts and comments. Lastly, you can see the tags they post in the most. If you were to visit your own profile, instead of seeing your own comments to the right, you would see a list of all the comments left on your posts.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfcp1d5lvhy9o1obwjcc.png)

Clicking on "Show comment replies" will allow you to asynchronously switch this section to show replies to comments you've left on any post.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kdjg6sp6zr7ndf0kzuq.gif)

Many of these features were done with RSpec tests written first to help in the process. For the sake of developmental testing, seeds were also created for the users, and most of the tags and posts shown in this demonstration.

Most of the backend magic happens inside the Posts controller. The "new post" buttons send different queries depending on the respective post type, then, in the controller, the respective form is rendered like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8k3n9408xld6yw92on95.png)

The commented section is how I originally did it, woof!

The index action works in tandem with the Tags controller to list posts depending on the scenario.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h566ggwudeai24z209sl.png)

When a signed in user is following some tags, the root path will always show posts from their followed tags, unless they click on "all posts", which submits an "all" query. They can search for tags outside of the top ones listed on the sidebar, and any tag matching their search will show up where the Top Tags usually are. If the user clicks on a tag, they're taken to its respective page, and all the posts with that tag are shown. Lastly, if the user isn't signed in, or is but doesn't follow any tags, the root path will default to simply showing all posts.

Trying to use the followed tags system caused some headaches for me when I tried to display posts that were only associated with those tags. The first attempt was to simply iterate through the tags, then display any posts that had that tag.

```ruby
@tags = current_user.tags
@tags.each do |tag|
  @posts = tag.posts
end
```

This seemed to work swimmingly at first! Until I followed a second tag, and the posts were only showing from the last tag followed. After trying many different things and googling, I was at a loss and had to ask my friend and quasi-mentor Ioan if he had a solution. He suggested this hack, with the warning that it wasn't the best, and that I should try to utilize the database using joins and not use Ruby to create a simple array.

```ruby
@posts = @tags.flat_map(&:posts)
```

Again, this worked well at first, until I tried to call a scoped method that sorted posts by the amount of likes they had, which returned an error since it wouldn't work on an array.

That's when I looked into the "joins" query to figure out a more database-geared solution, which is what we ended up with!

```ruby
@posts = Post.joins(:tags).
              where('tags.id IN (?)', 
              current_user.tags.select(:tag_id)).distinct
```

In the index view for posts, the @posts variable is sorted by a scoped method defined in the Post model file that sorts posts by likes. It's then iterated through and the list of posts you see on the is generated. The actual code for the listing of posts is delegated to a partial, since it's reused on user profiles and the pages for individual tags.

The controllers for Users, Tags, and Comments are all pretty straightforward, mostly only consisting of CRUD actions plus additional methods for following / unfollowing tags or liking / unliking posts and comments. The Users controller features the inverse of the above code for the post and tag joins, except it's called on Tag, so that the tags that the user follows can be shown on their profile.

That is the very brief overview of Linkit! The repo can be found  [here!](https://github.com/concreteguillotine/linkit). Fork it and run
```ruby
rails db:seed
```
to begin checking it out!