====== A sample Rails application - mydiary ====== * We'll be working through the creation of the //mydiary// Rails app, a sample online diary application. ==== Starting with a blank rails app ==== * Run these commands in a Terminal. * pay attention to the comments marked with "#" cd work_dir # where work_dir is a directory of your choice mkdir rails # create rails directory cd rails rails new mydiary # This rails command will create a mydiary project directory. cd mydiary * All the newly-created directories in the ''mydiary'' directory will make up the //mydiary// rails web app. * app: Contains most of the Ruby source code and output templates directly associated with the app; most directly connected to the MVC components of the Web app. * app/controllers: Contains the controller files * app/helpers: Contains ruby source code files that provide methods that you can use from views * app/views: Contains output templates (views) for the app. * config: Contains configuration files for the app. * db: Used for database dumps, backups and migrations. In the case of apps that use SQLite, may contains the database file(s). * storage: (Newer versions of Rails) Contains database files from migration operations. * bin: Contains important scripts and command line tools used in constructing and deploying Rails apps. * The ''Gemfile'' in ''mydiary'' belongs to ''[[http://bundler.io|bundler]]'', which manages Ruby gems dependencies for your new rails project. * ''Gemfile'' can be customized in a text editor if needed to add custom gem dependencies to your project. ==== Database (SQLite3) Config ==== * See config/database.yml * ''database.yml'' is a [[http://en.wikipedia.org/wiki/YAML | YAML]] file with info about the database(s) that the app will use. * Note "development," "test" and "production" targets. ==== Using "Scaffolding" ==== * The Rails scaffolding mechanism generates default, generic code to provide a full set of basic Web app operations for any of your models. * These "Web app operations" are the //CRUD (Create, Retrieve, Update, and Delete)// operations and views on any database table * You can then build your own views and controller methods off of this basic scaffolding. * Run the following command in the Terminal: rails generate scaffold Entry title:string content:text * For our //mydiary// application, entries will initially solely consist of a title and some content. * There are other attributes (or "fields" in the database sense) that Rails will add by default to the underlying database table, such as //id// (a unique numeric identifier). * A directive is also added into the default migration (database creation) code to create timestamp columns: //created_at// (a timestamp of when the record/associated object was created), and //updated_at// (a timestamp of when the record/associated object was amended last). * Because of this automation, it is only necessary to specify the two custom, additional attributes to the scaffold generator to get things going. ==== Migrations to create db tables ==== * We're building an online diary. * **Our model is the diary entry that we'll call //Entry//.** * The scaffolding step above defined our database table and its fields to reflect the structure of our model. * **In Rails, models and database tables generally have a direct relationship.** * If you have a database table called //entries//, then this will be directly related to a model class in your Rails app called //Entry//. * Rails naming conventions * expects objects from a singular class name to be saved to a database table which is the plural of the class name * Example: expects objects from the class Person to be saved to a database table named people (not "persons") * Rails has a //pluralization engine// to figure out what object maps to what table. * Database tables can be created directly with SQL or other db frontend if desired. * But the preferred way is to use //migrations//. * Migrations are mostly database-independent and allow you to manage changes to your db's schema over time and give you the ability to "roll back" to older versions of your schema. * See the migration script that was automatically generated by the previous ''generate scaffold'' command. It will be a .rb script found in ''db/migrate'' that looks something like this: # .rb script in the db/migrate directory of the rails project # class CreateEntries < ActiveRecord::Migration[8.0] def change create_table :entries do |t| t.string :title t.text :content t.timestamps end end end * By default, table names are pluralized and model names are singular. * Example: table name //entries// & model name //Entry// * Rails works out the difference between singular and plural names automatically. * Use the Rails ''rake'' command to create the database tables using a migration. * ''rake'' tasks are administrative tasks associated with your app that are managed by the ''rake'' tool. * This tool is analogous to the ''make'' command used for C/C++ development. * Notice the ''Rakefile'' in the root of your app directory structure. * After running the following ''rake'' command, you should notice a SQLite database file ''development.sqlite3'' in the ''db'' project directory. * Note: In newer versions of rails, ''development.sqlite3'' will be found in the ''storage'' project directory. ### Recommended in rails 4+ (bundle forces use of the rake version specified in Gemfile) ### bundle exec rake db:migrate ### If you don't have the 'bundle' command after installing rails (Windows), then just run ### ### rake db:migrate ==== Configure web app access host authorization (SP23, Rails 6+) ==== * Per https://www.fngtps.com/2019/rails6-blocked-host * Edit the ''config/environments/development.rb'' file and add the following code somewhere inside the ''Rails.application.configure do'' block: # Authorize access from any host: config.hosts.clear ==== Start simple web server and view current web app ==== rails server -b 0.0.0.0 -p ### = some value >= 3000 ### * Running the above ''rails server...'' command should start a simple development web server to serve your app. * Start a web browser on the same host that the rails server is running on, and browse the URL and port that the web server provides. * For example, if simple web server output includes "0.0.0.0:3010", browse the rails app using the URL http://localhost:3010 * Also try browsing the subpage that corresponds with the controller that was created by the Scaffolding step: * See ''app/controllers'' * e.g., http://localhost:3010/entries * The default "route" for a new Rails application is just a generic Rails web page that is displayed. * The generic Rails page is generated by the "root route" of your mydiary app. * To replace this page, we'll set up a different root route later. * To begin using the //entries// controller, you must browse the following 'route': http://localhost:/entries # e.g., http://localhost:3010/entries * As you browse your app, the simple web server that you are running your rails app on will be throwing output in the Terminal window in which you started it. * The output includes SQL queries that are used to extract needed information from the app's sqlite3 database. ==== Controllers ==== * This part describes what the scaffolding step generated, how it works, and how it can be extended with custom methods and views. * Controller actions * Within the app's directory structure, see ''app/controllers/entries_controller.rb'', which contains a class definition for the ''Entries'' controller and its methods. * Rails controllers are implemented as classes that inherit from more generic ''ApplicationController'' and ''ActionController'' classes. * The ''index'', ''new'' and ''create'' methods all make calls to methods of the ''Entry'' class, such as ''Entry.new'' and ''Entry.all''. * ''Entry'' is the model class, which inherits methods from the generic ''ActiveRecord'' (or ''ApplicationRecord'') class that allow navigating and finding data in the database table for the ''Entry'' model. * For example, ''Entry.find(:all)'' returns all records as an array of objects from the ''entries'' database table. ==== Views and Embedded Ruby ==== * The //views// associated with some controller methods are located in ''app/views/entries''. * The ''.erb'' files in ''app/views/entries'' are HTML template files containing //Embedded Ruby//. * These files define the views of a Rails web app. * Embedded Ruby code sections are delimited by ''<% and %>'' * See ''_entry.html.erb'' and ''show.html.erb''. * Embedded Ruby filenames that begin with underscore are called [[https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials|partials]] and are used by other views through calls to the ''render'' method. <%### _entry.html.erb (partial) ###%>

Title: <%= entry.title %>

Content: <%= entry.content %>

<%### show.html.erb ###%>

<%= notice %>

<%### Use _entry.html.erb partial ###%> <%= render @entry %>
<%= link_to "Edit this entry", edit_entry_path(@entry) %> | <%= link_to "Back to entries", entries_path %> <%= button_to "Destroy this entry", @entry, method: :delete %>
* ERB files may contain different forms of the <% tag. The differences between them are explained at https://stackoverflow.com/questions/7996695/what-is-the-difference-between-and-in-erb-in-rails ==== Creating a New Action and View ==== * Try adding a new method manually to the Entries controller (in ''app/controllers/entries_controller.rb''): # Defines a new method and, therefore, a new controller action. # Similar to index, but sorted in descending (DESC) order # by creation time. # GET /entries/view_all def view_all @entries = Entry.order('created_at DESC').all end * If we want to use this new controller method, it is necessary to tell the Rails routing system (the system that sends incoming request to the correct controllers and methods) that you have defined a new method that may be called on the entries controller. * We need to edit the ''config/routes.rb'' file, and add some statements just **BEFORE** the following line (2nd line of file): resources :entries * The statements to add are: # Redefining root route as the view_all action: root 'entries#view_all' # Defining this route to use new view_all controller action: get '/entries/view_all' => 'entries#view_all' * After that, attempting to use the new //view_all// method at http://localhost:/entries/view_all should result in an error message in the browser window stating something like //"Template is missing ... Missing template ..."// * This means that the associated view for //view_all// still needs to be created in ''app/views/entries''. * Edit a new file ''app/views/entries/view_all.html.erb''. * Put the following Embedded Ruby code in ''view_all.html.erb'', and then try using the //view_all// action again in the web browser: <%# view_all.html.erb: Define the view for the view_all controller action. %> <%= link_to 'Add New entry', controller: 'entries', action: 'new' %>
<% @entries.each do |entry| %>

<%= entry.title %>

<%= entry.content %>

Posted at <%= entry.created_at %>

<%= link_to "Show this entry", entry %>


<% end %>
* See the current mydiary app routes by running the following command in a Terminal: rails routes * Retry the view_all controller method by visiting http://localhost:/entries/view_all. * Try the new root route for your app at http://localhost:. ==== Modify Views ==== * Add a "View all entries" link to the //show// view (''app/views/entries/show.html.erb''). * Replace the current ''index'' view with the following ERB code: <%# index.html.erb: Tabular index view with all actions %>

Listing Entries

<% @entries.each do |entry| %> <% end %>
Title Content
<%= entry.title %> <%= entry.content %> <%= link_to 'Show', entry %> <%= link_to 'Edit', edit_entry_path(entry) %> <%= button_to 'Delete', entry, method: :delete, data: { turbo_confirm: 'Are you sure?' } %>

<%= link_to 'New Entry', new_entry_path %>
----