Rails Portfolio Project – Recipe Manager

Recipe Manager was built using Ruby on Rails framework and Bootstrap. All users can safely signup, login and logout. I created authentication and authorization methods without using any gem. Recipe Manager include a third party signup/login via Github thanks to the OmniAuth gem.

Users can check the recipes, rate and comment them but only admin users can create and edit the recipes and the categories. The user can browse recipes by name and author and all the comments by author and recipe. The app include reasonable validations for its forms and an ActiveRecord scope method to get the newest recipe that can be seen in its own URL, in this case: http://localhost:3000/newest_recipe.

My models:

User

  • Has many recipes
  • Has many comments
  • Has many ratings
  • Attributes: name, email, password_digest, admin, and finally I added uid and provider for the signup/login with OmniAuth.

Recipe:

  • Belongs to a user
  • Has many recipe_ingredients
  • Has many ingredients through recipe_ingredients
  • Has many recipe_categories
  • Has many categories through recipe_categories
  • Has many comments
  • Has many ratings
  • Attributes: name, cooking_time, servings, directions, date created

Ingredient

  • Has many recipe_ingredients
  • Has many recipes through recipe_ingredients
  • Attributes: name

RecipeIngredient

  • Belongs to recipe
  • Belongs to ingredient
  • Attributes: quantity.

Category

  • Has many recipe_categories
  • Has many recipes through recipe_categories
  • Attributes: name

RecipeCategory

  • Belongs to Recipe
  • Belongs to Category

Comment

  • Belongs to user
  • Belongs to recipe
  • Attributes: content

Rating

  • Belongs to user
  • Belongs to recipe
  • Attributes: score

Generate my new application:

Generate my models with their associations and create my db:

*Adding :default => true to boolean in existing Rails column

Implementing a sign-in, login and logout functionality:

In order to encrypt the passwords I will use the gem bcrypt, include “has_secure_password” in my User model and have a password_digest attribute in my users table.

Sign in with Github

  • Add to your gemfile the OmniAuth gem and the provider-specific OmniAuth gem, in this case “omniauth-github”, and run bundle install.
  • Create a file named config/initializers/omniauth.rb. I will contain the following:
  • Create an application in Github.

[Settings/Developer Settings/OAuth Apps/Register a new OAuth application]

In homepage url enter: https://localhost:3000/

In the Authorization url: http://localhost:3000/auth/github/callback

After creating the app you get a client ID and a Client secret.

  • Add dotenv-rails to your Gemfile and run bundle install.
  • Create a file named .env at the root of the application and add your Github credentials.

  • Add .env to your .gitignore file.
  • Create a link to login via Github
  • The User model needs these attributes, all strings: name, email and uid, that it is the user’s ID on Github. *I added a provider attribute later on.
  • Create the route where Github will redirect users in the callback phase of the login process.
  • And in our Sessions controller we find or create the user who logging via github.
  • In our User model:

I want to add some helpful links:

Sinatra Portfolio Project

I would like to make a Content Management System for my daughter’s nursery. They keep the daily information about the children in notebooks full of very easy to lose post its and hope this app can help with it. The daily information will be referred to as activities.

The app provides a database and web interface for users to:
* Sign up, log in or log out securely as a nursery staff or parent.
* Nursery staff can create, read, update, and delete (CRUD) an activity.
* Parents can read only the personal profile page of their children.
* User inputs are validated.
I am using Sinatra to build the app, extended with Rake for working with a SQL database using ActiveRecord ORM.
Sinatra is a Domain Specific Language implemented in Ruby that’s used for writing web applications.
My first steps to create the app are:

-Create a repository in Github.

-The project structure following the MVC conventions.

-Set my environment and my config.ru. I am following the Modular Sinatra Pattern and having a config.ru file is one convention of it. The purpose of config.ru is to detail to Rack (Sinatra gem is built on top of Rack) the environment requirements of the application and start the application, mount my application controllers. Rack is a convention, or specification, that lets web applications “plug” into a common interface for handling HTTP requests. To actually serve requests, we need a web server — something that can translate HTTP requests into Ruby calls. My gem ‘thin’ is a web server that do this job.

-Create my Gemfile with all gems that I am going to run trough Bundle, which is set up in my environment file.

Here are the gems I want to use:

Sinatra: to create my web application.
activerecord: it is my Object Relation Mapper, links my Ruby models with rows in my database table. It is a gem to have access to the database mapping and associations.

sinatra-activerecord: it gives me access to some Rake tasks.
rake: short for “ruby make”, it is a package that lets me quickly create files and folders, and automate tasks such as database creation.
require_all: for a simplier way to load code.

sqlite3: sqlite is a library that implements a SQL database engine. sqlite is my database adapter gem.

thin: it is a web server.

shotgun: this is my development server.
pry: my debugger.
bcrypt: it is a hashing algorithm for passwords.
tux: it gives me an interactive console that pre-loads my database and
ActiveRecord Associations.
rake: great gem to create actions and tasks, for example a pry console to test the models and the database. It needs a Rakefile in the root directory of the app with the environment specified and requiring ‘sinatra/activerecord/rake’.
Model classes

I think that I will need at least 3 models, User, Activity and Student.

1. User: stores user attributes, including:
* Username
* Email
* Password (Secured with Bcrypt hashing algorithm)
* Nursery_staff, a boolean value to indicate if a user is a nursery staff
2. Activity: stores activity attributes, including:
* Date
* User_id, to associate Activity to User
* Student_id, to associate Activity to Student
* Breakfast
* Morning Snacks
* Lunch
* Afternoon Snacks
* Sleep
* Nappies
* Comments
3. Student: stores student attributes, including:
* Name
* User_id, to associate Student to User
* Key person
* Room
Model Associations
User has many students and has many activities
Student has many activities and belongs to a User (parent).
Activity belongs to a Student and belongs to User.

Is it works?

I am using Visual Studio Code and after creating the structure of my project with very basic info I tried to run shotgun and was getting the following error:

I had a server running in the background. After trying a lot of different things I could stopped it with the following I found in StackOverflow:

Ran lsof -i :9393 (9393 is the port I ran it on).

I killed the process using kill -9 *pid*. My pid was: 58152.

When I ran lsof -i :9393 again, nothing showed up.

I then ran shotgun and everything works fine now.

Model Validations
You can find all info about validations here.
I will use in my controllers the methods save and update, which trigger validations for themselves and will save the object to the database only if the object is valid.
I will use some helpers to validate that some specified attributes are not empty. I want to make sure that a user gives an username and an email and that an activity has a date and a student has a name at least. I will validate the uniqueness of the email too.

 

 

 

 

 

 

Authentication system

Enable sessions in your AppController and use the gem bcrypt. Make sure your user model has_secure_password and that on your users table you store the password like password_digest“.

 

 

 

 

Protecting the session secret by Martin Fowler:

I changed my configuration to look like this:

 

 

 

 

The password fails safe to a secure hex value if SESSION_SECRET is unset.

Database

Create a Database folder (db), run your migrations to create your tablesI have created one table for the students, another table for my users and a last one for the activities.

I run:

rake db:create_migration NAME=create_students

rake db:create_migration NAME=create_users

rake db:create_migration NAME=create_activities

Then modify my migration tables according to my needs.

And finally run rake db:migrate, and all your tables will be added.

Play with my models and the database.

I run tux in my console but I got the following error

config.ru does not support require_relative so I changed my link and everything works fine!

Now it is time to be more creative and design my routes in the controllers and my views. I am using Bootstrap.

To check this project follow the link.

Programming my classes

After all the previous steps now I have pretty clear how I am going to structure my code.

I want a CLI that displays art exhibitions and gives to the user more info  about the one it chooses. I am going to scrape from a public website all the info concerning the art events (Artlyst).

I want three classes: a CLI, an exhibition one and a scraper.

This three classes will live inside a lib folder.

I finally changed the CLI class name for artify in case I want to publish my gem in RubyGems.org. So I have threes classes: artify, exhibition and scraper.

Let’s start coding!

To check my code go to my Github repository.

After programming my classes I require them inside the environment.rb file.

And that’s it!

If you want to play with this gem please check this link: readme page.

Hope it was helpful!

The executable file

I create a folder bin and create inside of this folder an executable file called in this case as the gem, ‘artify’.

When I type: $ruby bin/artify,  everything run but I don’t want this. I want that the user from the bash can type: $ ./bin/artify.

So I type:

$ cd bin/

$ ls -lah

Ad I can see that artify doesn’t have executable permissions. To change this I type:

$ chmod +x artify

$ ls -lah

This executable file is not a ruby file so I have to include at the top the following:

#!/usr/bin/env ruby

Then I require our environment file to use all the gems already installed.

In this file I want to run for my still imaginary CLI class. I want to write here something like: CLI.new.run.

So, at this point, I know I want a class CLI, where I am going to build all the methods and keep all data to run my CLI.

I finally changed this name for ‘artify’ in case I want to publish it in RubyGems.org.

How to build a CLI Ruby Gem

Steps followed:

  • Planning my gem.

I would like to build a command line interface gem where I can check art exhibitions and get more info about the ones I liked.

How it would work:

– User types “exhibitions”.

– And ‘Artify’ will show a list of exhibitions like this:

Exhibition title – where

dates

-Which exhibition do you want to learn more about?

-And it will display more info about the exhibition selected.

Walkthrough video

Artify Environment

Setting my own environment: Visual Studio Code, Git and Bundler

First, I create a repository in Github.

Then, I create a new folder in my laptop where I want to clone the repository.

I open this folder in my VS code and open the terminal (ctrl + `).

I run: git clone + the https from the repository.

When I make changes:

First, I stage this changes.

Second, I write commit message and commit.

Third, I push my changes.

Installing Bundler

First I make sure I have the latest version of RubyGems. I run in the VS code terminal $gem update –system

Then I instal Bundler: gem install bundler but I got an error message

I don’t have write permissions. I googled this error output and I am trying something  I found in stack overflow: sudo gem install bundler. I gave the personal password for my computer and it worked!

The purpose of sudo is to execute the command given to it with root privileges.

Adding Gems to gemfile

I created a new file gemfile and I am adding my gems to it, bundler.

‘~> 2.0’

~> is called “Twiddle-Wakka.” '~> 2.0' means any minor version above 2.0,  2.1, 2.2, 2.3 … and 2.9 included would work (including patches); but version 3.0 wouldn’t work because it indicates a new major version.

The second specification '>= 2.0.1'means any version greater than or equal to 2.0.1. Both specifications have to be true, so this gem couldn’t use version 2.0because it’s lower than 2.0.1.

My Gemfile.lock file

I run in the VS code terminal $bundle installAutomatically a file was created in my directory: Gemfile.lock

Reviewing my installed gems

I run in the VS code terminal $gem list

Checking where my Bundler is

I run in the VS code terminal $bundle show [bundler]

Adding Gemfile and Gemfile.lock to my repository

I run in the VS code terminal $git add Gemfile Gemfile.lock.

I could staged the changes in my VS code, they do the same thing that the git add command.

I check my Github repository….and it didn’t work.

Ok! I forgot to push my changes, so I ran: $git push origin master

Done! I got my two new files in my repository.

Now, I want to install Nokogiri

Installing Nokogiri

I run in the VS code terminal $sudo gem install nokogiri

And after the installation was successfully  I add my new gem to the Gemfile

I check if my changes are in Github and…no! They are not! Why? BecauseI forgot to push them.

My work flow in VSC:

Save changes – stage changes – commit changes – push changes

Installing pry

I run in the VS code terminal $sudo gem install pry

And then I add the new gem to the Gemfile

And I don’t forget about my VSC workflow! Save, stage, commit and push.

Installing ‘open-uri’

I was trying to install it and got this error:

I googled it and…

OpenUri is part of Ruby standard library, I just need to require it.

After all this installations I checked my Gemfile.lock and I couldn’t see anything about nokogiri or pry. I run $bundle install and my Gemfile.lock is updated.

Installing ‘colorize’

I run in the VS code terminal $sudo gem install colorize

I add my new gem to the Gemfile.

And run $bundle install to update my Gemfile.lock.

 

I open my Gemfile and do this with my pry gem:

This is Group Syntax and I am specifying the environment where it is going to be used pry. I am going to change it for a shorter syntax, the hash syntax: gem ‘pry’, :group => :development

After installing all my gems I need to require them where I am going to use them.

config/environment.rb

Now, I create a folder config and a file environment.rb inside. In this file I will be loading, requiring, all of my app’s dependencies like my gems.

With this code all the gems that are inside Gemfile are required.

I need to require ‘open-uri’ and ‘colorize’ too in my environment.

What I have learned so far about OO Ruby

From what I’ve understood Object-Oriented programming is very popular and it has lots of advantages. The programmer may have a closer feeling of working with real-life entities due to the multitask nature of the objects in OOP. From my point of view OOP is a good way to keep all your code organise. Each object contains its data and functionality within its scope and if its methods are written in an abstract way they can be reused for other objects or for a different project.

I am still working in the OO Ruby section and it is taking me some time to understand all the concepts and why or when to use them. Here it is what I have learned so far:

Creating a class

Class names begin with capital letters because they are stored in Ruby constants. If our class name contains two words, the name should be CamelCased.

Instantiation

Every time we want to represent a new individual in our program we should instantiate an object of that type (Dog.new). We call these individuals instances.

Instances variables

An instance variable represents a singular property of an individual, of an instance. This variable is accessible in any instance method in a particular instance of a class. The name of the instance variable is written with a @ at the beginning of the word: @instance_variable_name.

Instance methods

We call the methods defined within the object’s class instance methods because they are methods that belong to any instance of the class.

They provide a mechanism for an object to expose to the outside world its data. They can manipulate the internal state of an object and provide some functionality for an individual object too.

Attribute readers, writers and accesors

Our object’s instances can have attributes, like a name or hair colour in the case of our Dog class. In order to set these attributes we have to use a writer and in order to have these data available in the outside world we have to have a reader. It is very important to use an instance variable (@name), otherwise the getter method couldn’t read the data that we set in the writer method because it is out of scope.

This is the explicit method for the attribute @name. There is another faster way to do it using a macro: the attr_writer, the attr_reader and the attr_accessor that does the work of the two previous macros. In Ruby a macro is like a method that instead of returning a Ruby datatype returns more Ruby code. The implementation of macros is considered metaprogramming.

When to use attr_accessor vs a custom reader/writer.

The attr_accessor macro will provide a naive interface for reading and writing to an instance variable. Anytime we need something conceptually different or more complex, we should build our own reader or writer.

Self

Whenever an object needs to refer to itself, the keyword “self” is used.

In an instance method, self will always refer to the instance itself and in a class method, self will always refer to the class itself.

Class variable

Class variables are for properties that belong to the entire class. A class level variable can be used for example for instance memoization, for keeping track of all instances of a class or for collaborating data. The name of the class variable is written with a double @ at the beginning of the word: @@class_variable_name.

#self.all is a class method for reading data stored in the class variable @@all. This is a class reader, very similar to an instance reader method that reads an instance property.

Class methods

With these methods we can access any class variable. A class method operate on the entire class and expose the class’ scope to the rest of our program. They don’t have access to the instance scope.

The most common class methods are:

Class finders

A class finder method returns existing instances.

Custom class constructors

A class constructor is any class method that instantiates an instance of the class.

Class operators

A class operator is perfect to manipulate any class level data.