📜 ⬆️ ⬇️

Introducing Ruby on Rails (Part 2)

In the continuation of the article " The first acquaintance with Ruby on Rails " we will learn to work with the database, and create a directory of articles.
We learn how to write a plugin, try to use AJAX and consider some problems when deploying an application on a hosting.

Let's start with the database.



I work with MySQL, so installation examples will be for it.

Windows users need to download and install MySQL-5.0 .
')
Linux users (Ubuntu) are even easier:

  <code class = 'sh' lang = 'sh'> $> sudo apt-get install mysql-server-5.0 libmysql-ruby </ code> 


After installation, check that the server is running:

  <code class = 'sh' lang = 'sh'> $> mysqladmin ping -u root
 mysqld is alive </ code> 


It is time to create the necessary databases. You will need two of them - one for development and one for testing:

  <code class = 'sh' lang = 'sh'> $> mysqladmin create example_development -u root
 $> mysqladmin create example_test -u root </ code> 


Now let's look at the config/database.yml file. Here are the parameters for connecting to the database. Usually nothing needs to be changed; by default, mysql creates the root user with all rights and without a password.

It remains to test the application connection to the database. Go to the folder with the application and type

  <code class = 'sh' lang = 'sh'> $> rake </ code> 


If error messages appear, it means that somewhere you made a mistake in the connection settings, check them.

Introduction to working with the database.



The connection of objects and databases in the rails is carried out using the OR mepper , which is called ActiveRecord . He is engaged in displaying fields from a database table into fields of an object, validating objects before saving, generating code to represent connections between objects.

To create a new model, it is enough to inherit from the class ActiveRecord::Base

  <code class = 'ruby' lang = 'ruby'> class Article <ActiveRecord :: Base
 end </ code> 


By default, ActiveRecord will work with a table also named as a class, only in the plural. In our case, Articles.

All table fields can be accessed using methods with the same name:

  <code class = 'ruby' lang = 'ruby'> # Suppose there is a title field in the Articles table
 Article.create (: title => 'Hello World!')
 article = Article.find (1)
 print article.title
 # => Hello World! </ Code> 


ActiveRecord clearly demonstrates the essence of the principle of “Convention over Configuration” - you do not need to write code in order for the program to work; the code is needed only when the program should not work as usual. For example:



ActiveRecord provides many useful features, here are some of them:



To view the documentation for ActiveRecord and other installed gems you need to run

  <code class = 'sh' lang = 'sh'> `$> gem_server` </ code> 


and open in browser

http: // localhost: 8808 /

Now let's try creating a directory.



We will use script/generate to create a model, controller, and views for the directory.

  <code> $> ruby ​​script / generate scaffold_resource article title: string body_format: string body: text </ code> 


I will not include the source code in the text in order not to inflate the article, it can be viewed online , I will describe why we need different pieces of code.

Rails generated several files, look at some of them:



I think everything is clear with the model, let's look at the controller. The controller has 7 methods:



The map.resources :articles in the config/routes.rb adds the necessary URL routing rules. Here is what urls look like:



PUT and DELETE are HTTP methods like GET and POST.
Thus, it turned out to fit all the methods necessary for managing the collection into a small and user-friendly set of URLs.

The principle of partitioning an application into sets of resources and providing a universal format for accessing resources (a way to build URLs) is called REST . The idea is to use a stateless protocol for working with resources (all the necessary information is contained in the URL), which will improve the scalability of the application and simplify caching.

Since the resource is uniquely identified by the URL, the main work of the application comes down to two tasks - giving the user pages with the resource specified in the URL and checking access rights if the resource is not public. Therefore, writing and maintaining web applications in the style of REST is very simple.

Look at the result, run the server

  <code class = 'sh' lang = 'sh'> `$> ruby ​​script / server` </ code> 


and go

http: // localhost: 3000 / articles

I got this error:

  <code> Mysql :: Error: Table 'example_development.articles' does not exist: SELECT * FROM articles </ code> 


There is no articles table in the database, it would be necessary to create it.

Changes to the database tables in the rails are made through the migration mechanism. One migration rails generated for us - the creation of the articles table ( db/migrate/001_create_articles.rb ). We need to apply it to the database, go to the folder with the application and run

  <code> $> rake db: migrate </ code> 


As a result of the migration, the articles table was created in the database, and now we can click the F5 browser and play with the application.

A few words about rake.



Rake is a replacement for utilities like make , ant , maven .
To find out what Rake can do for us, do the following:

  <code class = 'sh' lang = 'sh'> $> rake -T </ code> 


We get a long list of tasks that Rake can do. Tasks are written in Ruby and are in the Rakefile file. Initially, the file contains only the standard set of tasks that are connected using require 'tasks/rails' . For example, the db:migrate task description looks like this:

  <code class = 'ruby' lang = 'ruby'> desc "Migrate the database through scripts in db / migrate. Target specific version with VERSION = x"
 task: migrate =>: environment do
   ActiveRecord :: Migrator.migrate ("db / migrate /", ENV ["VERSION"]? ENV ["VERSION"]. To_i: nil)
   Rake :: Task ["db: schema: dump"]. Invoke if ActiveRecord :: Base.schema_format ==: ruby
 end </ code> 


The beauty of Rake is that the task description is plain Ruby code. This makes it much easier to add new tasks compared to ant or maven .

Martin Fowler has a great Rake article (in English).

We will get acquainted with Rake as needed. Now we already know that the db:migrate task starts migrations, and you can both apply and roll back the changes. If we look in the db/migrate/001_create_articles.rb , we will see that the CreateArticles class has two methods: up and down. These methods are called when the migration is applied and rolled back accordingly. The numbers 001 in the file name are the sequence number of the migration, it is used to determine the order in which the migrations are applied, while the rails store its version in the database in order not to apply one migration several times. To migrate the database to a specific version, you need to run db:migrate with the VERSION parameter:

  <code> $> rake db: migrate VERSION = 0 </ code> 


As a result, the database migrates to the zero version, when no tables have been created yet. This is a convenient way to clear the base after experimenting with the application. Then you can call db:migrate again without parameters, in the end all migrations will be applied.

The catalog is great, but the articles are not formatted.



I hope you have already looked at the catalog and saw this. Time to do the main task of the application - formatting articles.

We already have code for formatting, now we need to understand how to use it. In the first version, we only had a controller, so the code was located right in it, the Article model now appeared, but if we place the code in the model, then we tightly associate the formatting with a specific model, and this will lead to the fact that we cannot reuse the code , although it does not depend on the model that will be formatted.

We will write a plugin.



On rails, this is the natural way to add functionality to an application. In this case, the formatting code will be separated from the model, which will allow its use in other applications.

I would like the plugin to provide support for formatting without further ado, for example:

  <code> class Article <ActiveRecord :: Base
   acts_as_formatted: body
 end </ code> 


In this case, the source text is in the body field, and the formatted text of the article can be obtained using the body_as_html method. The format in which the article is written is in the body_format field.

Let's start. First of all, let's trust the rails to create a plug-in skeleton for us:

  <code> $> ruby ​​script / generate plugin acts_as_formatted </ code> 


Our plug-in appeared in the vendor/plugins folder. What's inside:



It now remains to write the code for formatting and add support to ActiveRecord.

How to do it? The task is to add the acts_as_formatted method to ActiveRecord::Base . And in this method, generate the code needed to support formatting. To do this, we need to know how to do this in Ruby.

How to add functionality to an existing class in Ruby.



In Ruby, everything is an object, in this simple program.

  <code class = 'ruby' lang = 'ruby'> print "Hello World!" </ code> 


The object method is called. Which object? This is an object of type module (analogous to namespace, package), a global module called Kernel. Modules are similar to classes, differing in that they can contain only methods, constants, and other modules and classes. At the same time, ruby ​​allows you to mix (mixin) modules into other modules and classes, this is done using the extend and include methods of modules and classes, for example:

  <code class = 'ruby' lang = 'ruby'> class MyClass
   extend enumerable
 end </ code> 


or

  <code class = 'ruby' lang = 'ruby'> class MyClass
 end

 MyClass.extend (Enumerable) </ code> 


In this case, all the methods, constants, classes and modules defined in the Enumerable module will appear in the MyClass class.

Writing a plugin.



Virtually all of the plugin’s code will be in the acts_as_formatted.rb file.

The plugin consists of two modules:



Let's see what the acts_as_formatted method acts_as_formatted .
First we will find out which formats are supported and which fields will be used (in our case body , body_format , body_as_html ), then we add a validation rule to check that the format field contains a valid format (the object cannot be saved if it does not pass validation), and we add two methods to the class get supported formats and formatted fields ( supported_formats and body_as_html ).

All formatting work takes place in the ActiveRecord::Acts::ActsAsFormatted::Formatting module. There are methods that format the text: format , format_markdown and format_textile , and the supported_formats method, which defines the supported formats, based on the methods that are in the module.

Now add the initialization code to init.rb :

  <code class = 'ruby' lang = 'ruby'> require 'acts_as_formatted'

 ActiveRecord :: Base.extend (ActiveRecord :: Acts :: ActsAsFormatted :: ClassMethods) </ code> 


and you can try the plugin in action. You need to correct the view to display the formatted text.

Combing the views and making a preview



First of all, you should get rid of duplication of code in the new and edit forms and create one form that you can use to create and edit articles. The only difference between the forms is the URL and the dispatch method. Therefore, it is convenient to make an auxiliary method that will determine the URL and method depending on how the form is used, for editing or for creating an article.

For helper methods, rails create helpers, we place the code in app/helpers/application_helper.rb . The edit_form_for method determines whether the model has already been saved and, depending on this, generates a form for creating or updating the model. The submit_edit and cancel_edit create a button to submit the form and a link to return from the form.

Now create a view for the form. Reusable pieces of views in rails are called partials. Partials file names begin with an underscore. Usually they are located in the same place where the views that use them .

After this refactoring, the new and edit view code becomes quite simple and comes down to one line:

  <code class = 'ruby' lang = 'ruby'> <% = render: partial => 'article',: object => @article%> </ code> 


It remains to add a preview. To do this , create a preview method in the controller that will return the formatted text of the article.

  <code class = 'ruby' lang = 'ruby'> def preview
   article = Article.new (params [: article])
   render_text article.body_as_html
 end </ code> 


Then add the rule to the routing table.

  <code class = 'ruby' lang = 'ruby'> map.resources: articles,
               : collection => {: preview =>: any} </ code> 


The second line adds the rule for URL /articles;preview . :collection means that the collection url will be used ( /articles ), since it does not matter for which particular article the preview is generated. Instead of :collection you can use :member , then the URL will be for a specific article ( /articles/:id ), in our case it will not allow you to make a preview of the articles being created. :any means that you can use any HTTP method for the call, in our case POST will be used when creating and PUT when editing.

Now add support to the form. In order not to overload the view with the code, we will make an auxiliary method . This method creates a button, when clicked, the form is asynchronously sent to the server and the response is displayed in the element passed to the method. It remains to add a button and an element to display the formatted article in the view .

Since the preview button uses the prototype library to asynchronously send requests to the server, you need to add its load to the page template .

  <code class = 'ruby' lang = 'ruby'> <% = javascript_include_tag 'prototype'%> </ code> 


At this functionality of the second version can be considered complete.

Now you can remove the code remaining from the first version.

  <code> script / destroy controller input preview </ code> 


And finally.

A little bit about installing the application from the hoster.



It may happen that at home everything works for you, and on the hosting it categorically refuses. There may be a lot of reasons, but the most frequent of them is that some gems are missing, or the hosting provider has the wrong version. This applies to both the rails themselves and the gems on which your application depends.

At first we will deal with dependence on rails. Before uploading your application to a hosting service, it’s very useful to do the following:

  <code> $> rake rails: freeze: gems </ code> 


As a result, a copy of the rails with which you develop your application will appear in the vendor/rails folder, and the server will use it, so you will no longer need to worry about what version the hoster has.

In addition to the rails, the application often also depends on some gems, in our case RedCloth and Maruku. To solve the problems with these dependencies, Dr Nic wrote a great plugin - Gems on Rails . It works on the same principle - it makes local copies of gems. Let's install it and learn how to use it:

  <code> $> gem install gemsonrails </ code> 


Go to the folder with the application and run

  <code> $> gemsonrails
 Installed gems_on_rails 0.6.4 to ./vendor/plugins/gemsonrails </ code> 


Now we have the Gems on Rails plugin installed, which added useful tasks for Rake.

  <code> $> rake -T
 ...
 rake gems: freeze # Freeze a RubyGem into this Rails application;  init.rb will be loaded on startup.
 rake gems: link # Link a RubyGem into this Rails application;  init.rb will be loaded on startup.
 rake gems: unfreeze # Unfreeze / unlink a RubyGem from this Rails application
 ... </ code> 




Let's make local copies of the gems needed by our application:

  <code> $> rake gems: freeze GEM = maruku
 $> rake gems: freeze GEM = redcloth </ code> 


I have the maruku-0.5.6 and RedCloth-3.0.4 folders in the vendor/gems folder.

That's all. Ask questions, read documentation and a book about rails .

The main thing is to write the code!



Ps. While writing the last lines I came across an interesting site with screencasts about the tracks.

Source: https://habr.com/ru/post/12570/


All Articles