---
# System prepended metadata

title: Model part
tags: [model, astro camp, Ruby on Rails]

---

## URL shortner

[:bulb:Youtube](https://www.youtube.com/playlist?list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc)
>1~4 build link model, url shortener model with RSpec
>5 build link controller with RSpec
>6 build the controller Home (homepage we land on:) 
>7 validates user entry url format[skipped]
>8 make the shortlink in link.rb and create.js.erb
>9 styling with bootstrap[skipped]
new a project :)
make sure webpacker, net-smtp, RSpec is bundled in the Gemfile.

:bulb: [RSpec](https://github.com/rspec/rspec-rails/tree/6-0-maintenance)
---
# Model part
----
# Write url shortener model and spec 
> Go to `spec`, and create a folder [services], in the file you create a `urlshort_spec.rb`
> > In here you will create the TDD Tests, there are 3 logic tests in this spec.
```ruby!
require 'rails_helper'

RSpec.describe Urlshort do

  it "shortens a URL to 7 character like bit.ly" do
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    expect(urlshort.lookup_code.length).to eq(7)
  end

  it "gives each URL its own lookup code" do
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    code1 = urlshort.lookup_code
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWabcd"
    urlshort = Urlshort.new(url)
    code2 = urlshort.lookup_code
    
    expect(code2).not_to eq(code1)
  end
  
  it "always gives the same lookup code to the same URL" do
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    code = urlshort.lookup_code
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    same_code = urlshort.lookup_code

    expect(code).to eq(same_code)
  end
    
  it "it generates a Link record with a unique lookup code" do
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    link = urlshort.generate_short_link
    expect(link.valid?).to eq(true)
  end
  
end

```



> Go to `app`, and create a folder [services], in the file you create a `urlshort.rb`
> 
> > In here you will create the class and functions that in spec file you use 
```ruby!
require 'digest/sha2'
class Urlshort
  attr_reader :url, :link_model

  def initialize(url, link_model = Link)
  @url = url  
  @link_model = link_model
  end

  def lookup_code
    Digest::SHA256.hexdigest(url)[0..6]
  end

  def generate_short_link
    link_model.create(url: url, lookup_code: lookup_code)
  end
  
end
```

---
# Write link model and specs



> Go to terminal, 
```shell!
rails g model Link lookup_code:string url:string 
```
```shell!
rails db:migrate
```

> Go to `spec/models/link_spec.rb`
> > In here you will create the TDD Tests, valid if there are url and lookup code, invalid if lost either of them.
```ruby!
require 'rails_helper'

RSpec.describe Link, type: :model do

  it "is valid if always has a URL and always has a lookup code" do
    link = Link.new(
      url:  "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc",
      lookup_code: "1234567"
    )
    expect(link.valid?).to be(true)
  end

  it "is invalid if no url" do
    link = Link.new(
      lookup_code: "1234567"
    )
    expect(link.valid?).to be(false)
  end

  it "is invalid if no lookup code" do
    link = Link.new(
      url:  "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    )
    expect(link.valid?).to be(false)
  end

  it "is invalid if lookup code is not unique" do
    link = Link.new(
      url:  "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc",
      lookup_code: "1234567"
    )
    link.save
    link2 = Link.new(
      url:  "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWabcd",
      lookup_code: "1234567"
    )
    expect(link2.valid?).to be(false)
  end

end
```

> Go to `models/link.rb`
> > In here you will create the class to pass your test.
```ruby!
class Link < ApplicationRecord
  validates_presence_of :lookup_code, :url
  validates_uniqueness_of :lookup_code
end

```

:::success
:bulb:Dependency injection:
in the model.rb, you can refer to another class with capital, like the `Link` in `urlshort.rb`
:::

:::success
:bulb:Lookup_code method logic
1. Get a lookup code
2. Check with the loop if it already exist
3. Break out of the loop if it does not exist
4. If not, get another lookup code
:::

> Go to `urlshort.rb`
> 
> > In here you will create the method based on above logic
```ruby!
def lookup_code
    i = 0
    loop do 
    code = get_fresh_code(i)
    break code unless link_model.exists?(lookup_code: code)
    i = i + 1
    end
end

private
#make a get_fresh_code method so you can change your algorithm of generating it anytime you want if not Digest::SHA256
def get_fresh_code(i)
    Digest::SHA256.hexdigest(url)[i..(i + 6)]
end
```

:::success
:bulb: get_fresh_code method logic
if you go to rails console, and type in SecureRandom, this is a library that get a unique user id.
>SecureRandom.uuid, you can get random charaters, so you can use this instead od Digest::SHA256 for generating random characters for the short link
:::

![](https://i.imgur.com/8Zm7tig.png)

> Go to `urlshort.rb`
> 
> > In here you will create the method based on above logic

```ruby!
def lookup_code
    loop do 
    code = get_fresh_code
    break code unless link_model.exists?(lookup_code: code)
    end
end

private
#use library SecureRandom
def get_fresh_code
    SecureRandom.uuid[0..6]
end
```

> Go to `urlshort_spec.rb`
> Delete this spec because it no longer serves the purpose

```ruby!
it "always gives the same lookup code to the same URL" do
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    code = urlshort.lookup_code
    url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
    urlshort = Urlshort.new(url)
    same_code = urlshort.lookup_code

    expect(code).to eq(same_code)
  end
  ```
  
  ----
  # Controller part  
  ---
  # Link Controller
  > Go to `spec`, and create a folder [controllers], in the file you create a `links_controller_spec.rb`
```ruby!
require 'rails_helper'

RSpec.describe LinksController, type: :controller do
  it "can shorten a link provided by the user" do
  url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
  post :create, params: { link: {url: url}}
  end
end
```


:heavy_check_mark: checking terminal `rspec`, know that you need to create controller
```shell
rspec
```

> Go to `controllers`, create file `links_controller.rb`
```ruby!
class LinksController < ApplicationController
end
```


:heavy_check_mark: checking rspec, know that you need to create routes => No route matches {:action=>"/links

> Go to `routes.rb`
```ruby!
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  post "/links" => 'links#create'
end

```

:heavy_check_mark: checking rspec, know that you need to create `create` action in link controller

>Go to `links_controller.rb`
```ruby!
class LinksController < ApplicationController
    def create
    end
end
```

>Go to `links_controller_spec.rb`
```ruby!
require 'rails_helper'

RSpec.describe LinksController, type: :controller do
  it "can shorten a link provided by the user" do
  url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"
  post :create, params: { link: {url: url}}
  #assigns: looking for instance variable that already has been set
  link = assigns(:link)
  expect(link.url).to eq (url)
  expect(link.valid?).to be (true)
  expect(link.lookup_code.length).to eq (7)
  end
end
```
:heavy_check_mark: checking rspec, know that you need to add gem `rails-controller-testing` in the gemfile, and run `bundle` in terminal

:bulb: `assigns`: looking for instance variable that already has been set

>Go to `links_controller.rb`
```ruby!
class LinksController < ApplicationController

  def create
    url = link_params[:url]
    urlshort = Urlshort.new(url)
    @link = urlshort.generate_short_link
  end

  private
  def link_params
    params.require(:link).permit(:url)
  end
endx
```

---

# Views part

>Go to `views`, create a folder `links`, and create a file `create.js.erb`


>Go to `links_controller_spec.rb`
>Add request.env["HTTP_ACCEPT"] = "text/javascript"
>Add expect(response).to render_template('create')
>Add expect(link.persisted?).to be (true)
```ruby!
require 'rails_helper'

RSpec.describe LinksController, type: :controller do
  it "can shorten a link provided by the user" do
  url = "https://www.youtube.com/watch?v=ZBs1a6Y25bY&list=PLjItgYqIzJ9WGy9WMf-44DXHB_7aWwJMc"

  request.env["HTTP_ACCEPT"] = "text/javascript"
  post :create, params: { link: {url: url}}
  #assigns: looking for instance variable that already has been set
  link = assigns(:link)
  expect(link.url).to eq (url)
  expect(link.valid?).to be (true)
  expect(link.lookup_code.length).to eq (7)
  expect(link.persisted?).to be (true)
  expect(response).to render_template('create')
  end
end
```
---
>Go to terminal, we are going to build a page!
>This will set up a controller, a page, and routes for us
```shell!
rails g controller Home index
```
>Go to routes, change the get to root to:
```ruby!
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  # get 'home/index'
  root to:'home#index'
  post "/links" => 'links#create'
end

```
:heavy_check_mark: you should be able to see this 
![](https://i.imgur.com/k9uKAkn.png)

>Go to `home_controller.rb`

```ruby!

```
>Go to `app/views/home/index.html.erb`
>let's build a form for the url shortener!

```erb
<h1>Destly- A url shotener</h1>

<%= form_with model: @link, remote: true do |form| %>
<%= form.text_field :url %>
<%= form.submit "create shortlink"%>
<% end %>
  
```

:heavy_check_mark: You should be able to see this 
![](https://i.imgur.com/sccOV3z.png)


>Go to `create.js.erb`
```javascript!
var lookupCode = "<%= @link.lookup_code %>;"
console.log(lookupCode)
```

:::success
:bulb: so far all the entry can generate a 7 character url short, we need gem pry-rails, so add that in dev `Gemfile` and `bundle`
:::

>Go to `links_controller.rb`
>Add binding.pry
```ruby!
class LinksController < ApplicationController

  def create
    url = link_params[:url]
    urlshort = Urlshort.new(url)
    @link = urlshort.generate_short_link
      binding.pry
  end

  private
  def link_params
    params.require(:link).permit(:url)
  end
endx
```

---
# Render the short link

:heavy_check_mark: let's add the short link to the home page

>Go to `index.html.erb`
```htmlembedded!
<h1>Destly- A url shotener</h1>

<%= form_with model: @link, remote: true do |form| %>
<%= form.text_field :url %>
<%= form.submit "Create Shortlink"%>
<% end %>

<div id="url-shortlink">
</div>
  
```
>Go to `create.js.erb`
```javascript!
var lookupCode = "<%= @link.lookup_code %>;"
console.log(lookupCode)

var element = document.getElementById("url-shortlink")
element.innerHTML = lookupCode
```

---
# Redirecting to the original url
>Go to spec and create a folder `requests`, create a file `link_redirect_spec.rb`
