王筝的博客
ruby学习
flash.now[:error] = ""
render :new


flash[:error] = ""
redirect videos_path

http://guides.rubyonrails.org/action_controller_overview.html

The flash is a special part of the session which is cleared with each request. This means that values stored there will only be available in the next request, which is useful for passing error messages etc.

It is accessed in much the same way as the session, as a hash (it’s a FlashHash instance).

Let’s use the act of logging out as an example. The controller can send a message which will be displayed to the user on the next request:

class LoginsController < ApplicationController
  def destroy
    session[:current_user_id] = nil
    flash[:notice] = "You have successfully logged out."
    redirect_to root_url
  end
end

Note that it is also possible to assign a flash message as part of the redirection.

You can assign :notice:alert or the general purpose :flash:

redirect_to root_url, notice: "You have successfully logged out."
redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }

The destroy action redirects to the application’s root_url, where the message will be displayed. Note that it’s entirely up to the next action to decide what, if anything, it will do with what the previous action put in the flash. It’s conventional to display any error alerts or notices from the flash in the application’s layout:

<html>
  <!-- <head/> -->
  <body>
    <% flash.each do |name, msg| -%>
      <%= content_tag :div, msg, class: name %>
    <% end -%>
 
    <!-- more content -->
  </body>
</html>

This way, if an action sets a notice or an alert message, the layout will display it automatically.

You can pass anything that the session can store; you’re not limited to notices and alerts:

<% if flash[:just_signed_up] %>
  <p class="welcome">Welcome to our site!</p>
<% end %>

If you want a flash value to be carried over to another request, use the keep method:

class MainController < ApplicationController
  # Let's say this action corresponds to root_url, but you want
  # all requests here to be redirected to UsersController#index.
  # If an action sets the flash and redirects here, the values
  # would normally be lost when another redirect happens, but you
  # can use 'keep' to make it persist for another request.
  def index
    # Will persist all flash values.
    flash.keep
 
    # You can also use a key to keep only some kind of value.
    # flash.keep(:notice)
    redirect_to users_url
  end
end
5.2.1 flash.now

By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request.

For example, if the create action fails to save a resource and you render the new template directly,

that’s not going to result in a new request, but you may still want to display a message using the flash.

To do this, you can use flash.now in the same way you use the normal flash:

class ClientsController < ApplicationController
  def create
    @client = Client.new(params[:client])
    if @client.save
      # ...
    else
      flash.now[:error] = "Could not save client"
      render action: "new"
    end
  end
end

 

今天存储数据的时候报错,发现是3435065640超出了常规int的存储长度,

RangeError (3435065640 is out of range for ActiveRecord::Type::Integer with limit 4)

$ bundle exec rails db -p
mysql> desc recommended_videos;
+-------------------------+--------------+------+-----+---------+----------------+
| Field                   | Type         | Null | Key | Default | Extra          |
+-------------------------+--------------+------+-----+---------+----------------+
| id                      | int(11)      | NO   | PRI | NULL    | auto_increment |
| video_id                | varchar(255) | YES  |     |         |                |
| res_desc                | text         | YES  |     | NULL    |                |
| state                   | int(11)      | YES  |     | 0       |                |
| created_at              | datetime     | YES  |     | NULL    |                |           |
+-------------------------+--------------+------+-----+---------+----------------+

http://dev.mysql.com/doc/refman/5.7/en/integer-types.html

12.2.1 Integer Types (Exact Value) – INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT

MySQL supports the SQL standard integer types INTEGER (or INT) and SMALLINT. As an extension to the standard, MySQL also supports the integer types TINYINTMEDIUMINT, and BIGINT. The following table shows the required storage and range for each integer type.


Type Storage Minimum Value Maximum Value
(Bytes) (Signed/Unsigned) (Signed/Unsigned)
TINYINT 1 -128 127
0 255
SMALLINT 2 -32768 32767
0 65535
MEDIUMINT 3 -8388608 8388607
0 16777215
INT 4 -2147483648 2147483647
0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807
0 18446744073709551615
int的存储上限是2147483647,
所以change_column
class ChangeTotalVvTypeToRecommendedVideos < ActiveRecord::Migration
  def change
    change_column :recommended_videos, :total_vv, :integer, :limit => 8
  end 
end
mysql> desc recommended_videos;
+-------------------------+--------------+------+-----+---------+----------------+
| Field                   | Type         | Null | Key | Default | Extra          |
+-------------------------+--------------+------+-----+---------+----------------+
| id                      | int(11)      | NO   | PRI | NULL    | auto_increment |
| state                   | int(11)      | YES  |     | 0       |                |
| sequence                | int(11)      | YES  |     | 1       |                |
| created_at              | datetime     | YES  |     | NULL    |                |
| updated_at              | datetime     | YES  |     | NULL    |                |
| total_vv                | bigint(20)   | YES  |     | NULL    |                |
+-------------------------+--------------+------+-----+---------+----------------+

 

When you first started coding, errors were probably the last thing you wanted to see.

After all, it’s not a far stretch to associate “error” with “I messed up”.

Hopefully by now you’ve come to appreciate the value of a good error message. Take a look at the two following errors:

one + 3
NameError: undefined local variable or method 'one' for main:Object
one + 3
TypeError: no implicit conversion of Fixnum into String

Both errors were triggered by the same line of code. Each is a totally different error message, though.

The first, NameError, lets you know that one is a variable that hasn’t been defined. To fix this, you’ll have to actually define the variable one.

The second, TypeError, lets you know that you’re trying to add a Fixnum to a String.

Here, there’s a good chance that one is a variable that is already set to a String.

Because these errors were meaningful errors, they allowed you to debug your program painlessly, and ideally also provided a learning opportunity.

Inheriting an Exception

In Ruby, just about everything is an object, including errors. Turns out that NameError andTypeError are simply the class names for these errors!

All errors inherit their functionality from the Exception class.

There are about twenty or so default errors baked into Ruby (read more about them here) and some indicate more serious issues than others.

Issues that deal with problems in your code (as opposed to your computer being on fire) all inherit from StandardError. This includes errors like NameError and TypeError.

This means that if you’ve got a custom error that you’d like to create, like anInvalidPasswordError, you can easily create one by inheriting from StandardError.

class InvalidPasswordError < StandardError
end

It’s really that easy. You don’t even need anything inside this class!

To manually trigger an exception or error, which can be described as “raising” or “throwing” an exception, use the raise keyword.

raise InvalidPasswordError
InvalidPasswordError: InvalidPasswordError

Easy enough. Now you know that you can raise this InvalidPasswordError whenever it’s appropriate.

May I suggest you use it when a password is… invalid?

But this still isn’t super descriptive. Luckily, you can raise an error with an additional message.

raise InvalidPasswordError, "The password you entered is invalid."
InvalidPasswordError: The password you entered is invalid.

That’s more like it. Now it’s explicit what went wrong when this particular exception was thrown.

This is by no means a comprehensive guide to throwing errors and exceptions.

This material could fill a course by itself, and it is a topic we will return to later in this material.

This is, however, the most common way you’ll see exceptions and errors being thrown in the wild.

Exceptional Errors

When other developers are using your code, it’s a good idea to bake meaningful errors right into your public API.

Let’s see how you might be able to use this InvalidPasswordError in the context of the examples from earlier in the lesson.

class InvalidPasswordError < StandardError
end

class Customer
  attr_reader :funds

  def initialize(funds, password)
    @password = password
    @funds = funds
  end

  def withdraw_securely(amount, password)
    if password == @password
      remove_funds(amount)
    else
      raise InvalidPasswordError, "'#{password}' is not the correct password."
    end
  end

  private

  def remove_funds(amount)
    @funds -= amount
  end
end

Now, if the correct password was entered, the funds are removed as expected.

But this time, if the incorrect password is entered, your new InvalidPasswordError is thrown with a useful little message.

 

kim = Customer.new(1000, "coolpassword")
# => #<Customer:0x007faabc8012b8 @password="coolpassword", @funds=1000>
kim.withdraw_securely(200, "coolpassword")
# => 800
kim.withdraw_securely(150, "badpassword")
InvalidPasswordError: 'badpassword' is not the correct password.

That’s so useful!

 

Often, classes will have shared characteristics with other classes.

Rewriting the same methods for each class over and over again can be pretty cumbersome, and not always necessary.

class BankTeller
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end

  def do_bank_teller_stuff
    # method stuff...
  end
end

class LoanSpecialist
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end

  def do_loan_specialist_stuff
    # method stuff...
  end
end

I mean, just look at these two classes! They’re nearly identical!

The only differences are thedo_bank_teller_stuff and do_loan_specialist_stuff methods.

Both of these classes have characteristics that are shared across all employees.

Being redundant like this works, but will quickly cause problems if you decide that employees should be paid bi-weekly instead of on a fixed day of the month.

You’d have to update the get_paid method for every single employee class! Right now you’ve only got two,

but what if you had 30 different roles in the company. No thank you.

Here, you can use inheritance to share traits across various classes.

class Employee
  def get_paid
    # method stuff...
  end

  def come_to_work
    # method stuff...
  end

  def go_on_vacation!
    # method stuff...
  end
end

class BankTeller < Employee
  def do_bank_teller_stuff
    # method stuff...
  end
end

class LoanSpecialist < Employee
  def do_loan_specialist_stuff
    # method stuff...
  end
end

You can use the < when defining these classes to indicate that LoanSpecialist and BankTellerare the children of Employee.

When this happens, they inherit all the methods of the parent class. You can continue to define new methods for the child class as you normally would.

Note that a child can only have one parent class.

This technique of inheritance is a handy way to reduce code duplication and logically separate out the concerns of your code.

Feeling Protected

We previously mentioned that in addition to the public and private keywords, there is also aprotected keyword.

private and protected are similar in many ways, with one key difference: private methods are not shared through inheritance,

whereas protected methods are shared across all children classes.

 

When calling an instance method like withdraw_securely, the syntax generally looks something like this:

object.method_being_called(arguments)

One would therefore think it’s safe to assume that an instance method is always preceded by a .,

which is in turn preceded by the object that is calling the method.

Why, then, did this code work in the previous example?

 

# from inside the Customer class

def withdraw_securely(amount, password)
  if password == @password
    remove_funds(amount)
  end
end

Isn’t remove_funds also an instance method? Why is it suddenly exempt from following the sameobject.

method_being_called syntax just because it’s inside a method?

This can be compared to spoken language. If you were asking Diego to tell us his name, you might say to him “Diego, tell us your name.”

But if you were asking me to tell you my name, you’d likely say “Tell me your name”.

Yes, you could have said “You, tell me your name,” but that would have been redundant. “You” isimplicit in “Tell me your name”.

Similarly, when you call an instance method from within a class, there is an implicit object being called: itself.

# from inside the Customer class

def withdraw_securely(amount, password)
  if password == @password
    self.remove_funds(amount)
  end
end

An object can refer to itself using the self keyword. Think of it as an object’s way of saying “me” or “I”.

When you call remove_funds from within the Customer class, you’re saying “remove these funds from myself”.

And since Ruby is all about removing any unnecessary syntax, self in this context is implicit, and can be left out entirely.

An instance variable like @password is scoped to a particular instance of a class.

But what if you wanted a variable that was shared across all instances of a class? Almost like… a class variable.

@@check_out_this_cool_class_variable = "boom."

Boom is right.

A class variable’s syntax is twice as cool as an instance variable, because it has two @’s instead of just one.

Let’s see how one might use a class variable.

 

class Employee
  @@bank = "Udacity International Bank"

  def bank
    @@bank
  end
end

Unfortunately, class variables can’t be accessed using attr_accessor, so you’ll need to create your own bank getter method in this case.

Initialize two instances of an employee to see this working.

 

elana = Employee.new
# => #<Employee:0x007fcdb48c19d0>
corey = Employee.new
# => #<Employee:0x00nfbdm132ejd9>
elana.bank
# => "Udacity International Bank"
corey.bank
# => "Udacity International Bank"

Great, now this @@bank class variable is shared across all instances of an Employee class.

But… Why?

Class variables are used considerably less frequently than instance variables.

Unlike the publickeyword, though, there are some practical use cases for class variables. Here are two of those use cases.

class Customer
  attr_reader :id

  @@id = 1
  @@customers = []

  def initialize
    @id = @@id
    @@id += 1
    @@customers << self
  end
end

If you break this apart, you’ll see two class variables, @@id and @@customers.

Every time a new customer is instantiated, an instance variable of @id is set to the value of theclass variable @@id.

Immediately afterwards, the @@id class variable is incremented by one.

 

larry = Customer.new
# => #<Customer:0x007faaba8a6aa8 @id=1>
christine = Customer.new
# => #<Customer:0x007faaba8a6aa8 @id=2>
larry.id
# => 1
christine.id
# => 2

This way, the Customer class can keep track of the total number of customer objects that have been created.

By assigning the class variable to the instance variable @id, you are capturing the current ID number from when that particular customer object was created.

Similarly, @@customers in this case is an Array that holds all the customer objects that have ever been created.

After a new customer is initialized, self, the particular instance currently being used, is pushed into this @@customers Array.

Unfortunately, we’re left without an appropriate interface for accessing @@customers.

It wouldn’t make sense to create a customers instance method, since an Array of customers isn’t really a property of a particular customer.

If only there were something else…

Class variables can be especially powerful when coupled with class methods.

Class methods differ from instance methods in that they are called directly on the class name, like so:

Customer.this_is_a_class_method

These are used when you need to call a method that doesn’t apply to a specific instance of a class.

Say you need a method to retrieve all the customers you’ve created so far. This method could be called all.

Defining a class method works just like defining an instance method, except it must be preceded by self.

class Customer
  attr_reader :id

  @@id = 1
  @@customers = []

  # ...

  def initialize
    @id = @@id
    @@id += 1
    @@customers << self
  end

  def self.all
    @@customers
  end
end

Now, if at any point you’d like to retrieve an Array of all existing customers, you can simply call the class method.

Customer.all
# => [#<Customer:0x007faaba84c148 @id=1>, #<Customer:0x007faaba82fe30 @id=2>]

 

https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-debian-7

https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-nginx-on-ubuntu-14-04

1) Update Apt-Get

The apt-get update command is used to re-synchronize the package index files from their sources. If used in combination with the apt-get upgrade command, they install the newest versions of all packages currently available.

At the moment, we only need to do a thorough update:

sudo apt-get update

2) Install MySQL on your VPS

MySQL is a powerful database management system used for organizing and retrieving data

To install MySQL, open terminal and type in these commands:

sudo apt-get install mysql-server

During the installation, MySQL will ask you to set a root password. If you miss the chance to set the password while the program is installing, it is very easy to set the password later from within the MySQL shell.

Password

Once you have installed MySQL, we should activate it with this command:

3) Install and Configure Nginx on your VPS

Installation

Initial installation is simple with the apt-get command.

sudo apt-get install nginx

nginx needs a command to begin running:

sudo service nginx start

Configuration

vim /etc/nginx/nginx.conf, 在http中加入以下代码,

      server {
        listen   80;


        root /opt/app/blog/wz/wordpress/;
        index index.php index.html index.htm;

        server_name iwangzheng.com;

        location / {
                try_files $uri $uri/ /index.html;
        }

        error_page 404 /404.html;

        error_page 500 502 503 504 /50x.html;
                location = /50x.html {
                root /usr/share/nginx/www;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;

        }

     }

nginx.conf的第一行是user www-data;

把这个项目目录的权限修改下

sudo chown -R www-data:www-data /opt/app/blog/wz/wordpress/

4) Install and Configure PHP

Installation

You probably guessed it! We will use the apt-get command to install PHP-FPM:

sudo apt-get install php5-fpm php5-mysql
sudo service nginx restart
sudo service php5-fpm restart

 

# cat /etc/issue
Debian GNU/Linux 8 \n \l

#apt-get update

http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

class CreateStars < ActiveRecord::Migration
  def self.up
    create_table :cms_tv_stars do |t| 
      t.string :username
      t.string :image
      t.integer :person_id

      t.timestamps
    end 

    change_table :cms_tv_stars do |t| 
      t.index :person_id, uniq: true
    end 
  end 

  def self.down
    drop_table :cms_tv_stars
  end 
end

 

class CreateSubchannelItems < ActiveRecord::Migration
  def self.up
    create_table :tv_subchannel_items do |t| 
      t.string :title
      t.string :subtitle
      t.string :version
      t.string :image
      t.references :subchannel
      t.references :showable, polymorphic: true
      t.integer :state, limit: 1, default: 0
      t.integer :position, default: 1

      t.timestamps
    end 

    change_table :tv_subchannel_items do |t| 
      t.index [:showable_type, :showable_id], name: :subchannel_items_showable_index
      t.index [:subchannel_id, :state, :version, :position], name: :subchannel_items_sort_index
    end 
  end 

  def self.down
    drop_table :tv_subchannel_items
  end 
end

http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

If you have an instance of the Picture model, you can get to its parent via @picture.imageable.

To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps null: false
    end
 
    add_index :pictures, :imageable_id
  end
end

This migration can be simplified by using the t.references form:

class CreatePictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.string :name
      t.references :imageable, polymorphic: true, index: true
      t.timestamps null: false
    end
  end
end

 

Let’s check the index in the database

$ bundle exec rails db -p
mysql> show index from tv_subchannel_items;
+---------------------+------------+---------------------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table               | Non_unique | Key_name                        | Seq_in_index | Column_name   | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------------------+------------+---------------------------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| tv_subchannel_items |          0 | PRIMARY                         |            1 | id            | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_showable_index |            1 | showable_type | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_showable_index |            2 | showable_id   | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_sort_index     |            1 | subchannel_id | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_sort_index     |            2 | state         | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_sort_index     |            3 | version       | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
| tv_subchannel_items |          1 | subchannel_items_sort_index     |            4 | position      | A         |           0 |     NULL | NULL   | YES  | BTREE      |         |               |
+---------------------+------------+---------------------------------+--------------+---------------+-----------+-------------+----------+-----

 

Your latest client is a bank, and they’ve tasked you with requiring customers to enter their password in order to make withdrawals.

Currently, this is what they’ve got:

class Customer
  attr_reader :funds

  def initialize(funds, password)
    @funds = funds
    @password = password
  end

  def remove_funds(amount)
    @funds -= amount
  end
end

Let’s break that apart. You can paste that whole class into irb to follow along.

When a customer is initialized, it receives a specified amount of funds and a password is set.

diego = Customer.new(500, "udacious")
# => #<Customer:0x007fcdb48ca5a8 @funds=500 @password="udacious">

Thanks to the attr_reader, you can see the value of his current funds.

diego.funds
# => 500

And the remove_funds method allows funds to be removed from the customer’s account.

Checking on the funds again confirms this.

diego.remove_funds(50)
# => 450
diego.funds
# => 450

These methods, funds and remove_funds, are part of the Customer class’ API, or application programming interface.

An API is, according to Wikipedia, “a set of routines, protocols, and tools for building software applications”.

Well, that’s vague.

“API” is a popular term in recent years, but many people use it without quite understanding what it means. Think of methods like remove_funds as your way of interfacing with the Customer class. These methods are the keys to accessing information about a particular customer.

There isn’t currently a way to access the @password instance variable.

It could be said that the customer’s password can’t be accessed by the customer’s public API.

In this situation, that’s a good thing! You don’t want information like a password to be publicly available to other objects.

Let’s implement a method called withdraw_securely, which takes two arguments, amount andpassword.

If the password entered matches the customer’s password, go ahead and remove the funds. Otherwise, nothing happens.

class Customer
  attr_reader :funds

  def initialize(funds, password)
    @password = password
    @funds = funds
  end

  def remove_funds(amount)
    @funds -= amount
  end

  def withdraw_securely(amount, password)
    if password == @password
      remove_funds(amount)
    end
  end
end

Play around with this in irb to see it in action.

diego.withdraw_securely(50, "udacious")
# => 400
diego.withdraw_securely(100, "wrong password")
# => nil
diego.funds
# => 400

✨Hooray. Calling withdraw_securely using the correct password decreases the total funds by calling remove_funds,

while using the incorrect password does nothing.

There’s one issue here, can you spot it?

diego.remove_funds(75)
# => 325
diego.funds
# => 325

Malicious users can still withdraw funds directly using the remove_funds method!

With this current setup, both remove_funds and withdraw_securely are part of a customer’s public API.

In reality, remove_funds needs to somehow be tucked away so that it isn’t accessible via the public API.

This can be done by making it a private method.

Private methods can be invoked only from within a class. If remove_funds were a private method,

that would mean it can only be accessed by another method, like withdraw_securely. To do this, you can use the private keyword.

class Customer
  attr_reader :funds

  public

  def initialize(funds, password)
    @password = password
    @funds = funds
  end

  def withdraw_securely(amount, password)
    if password == @password
      remove_funds(amount)
    end
  end

  private

  def remove_funds(amount)
    @funds -= amount
  end
end

Here, anything after the private keyword will be treated as a private method. Test it out in irb and see it in action!

diego.withdraw_securely(75, "udacious")
# => 250

Okay, withdraw_securely still works as expected…

diego.remove_funds(100)
NoMethodError: private method 'remove_funds' called for
#<Customer:0x007fcdb496ae40 @funds=400, @password="udacious">

But now remove_funds throws an error! A NoMethodError in particular. If you read through this error,

you’ll see that it does exactly what you intended: it will not let you call the private method,remove_funds.

Using private methods allows you to reuse logic in your code without having to worry about exposing it to other classes.

As a guiding principle, it’s not a bad idea to assume a method should be private until you’ve been given a good reason to make it a part of an object’s public API.

Going Public

Technically, there’s also a public keyword. It works just like the private keyword, and it’s used to indicate that methods should be publicly available.

The public and private keywords (there’s also protected, but we’ll get to that later) can only be used once inside each class, and methods are by default public.

class Customer
  attr_reader :funds

  public

  # public methods...

  private

  # private methods...
end

While this would technically work just fine, using the public keyword tends to be redundant. You’ll hardly ever see it in code anywhere.

A disclaimer about Private Methods

Guilty confession: just because a method is private doesn’t mean it can’t ever be called.

diego.send(:remove_funds, 65)
# => 185

There’s a little workaround using the send method, which sends the method of the name you specify (in this case, :remove_funds) to be called by the object.

What does this mean for you? Well, if security is actually a primary concern for you, creating a private method alone isn’t enough of a solution.

But the benefits of tucking away private methods remain. Indicating that a method is private lets other developers know that a method isn’t intended to be part of an object’s public API, and discourages them from using it.

By encapsulating all the logic for an object, whether it’s a Dog or a User or an IceCreamShop, you are able to keep all of the logic and responsibilities of your object within its own scope. This lets you avoid having to worry about other objects using methods that they shouldn’t have access to.

Let’s take one last look at how your Dog methods took advantage of encapsulation. Let’s say you also had some methods for your pet parrot in there as well. Here’s what your code might have looked like:

 

def drink_water_from_bowl(dog)
  # code for method
end

def wants_to_play?(dog)
  # code for method
end

def fly_away(parrot)
  # code for method
end

def wag_tail(dog)
  # code for method
end

And here’s what it would look like using OOP:

class Dog
  def drink_water_from_bowl
    # code for method
  end

  def wants_to_play?
    # code for method
  end

  def wag_tail
    # code for method
  end
end

class Parrot
  def fly_away
    # code for method
  end
end

Not only is it much more apparent which actions are supposed to be performed by which objects, but now you won’t have to worry about accidentally calling fly_away on your dog. Who knows what could have happened there!

To build off the benefits of encapsulation, OOP helps keep your code readable. By keeping all of its instance methods tabbed in one level deep, it becomes much simpler to skim through your files.

Requiring Files

To take this organization to the next level, let’s address one last convention when it comes to classes. As a rule of thumb, each class should exist in its own file. This way, when you’re working on an application that has multiple classes, like ClothingStore and Customer, you know exactly where to look to make changes to your code.

These files should have the same name as the class they contain, with a few slight changes. Instead of naming your files in upper camel case (ClothingStore.rb), you should name them using snake case, like you would name a variable (clothing_store.rb).

But how do you get those files to interact with each other? Files don’t just magically know that there are other files that need to be included in your project. You’ll need to explicitly require those files.

Let’s say you’ve got three files: clothing_store.rbcustomer.rb, and app.rb. To include the contents of the first two files in the last file, you can use require_relative. Let’s see that in action.

# app.rb

require_relative "clothing_store.rb"
require_relative "customer.rb"

# The rest of your code...

This code will look for the files you specify relative to the current file. That means that ifcustomer.rb was one directory level above the current directory, you’d writerequire_relative “../customer.rb".

Additionally, Ruby allows you to omit the .rb file extension by default. So your app.rb file can be written instead as:

# app.rb

require_relative "clothing_store"
require_relative "customer"

# The rest of your code...

 

  • Nearly everything in Ruby is an object!
  • You can define your own classes in addition to the standard Ruby classes like Integer to create new types of objects.
  • To see which class a particular object is, you can use the class method.
  • Objects have instance methods, or methods that can be called on a specific instance of a class, like reverse for Strings.
  • To see which instance methods are available for a given object, you can use the methodsmethod.
  • Getter and setter methods can be used to assign and retrieve the values of instance variables.
  • Speaking of, instance variables are scoped to a particular class, and always start with a @.
  • attr_readerattr_writer, and attr_accessor are handy shortcuts for getter and setter methods.
  • Ruby lets you write some things in more convenient ways, like leaving off parentheses. This issyntactic sugar .
  • You can use initialize to take care of any initial set-up for your classes.
  • You can split up your Ruby files using require_relative.
  • Generally speaking, each class should belong in its own file.