Code Monkey home page Code Monkey logo

sinatra-integrating-models-walkthrough's Introduction

Integrating Models Sinatra Code-along

Overview

In previous lessons, we've applied logic to data provided by the user directly in our application controller. While this works, it does not follow the principle of 'separation of concerns' - our files should do one thing and one thing only. In this code-along lesson, we'll learn how to move the logic to a model in a Sinatra application. By doing this, we'll create our first full Model-View-Controller application in Sinatra!

Learning Goals

  • Create a model in Sinatra
  • Link to created models from the application controller
  • Create instances of a model in the application controller
  • Display data from instances of a model in a view

Setup

We'll use input from a form to create an instance of a model, and then send that instance back to a view to be displayed to the user. As an example, we're going to create a web application that analyzes a block of text from the user - showing the number of words, most common letters, and least common letters to us.

To code along, fork and clone this lab. Run bundle install to make sure all of your dependencies are installed.

Starter Code

Let's take a closer look at the starter code. Run shotgun to make sure that your application can run.

Routes

  • The controller has two routes:
    • get '/' do, which renders the index.erb page.
    • post '/' do, which receives the form data through params and renders the results page.

Creating a model

We could analyze all of the data from params[:user_text] in our application controller, but our route would get messy very quickly. Instead, let's create a new class inside of our models directory that will take care of the analysis of the text. In your models directory, open the file called text_analyzer.rb.

We're not going to go deeply into creating models in this lesson, as you've covered it in depth in our unit on object oriented programming. Instead, paste the following code in to your text_analyzer.rb file:

class TextAnalyzer
  attr_reader :text

  def initialize(text)
    @text = text.downcase
  end

  def count_of_words
    words = text.split(" ")
    words.count
  end

  def count_of_vowels
    text.scan(/[aeoui]/).count
  end

  def count_of_consonants
    text.scan(/[bcdfghjklmnpqrstvwxyz]/).count
  end

  def most_used_letter
    s1 = text.gsub(/[^a-z]/, '') # gets rid of spaces
    arr = s1.split('')
    arr1 = arr.uniq
    arr2 = {}

    arr1.map do |c|
      arr2[c] =  arr.count(c)
    end

    biggest = { arr2.keys.first => arr2.values.first }

    arr2.each do |key, value|
      if value > biggest.values.first
        biggest = {}
        biggest[key] = value
      end
    end

    biggest
  end
end

The model above has an initializer which takes in a string text, converts it to lowercase, and saves it to an instance variable @text. This instance variable is then used in the four instance methods, which provide information on the block of text in question. If we wanted to use this class on its own, we could do the following:

my_text = TextAnalyzer.new("The rain in Spain stays mainly on the plain.")
my_text.count_of_words #=> 9
my_text.count_of_vowels #=> 13
my_text.count_of_consonants #=> 22
my_text.most_used_letter #=> {"n" => 6}

In general our models are agnostic about the rest of our application - we could drop this class into a Command Line or Ruby on Rails app and it would function in the exact same way.

Using a model in the controller

To use the model we've created with our controller, we need to connect the two. In order to do this, we'll add a require_relative statement to the controller so that this file can use this new class.. At the top of app.rb, add require_relative "models/text_analyzer.rb". This now gives us the ability to reference the TextAnalyzer class and invoke its new method.

Now, let's take the data from params[:user_text] (in the post '/' do route) and feed it into a new instance of the TextAnalyzer class:

post '/' do
  text_from_user = params[:user_text]

  @analyzed_text = TextAnalyzer.new(text_from_user)

  erb :results
end

We can shorten this to:

post '/' do
  @analyzed_text = TextAnalyzer.new(params[:user_text])

  erb :results
end

We now have the instance of TextAnalyzer saved to an instance variable called @analyzed_text. This means that we can call it and its methods from the results.erb view that is being rendered, using erb tags!

Using instance variables in ERB

In our results.erb file, use erb tags to display the data stored in the @analyzed_text variable. Your end result should look something like this:

Full MVC

Congratulations! You've now created your first Sinatra app that uses a model, views, and a controller! You are taking user input in a form, sending it via params to the 'post' route where a new instance of the model is created using the data from the form. This instance is passed back to the view, where it is rendered using erb tags. Pat yourself on the back, this is a big milestone in your developer journey!

sinatra-integrating-models-walkthrough's People

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sinatra-integrating-models-walkthrough's Issues

description of post route in the lab's instructions differs from cloned lab content

When I cloned down the lab, the controller's post route had no 'results' after the slash. It just looked like this:

post '/' do
text_from_user = params[:user_text]
erb :results
end

But in the original description from the lab it says to expect a '/results' route:

"The controller has two routes:

get '/' do, which renders the index.erb page.
post '/results' do, which receives the form data through params and renders the results page."

spec issue with Integrating Models for a Full MVC

This was just an example cut-n-paste lab, running learn gave this:
bundler: failed to load command: rspec (/usr/local/rvm/gems/ruby-2.3.1/bin/rspec)
NameError: uninitialized constant Capybara::DSL

So I compared to previous and added to spec_helper.rb:
require 'capybara/rspec'
require 'capybara/dsl'

...and learn ran fine, I was able to submit.

Tiny wording change

in the paragraph it asks to create a new file called textanalyzer.rb but there is already a file in the models folder and it is formatted as text_analyzer.rb

could there a rewrite of this paragraph

I was wondering if a re-write of this section would help a bit.
it is introducing a new step/concept.


**Using a model in the controller**

```text
To use the model we've created with our controller, we need to connect the two. In order to do this, we'll add the require_relative to the controller to bring in the model code we've created. At the top of app.rb, add require_relative "models/text_analyzer.rb". This now gives us the ability to create new instances of the TextAnalyzer class from within our controller.

Now, let's take the data from params[user_text] (in the post '/' do route) and feed it into a new instance of theTextAnalyzer class:

```

missing line from spec_helper.rb

The following line is missing from the spec_helper file, which causes an rspec error when you try and run the tests.

require 'capybara/dsl'

Once I added it, everything seamed to work fine.

Duplicate models files

The ReadMe for this lab says: "In your models directory, create a new file called textanalyzer.rb." However, there is already a file in that directory called text_analyzer.rb (with underscore). That file has a comment at the top that says # Your TextAnalyzer model code will go here. The ReadMe goes on to say "Instead, paste the following code in to your textanalyzer.rb file," but then conflicts itself by instructing "At the top of app.rb, add require_relative "models/text_analyzer.rb" [with underscore]

Seems like the ReadMe should be updated to reflect using the existing file (with underscore) and not creating a new one or pasting anything into it.

Thank you.

rspec error

the test is trying to pull data from a hash
but the method stores the outcome as an array

Tests not passing

My tests were not passing after completing the walkthrough and during AAQ it was noticed that I needed to require 'capybara/dsl' \on line 4 of spec_helper.rb.

Sinatra Basics and UP -- gem 'thin' issue

Hi there!

I've noticed that the curriculum requires the gem 'thin' when we fork/clone and bundle install these labs starting at Sinatra Basics and moving forward. Looked into it, and I believe that gem 'thin' is not compatible for some reason with Mac OS Catalina and newer. Thin gem looks like it hasn't been updated with a newer version of '1.7.2' since 2017, and Catalina I believe came out in late 2018/early 2019. A lot of our cohort has had this issue as well when running bundle install if it requires gem 'thin'.

For clarity as well, I've tried bundling every version of 'thin' that there currently is on rubygems.org, but none have allowed the bundle install to complete or be utilized in any of the Sinatra Basics moving forward from the curriculum I've completed.

Thank you for your time!

Sincerely,
Moose Davis

Missing a space

Now, let's take the data from paramsuser_text and feed it into a new instance of the TextAnalyzer class:

paramsuser_text => params user_text || params[:user_text]

Can't Pass the Spec: describe 'POST /' do it "displays string results" do

describe 'POST /' do
it "displays string results" do
visit '/'

  fill_in(:user_text, :with => "Green Eggs and Ham")
  click_button "submit"
  expect(page).to have_text("Number of Words:4")
  expect(page).to have_text("Vowels:5")
  expect(page).to have_text("Consonants:10")
  expect(page).to have_text("Most Common Letter: G, used 3 times")
end

end

for the phrase "Green Eggs and Ham", there are 4 Vowels and 8 Consonants.

Cleaner implementation of most_used_letter

This is a refactored version of the most used letter method.

def most_used_letter
  no_spaces = text.gsub(" ", "")
  letters = no_spaces.split("")
  histogram = Hash[*letters.group_by{ |v| v }.flat_map{ |k, v| [k, v.size] }]
  histogram.max_by{ |k, v| v }
end

Fix Errors in README

Line 86 -- At the top of app.rb, add require_relative "models/textanalyzer.rb".

  • change filename to match -> At the top of app.rb, add require_relative "models/text_analyzer.rb".

Line 80 -- my_text.most_used_letter #=> ["n", 6]

  • output from the method is actually a hash not an array -> my_text.most_used_letter #=> {"n"=>6}

tests did not run

spec/spec_helper.rb was missing this line: require 'capybara/dsl'

Make the last segment a "do it on your own thing"

The segment on instance vars in ERB is kinda random. I'd say something like "now that we have our instance variable set, modify the erb to use it. It should look like this (screenshot) when you're done"

Testing issue

The rspec tests are using "Green Eggs and Ham" while the model is only counting lower case letters.

` def count_of_vowels
@text.scan(/[aeoui]/).count

end

def count_of_consonants
@text.scan(/[bcdfghjklmnpqrstvwxyz]/).count
end
`

`describe 'POST /' do
it "displays string results" do
visit '/'

  fill_in(:user_text, :with => "Green Eggs and Ham")
  click_button "submit"
  expect(page).to have_text("Number of Words:4")
  expect(page).to have_text("Vowels:5")
  expect(page).to have_text("Consonants:10")
  expect(page).to have_text("Most Common Letter: G, used 3 times")
end

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.