"Não se deseja aquilo que não se conhece"
Nesse capítulo começaremos a desenvolver um sistema utilizando Ruby on Rails com recursos mais avançados.
Queremos criar um sistema de qualificação de restaurantes. Esse sistema terá clientes que qualificam os restaurantes visitados com uma nota, além de informar quanto dinheiro gastaram. Os clientes terão a possibilidade de deixar comentários para as qualificações feitas por eles mesmos ou a restaurantes ainda não visitados. Além disso, os restaurantes terão pratos, e cada prato a sua receita.
O site http://www.tripadvisor.com possui um sistema similar para viagens, onde cada cliente coloca comentários sobre hotéis e suas visitas feitas no mundo inteiro.
Crie um novo projeto chamado vota_prato:
Editoras tradicionais pouco ligam para ebooks e novas tecnologias. Não conhecem programação para revisar os livros tecnicamente a fundo. Não têm anos de experiência em didáticas com cursos.
Conheça a Casa do Código, uma editora diferente, com curadoria da Caelume obsessão por livros de qualidade a preços justos.
Casa do Código, ebook com preço de ebook.
Models são os modelos que serão usados nos sistemas: são as entidades que serão armazenadas em um banco. No nosso sistema teremos modelos para representar um Cliente, um Restaurante e uma Qualificação, por exemplo.
O componente de Modelo do Rails é um conjunto de classes que usam oActiveRecord, uma classe ORM que mapeia objetos em tabelas do banco de dados. O ActiveRecord usa convenções de nome para determinar os mapeamentos, utilizando uma série de regras que devem ser seguidas para que a configuração seja a mínima possível.
ORM (Object-Relational Mapping) é um conjunto de técnicas para a transformação entre os modelos orientado a objetos e relacional.
É um framework que implementa o acesso ao banco de dados de forma transparente ao usuário, funcionando como um Wrapper para seu modelo. Utilizando o conceito de Conventions over Configuration, o ActiveRecord adiciona aos seus modelos as funções necessárias para acessar o banco.
ActiveRecord::Base é a classe que você deve estender para associar seu modelo com a tabela no Banco de Dados.
Rake é uma ferramenta de build, escrita em Ruby, e semelhante ao make e aoant, em escopo e propósito.
Rake tem as seguintes funcionalidades:
Para criar nossas bases de dados, podemos utilizar a rake task db:create. Para isso, vá ao terminal e dentro do diretório do projeto digite:
$ rake db:create
O Rails criará dois databases no mysql: vota_prato_development,vota_prato_test.
Uma outra tarefa disponível em uma aplicação Rails e a rake db:create:all. Além de criar os dois databases já citados, ela também é responsável por criar o database vota_prato_production. No ambiente de desenvolvimento, é bem comum trabalharmos apenas com os databases de teste e desenvolvimento.
Para ver todas as tasks rake disponíveis no seu projeto podemos usar o comando (na raiz do seu projeto):
$ rake -T
A Alura oferece dezenas de cursos online em sua plataforma exclusiva de ensino que favorece o aprendizado com a qualidade reconhecida da Caelum. Você pode escolher um curso nas áreas de Java, Ruby, Web, Mobile, .NET e outros, ou fazer a assinatura semestral que dá acesso a todos os cursos.
Conheça os cursos online da Caelum.
Agora vamos criar o modelo do Restaurante. Para isso, temos um gerador específico para model através do comando rails generate model restaurante.
Repare que o Rails gerou uma série de arquivos para nós.
Migrations ajudam a gerenciar a evolução de um esquema utilizado por diversos bancos de dados. Foi a solução encontrada para o problema de como adicionar uma coluna no banco de dados local e propagar essa mudança para os demais desenvolvedores de um projeto e para o servidor de produção.
Com as migrations, podemos descrever essas transformações em classes que podem ser controladas por sistemas de controle de versão (por exemplo, git) e executá-las em diversos bancos de dados.
Sempre que executarmos a tarefa Generator -> model, o Rails se encarrega de criar uma migration inicial, localizado em db/migrate.
ActiveRecord::Migration é a classe que você deve estender ao criar uma migration.
Quando geramos nosso modelo na seção anterior, Rails gerou para nós uma migration (db/migrate/<timestamp>_create_restaurantes.rb). Vamos agora editar nossa migration com as informações que queremos no banco de dados.
Queremos que nosso restaurante tenha um nome e um endereço. Para isso, devemos acrescentar as chamadas de método abaixo:
t.string :nome, limit: 80 t.string :endereco
Faça isso dentro do método change da classe CreateRestaurantes. Sua migration deverá ficar como a seguir:
class CreateRestaurantes < ActiveRecord::Migration def change create_table :restaurantes do |t| t.string :nome, limit: 80 t.string :endereco t.timestamps end end end
Supondo que agora lembramos de adicionar a especialidade do restaurante. Como fazer? Basta usar o outro gerador (Generator) do rails que cria migration. Por exemplo:
$ rails generate migration add_column_especialidade_to_restaurante especialidade
Um novo arquivo chamado<timestamp>_add_column_especialidade_to_restaurante.rb será criado peloRails, e o código da migração gerada será como a seguir:
class AddColumnEspecialidadeToRestaurante < ActiveRecord::Migration def change add_column :restaurantes, :especialidade, :string end end
Note que através do nome da migração e do parâmetro que passamos no gerador, o nome da coluna que queremos adicionar, nesse caso especialidade, oRails deduziu que a migração é para adicionar a coluna e já fez o trabalho braçal para você!
Vamos apenas adicionar mais alguns detalhes a essa migração, vamos limitar o tamanho da string que será armazenada nesse campo para 40 caracteres:
class AddColumnEspecialidadeToRestaurante < ActiveRecord::Migration def change add_column :restaurantes, :especialidade, :string, limit: 40 end end
Crie nosso banco de dados
$ rake db:create
No terminal, acesse o mysql e verifique que os databases foram criados:
$ rails dbconsole mysql> show databases; mysql> quit
Crie o modelo do Restaurante
Edite seu script de migração do modelo "restaurante" para criar os campos nome e endereço:
t.string :nome, limit: 80 t.string :endereco
O código final de sua migration deverá ser como o que segue:
class CreateRestaurantes < ActiveRecord::Migration def change create_table :restaurantes do |t| t.string :nome, limit: 80 t.string :endereco t.timestamps end end end
Migre as tabelas para o banco de dados:
$ rake db:migrate
Verifique no banco de dados se as tabelas foram criadas:
$ rails dbconsole mysql> use vota_prato_development; mysql> desc restaurantes; mysql> quit
Adicione a coluna especialidade ao nosso modelo "restaurante":
$ rails generate migration add_column_especialidade_to_restaurante especialidade
add_column :restaurantes, :especialidade, :string, limit: 40
class AddColumnEspecialidadeToRestaurante < ActiveRecord::Migration def change add_column :restaurantes, :especialidade, :string, limit: 40 end end
rake db:migrate
== CreateRestaurantes: migrating ============================================= -- create_table(:restaurantes) -> 0.7023s == CreateRestaurantes: migrated (0.7025s) ==================================== == AddColumnEspecialidadeToRestaurante: migrating ============================ -- add_column(:restaurantes, :especialidade, :string, {:limit=>40}) -> 0.9402s == AddColumnEspecialidadeToRestaurante: migrated (0.9404s) ===================
$ rails dbconsole mysql> use vota_prato_development; mysql> show tables;
| Tables_in_vota_prato_development | | restaurantes | | schema_migrations | 2 rows in set (0.00 sec)
(Opcional) Utilizamos o método add_column na nossa migration para adicionar uma nova coluna. O que mais poderia ser feito? Abra a documentação e procure pelo módulo ActiveRecordConnectionAdaptersSchemaStatements.
Você chegou aqui porque a Caelum é referência nacional em cursos de Java, Ruby, Agile, Mobile, Web e .NET.
Faça curso com quem escreveu essa apostila.
Consulte as vantagens do curso Desenv. Ágil para Web com Ruby on Rails.
Podemos utilizar o console para escrever comandos Ruby, e testar nosso modelo. A grande vantagem disso, é que não precisamos de controladores ou de uma view para testar se nosso modelo funciona de acordo com o esperado e se nossas regras de validação estão funcionando. Outra grande vantagem está no fato de que se precisarmos manipular nosso banco de dados, ao invés de termos de conhecer a sintaxe sql e digitar a query manualmente, podemos utilizar código ruby e manipular através do nosso console.
Para criar um novo restaurante, podemos utilizar qualquer um dos jeitos abaixo:
r = Restaurante.new r.nome = "Fasano" r.endereco = "Av. dos Restaurantes, 126" r.especialidade = "Comida Italiana" r.save
r = Restaurante.new do |r| r.nome = "Fasano" r.endereco = "Av. dos Restaurantes, 126" r.especialidade = "Comida Italiana" end r.save
Uma outra forma possível para a criação de objetos do Active Record é usando um hash como parâmetro no construtor. As chaves do hash precisam coincidir com nomes das propriedades as quais queremos atribuir os valores. Veja o exemplo a seguir:
r = Restaurante.new nome: "Fasano", endereco: "Av. dos Restaurantes, 126", especialidade: "Comida Italiana" r.save
Porém o Active Record tem uma restrição quanto ao uso dessa funcionalidade. É preciso que você especifique exatamente quais são as propriedades que podem ter seu valor atribuído a partir do hash.
É preciso dizer isso explicitamente através da invocação do métodoattr_accessible. Vamos editar a classe Restaurante para que o código fique como o seguinte:
class Restaurante < ActiveRecord::Base attr_accessible :nome, :endereco, :especialidade end
Pronto! Agora você já pode criar objetos com as propriedades populadas através de um hash. Além disso, também pode invocar um método de classe emRestaurante que cria um objeto e já armazena os dados no banco, sem precisar da invocação ao método save, veja:
Restaurante.create nome: "Fasano", endereco: "Av. dos Restaurantes, 126", especialidade: "Comida Italiana"
Essa ideia de precisar especificar quais campos podem ser atribuídos através de um hash é referida por muitos como "mass assignment whitelist". A ideia é que o programador deve decidir quais propriedades de um objeto podem ser atribuídas diretamente.
Em versões anteriores do framework, toda propriedade era passível de atribuição, a não ser que o programador se lembrasse de adicionar uma invocação ao método attr_accessible. O problema disso é que, se por algum descuido, o programador esquecer de estabelecer exatamente quais propriedades estão abertas a atribuição, algumas falhas de segurança podem ocorrer.
Atualmente o padrão é justamente o inverso. Nenhuma propriedade pode ser atribuída diretamente a partir dos valores de um hash, caso queira isso, é preciso especificar através da invocação do attr_accessible.
Você pode ler mais a respeito nesse post:http://blog.caelum.com.br/seguranca-de-sua-aplicacao-e-os-frameworks-ataque-ao-github/
Note que o comando save efetua a seguinte ação: se o registro não existe no banco de dados, cria um novo registro; se já existe, atualiza o registro existente.
Existe também o comando save!, que tenta salvar o registro, mas ao invés de apenas retornar "false" se não conseguir, lança a exceção RecordNotSaved.
Para atualizar um registro diretamente no banco de dados, podemos fazer:
Restaurante.update(1, {nome: "1900"})
Para atualizar múltiplos registros no banco de dados:
Restaurante.update_all("especialidade = 'Massas'")
Ou, dado um objeto r do tipo Restaurante, podemos utilizar:
r.update_attribute(:nome, "1900")
r.update_attributes nome: "1900", especialidade: "Pizzas"
Existe ainda o comando update_attributes!, que chama o comando save! ao invés do comando save na hora de salvar a alteração.
Para remover um restaurante, também existem algumas opções. TodoActiveRecord possui o método destroy:
restaurante = Restaurante.first restaurante.destroy
Para remover o restaurante de id 1:
Restaurante.destroy(1)
Para remover os restaurantes de ids 1, 2 e 3:
restaurantes = [1,2,3] Restaurante.destroy(restaurantes)
Para remover todos os restaurantes:
Restaurante.destroy_all
Podemos ainda remover todos os restaurantes que obedeçam determinada condição, por exemplo:
Restaurante.destroy_all(especialidade: "italiana")
Os métodos destroy sempre fazem primeiro o find(id) para depois fazer odestroy(). Se for necessário evitar o SELECT antes do DELETE, podemos usar o método delete():
Restaurante.delete(1)
Teste a manipulação de registros pelo console.
Edite o arquivo localizado em app/models/restaurante.rb. Na declaração da classe invoque o método attr_accessible como no exemplo abaixo:
class Restaurante < ActiveRecord::Base attr_accessible :nome, :endereco, :especialidade end
Insira um novo restaurante
r = Restaurante.new nome: "Fasano", endereco: "Av. dos Restaurantes, 126", especialidade: "Comida Italiana"
$ rails dbconsole mysql> select * from restaurantes;
r.save
$ rails dbconsole mysql> select * from restaurantes;
Atualize seu restaurante
r.update_attributes nome: "1900"
$ rails dbconsole mysql> select * from restaurantes;
Vamos remover o restaurante criado:
Restaurante.destroy(1)
$ rails dbconsole mysql> select * from restaurantes;
Teste outras maneiras de efetuar as operações do exercício anterior.
Conheça a Casa do Código, uma nova editora, com autores de destaque no mercado, foco em ebooks (PDF, epub, mobi), preçosimbatíveis e assuntos atuais.
Com a curadoria da Caelum e excelentes autores, é uma abordagem diferente para livros de tecnologia no Brasil. Conheça os títulos e a nova proposta, você vai gostar.
Casa do Código, livros para o programador.
O ActiveRecord possui o método "find" para realizar buscas. Esse método, aceita os seguintes parâmetros:
Restaurante.all # retorna todos os registros Restaurante.first # retorna o primeiro registro Restaurante.last # retorna o último registro
Ainda podemos passar para o método find uma lista com os id's dos registros que desejamos:
r = Restaurante.find(1) varios = Restaurante.find(1,2,3)
Além desses, podemos definir condições para nossa busca (como o SELECT do MySQL). Existem diversas formas de declararmos essas condições:
Restaurante.where("nome = 'Fasano' and especialidade = 'massa'") Restaurante.where(["nome = ? and especialidade = ?", 'Fasano', 'massa']) Restaurante.where(["nome = :nome and especialidade = :especialidade", { nome: "Fasano", especialidade: "Massa"}]) Restaurante.where({nome: "Fasano", especialidade: "massa" })
Essas quatro formas fazem a mesma coisa. Procuram por registros com o camponome = "Fasano" e o campo especialidade = "massa".
Existem ainda os chamados dynamic finders:
Restaurante.where(["nome = ? AND especialidade = ?", "Fasano", "italiana"])
poderia ser escrito como:
find_all_by_nome_and_especialidade("Fasano", "italiana")
Temos ainda o "find_or_create_by", que retorna um objeto se ele existir, caso contrário, cria o objeto no banco de dados e retorna-o:
Restaurante.find_or_create_by_nome("Fasano")
Para finalizar, podemos chamar outros métodos encadeados para fazer querys mais complexas:
Exemplo mais completo:
Restaurante.where('nome like :nome', {nome: '%teste%'}). order('nome DESC').limit(20)
Existem mais opções, como o ":lock", que podem ser utilizadas, mas não serão abordadas nesse curso. Você pode consultá-las na documentação da API do Ruby on Rails.
Vamos testar os métodos de busca:
Restaurante.first
Restaurante.all
Restaurante.find(1)
Restaurante.where(["nome = ? AND especialidade = ?", "Fasano", "Comida Italiana"])
Restaurante.find_all_by_nome_and_especialidade("Fasano", "Comida Italiana")
Restaurante.order("especialidade DESC").limit(1)
Ao inserir um registro no banco de dados é bem comum a entrada de dados inválidos.
Existem alguns campos de preenchimento obrigatório, outros que só aceitem números, que não podem conter dados já existentes, tamanho máximo e mínimo etc.
Para ter certeza que um campo foi preenchido antes de salvar no banco de dados, é necessário pensar em três coisas: "como validar a entrada?", "qual o campo a ser validado?" e "o que acontece ao tentar salvar uma entrada inválida?".
Para validar esses registros, podemos implementar o método validate em qualquer ActiveRecord, porém o Rails disponibiliza alguns comandos prontos para as validações mais comuns. São eles:
Todos estes métodos disponibilizam uma opção (:message) para personalizar a mensagem de erro que será exibida caso a regra não seja cumprida. Caso essa opção não seja utilizada, será exibida uma mensagem padrão.
Toda mensagem de erro é gravada num hash chamado errors, presente em todo ActiveRecord.
Além dos validadores disponibilizados pelo rails, podemos utilizar um validador próprio:
validate :garante_alguma_coisa def garante_alguma_coisa errors.add_to_base("Deve respeitar nossa regra") unless campo_valido? end
Repare que aqui, temos que incluir manualmente a mensagem de erro padrão do nosso validador.
Se quisermos que o nome do nosso restaurante comece com letra maiúscula, poderíamos fazer:
validate :primeira_letra_deve_ser_maiuscula private def primeira_letra_deve_ser_maiuscula errors.add("nome", "primeira letra deve ser maiúscula") unless nome =~ /[A-Z].*/ end
Além dessas alternativas, o Rails 3 trouxe uma nova maneira de criar validações que facilita os casos onde temos vários validadores para o mesmo campo, como por exemplo:
validates :nome, presence: true, uniqueness: true, length: {maximum: 50}
equivalente a:
validates_presence_of :nome validates_uniqueness_of :nome validates_length_of :nome, :maximum => 50
Utilizamos aqui, o modificador de acesso private. A partir do ponto que ele é declarado, todos os métodos daquela classe serão privados, a menos que tenha um outro modificador de acesso que modifique o acesso a outros métodos.
Esse exemplo poderia ter sido reescrito utilizando o validador"validates_format_of", que verifica se um atributo confere com uma determinada expressão regular.
Devido ao encoding dos arquivos no Ruby 1.9 ser como padrão ASCII-8BIT, ao utilizarmos algum carácter acentuado no código fonte, é necessário indicarmos um encoding que suporte essa acentuação. Geralmente, esse encoding será o UTF-8. Portanto, temos que adicionar no começo do arquivo fonte a linha:
#encoding: utf-8
Se você gosta de estudar essa apostila aberta da Caelum, certamente vai gostar dos novos cursos online que lançamos na plataforma Alura. Você estuda a qualquer momento com aqualidade Caelum.
Conheça a assinatura semestral.
Para nosso restaurante implementaremos a validação para que o campo nome, endereço e especialidade não possam ficar vazios, nem que o sistema aceite dois restaurantes com o mesmo nome e endereço.
validates_presence_of :nome, message: "deve ser preenchido" validates_presence_of :endereco, message: "deve ser preenchido" validates_presence_of :especialidade, message: "deve ser preenchido" validates_uniqueness_of :nome, message: "nome já cadastrado" validates_uniqueness_of :endereco, message: "endereço já cadastrado"
#encoding: utf-8
Inclua a validação da primeira letra maiúscula:
validate :primeira_letra_deve_ser_maiuscula private def primeira_letra_deve_ser_maiuscula errors.add(:nome, "primeira letra deve ser maiúscula") unless nome =~ /[A-Z].*/ end
Agora vamos testar nossos validadores:
r = Restaurante.new nome: "fasano", endereco: "Av. dos Restaurantes, 126" r.save
r.valid? # verifica se objeto passa nas validações r.errors.empty? # retorna true/false indicando se há erros ou não r.errors.count # retorna o número de erros r.errors[:nome] # retorna apenas o erro do atributo nome r.errors.each {|field, msg| puts "#{field} - #{msg}"}
Repare que o Rails pluralizou o termo restaurante automaticamente. Por exemplo, o nome da tabela é restaurantes. O Rails se baseia nas regras gramaticais da lingua inglesa para fazer essa pluralização automática.
Apesar de restaurante ter sido plurificado corretamente, outros termos comoqualificacao por exemplo, será plurificado para qualificacaos, ao invés dequalificacoes. Podemos fazer o teste no console do Rails, a String tem os métodospluralize e singularize para fazer, respectivamente, pluralização e singularização do termo:
"qualificacao".pluralize # => "qualificacaos" "qualificacoes".singularize # => "qualificacoe"
Outro termo onde encontraremos problema é em "receita":
"receita".pluralize # => "receita" "receitas".singularize # => "receita"
Apesar da singularização estar correta, a pluralização não ocorre, isso acontece por que de acordo com as regras gramaticais do inglês, a palavra receita seria plural de receitum. Testando no console obteremos o seguinte resultado:
"receitum".pluralize # => "receita"
Ou seja, teremos que mudar as regras padrões utilizadas pelo Rails. Para isso, iremos editar o arquivo config/initializers/inflections.rb, adicionando uma linha indicando que a pluralização das palavras qualificacao e receita é, respectivamente, qualificacoes e receitas:
activesupport::inflector.inflections do |inflect| inflect.irregular 'qualificacao', 'qualificacoes' inflect.irregular 'receita', 'receitas' end
Feito isso, podemos testar nossas novas regras utilizando o console:
"qualificacao".pluralize # => "qualificacoes" "qualificacoes".singularize # => "qualificacao" "receita".pluralize # => "receitas" "receitas".singularize # => "receita"
Vamos corrigir a pluralização da palavra 'receita'
ActiveSupport::Inflector.inflections do |inflect| inflect.irregular 'receita', 'receitas' end
Existe um plugin chamado Brazilian Rails que é um conjunto de gems para serem usadas com Ruby e com o Ruby on Rails e tem como objetivo unir alguns recursos úteis para os desenvolvedores brasileiros.
$ rails console
"receita".pluralize
Vamos criar o nosso modelo Cliente, bem como sua migration:
$ rails generate model cliente
create_table :clientes do |t| t.string :nome, limit: 80 t.integer :idade t.timestamps end
$ rake db:migrate
Configure as propriedades adequadas para possibilitar o mass assignment na classe Cliente. Edite a definição criada no arquivo app/models/cliente.rb para adicionar a chamada ao attr_accessible:
class Cliente < ActiveRecord::Base attr_accessible :nome, :idade end
Vamos agora fazer as validações no modelo "cliente":
validates_presence_of :nome, message: " - deve ser preenchido" validates_uniqueness_of :nome, message: " - nome já cadastrado" validates_numericality_of :idade, greater_than: 0, less_than: 100, message: " - deve ser um número entre 0 e 100"
Vamos criar o modelo Prato, bem como sua migration:
t.string :nome, limit: 80
Vamos definir que o nome de um prato que pode ser atribuído via hash como parâmetro no construtor, edite o arquivo app/models/prato.rb e adicione a invocação ao attr_accessible:
attr_accessible :nome
Vamos agora fazer as validações no modelo "prato". Ainda no arquivoapp/models/prato.rb adicione o seguinte código:
validates_presence_of :nome, message: " - deve ser preenchido" validates_uniqueness_of :nome, message: " - nome já cadastrado"
Vamos criar o modelo Receita, bem como sua migration:
t.integer :prato_id t.text :conteudo
Complete a classe Receita definindo que prato_id e conteudo serão passíveis de atribuição via mass assignment, lembra como se faz? Edite a classe Receitasdefinida em app/models/receita.rb e adicione a seguinte linha:
class Receita attr_accessible :prato_id, :conteudo end
Vamos agora fazer as validações no modelo "receita":
validates_presence_of :conteudo, message: " - deve ser preenchido"
Querendo aprender ainda mais sobre a linguagem Ruby e o framework Ruby on Rails? Esclarecer dúvidas dos exercícios? Ouvir explicações detalhadas com um instrutor?
A Caelum oferece o curso RR-71 presencial nas cidades de São Paulo, Rio de Janeiro e Brasília, além de turmas incompany.
Consulte as vantagens do curso Desenv. Ágil para Web com Ruby on Rails.
Vamos corrigir a pluralização da palavra 'qualificacao'
inflect.irregular 'qualificacao', 'qualificacoes'
$ rails console
"qualificacao".pluralize
Vamos continuar com a criação do nosso modelo Qualificacao e sua migration.
t.integer :cliente_id t.integer :restaurante_id t.float :nota t.float :valor_gasto
add_index(:qualificacoes, :cliente_id) add_index(:qualificacoes, :restaurante_id)
$ rake db:migrate
Configure as propriedades de uma qualificação utilizando o attr_accessible:
attr_accessible :nota, :valor_gasto, :cliente_id, :restaurante_id
Vamos agora fazer as validações no modelo "qualificacao":
validates_presence_of :nota, message: " - deve ser preenchido" validates_presence_of :valor_gasto, message: " - deve ser preenchido" validates_numericality_of :nota, greater_than_or_equal_to: 0, less_than_or_equal_to: 10, message: " - deve ser um número entre 0 e 10" validates_numericality_of :valor_gasto, greater_than: 0, message: " - deve ser um número maior que 0"
Para relacionar diversos modelos, precisamos informar ao Rails o tipo de relacionamento entre eles. Quando isso é feito, alguns métodos são criados para podermos manipular os elementos envolvidos nesse relacionamento. Os relacionamentos que Rails disponibiliza são os seguintes:
Os relacionamentos vistos até agora foram sempre entre dois objetos de classes diferentes. Porém existem relacionamentos entre classes do mesmo tipo, por exemplo, uma Categoria de um site pode conter outras categorias, onde cada uma contém ainda suas categorias. Esse relacionamento é chamado auto-relacionamento.
No ActiveRecord temos uma forma simples para fazer essa associação.
class Category < ActiveRecord::Base has_many :children, class_name: "Category", foreign_key: "father_id" belongs_to :father, class_name: "Category" end
O GUJ é um dos principais fóruns brasileiros de computação e o maior em português sobre Java. A nova versão do GUJ é baseada em uma ferramenta de perguntas e respostas (QA) e tem uma comunidade muito forte. São mais de 150 mil usuários pra ajudar você a esclarecer suas dúvidas.
Faça sua pergunta.
Todos os resultados de métodos acessados de um relacionamento são obtidos de um cache e não novamente do banco de dados. Após carregar as informações do banco, o ActiveRecord só volta se ocorrer um pedido explicito. Exemplos:
restaurante.qualificacoes # busca no banco de dados restaurante.qualificacoes.size # usa o cache restaurante.qualificacoes.empty? # usa o cache restaurant
Infrastructure quality in Italy is rated to be at 3.78. It indicates a good quality - roads, railroad, ports and other facilities are adapted and regularly maintained to handle high levels of traffic at all times, as well as most probably there are special facilities for handling high intensity and/or special traffic or vehicles (e.g. motorways a.k.a. autobahns and deepwater ports).
In Italy, 100% of the population has access to electricity. There are 25,662,000 internet hosts in Italy. Italy has 129 airports nationwide.
The logistics performance index of Italy is 3.69. It indicates a satisfactory performance - in general, traffic is handeled well, some flaws in certain areas are possible, but overall the logistics system performs reliably and is ready to handle predictable amounts of traffic.
Xo สล็อตออนไลน์ โปรสล็อต XO เกมออนไลน์ทำเงินยอดฮิตเกมสล็อต xopg.net คือเกมทำเงิน reeffutures2018 ผ่านทางออนไลน์อย่างหนึ่ง ที่เล่นง่าย และได้เงินไว แถมยังลงทุนด้วยเงินน้อย mavoixtavoie ทำเงินได้ตลอดเวลา ซึ่งหลายคนอาจได้เคยเห็นรีวิวเรื่องของ สล็อต xo สล็อตออนไลน์ ไว้มากมาย เทคนิคสล็อต ทั้งเรื่องการเล่นแล้วได้เงิน herbalpertpresents และเล่น สล็อต แล้วไม่ได้เงิน นั่นเองค่ะ ซึ่งการที่คุณจะเล่นได้เงินหรือไม่ได้เงินนั้น essentialsforasoul ส่วนหนึ่งก็เป็นในเรื่องของดวงเข้ามาเกี่ยวด้วย northbristol เพราะสล็อตเป็นเกมออนไลน์เสี่ยงโชค ทดลองเล่น xo เกมหนึ่งซึ่งจะมีสูตร หรือเทคนิคเข้ามาช่วย gclub เพื่อโกงดวงอยู่เสมอซึ่งในเว็บของเรา สมัคร xo ก็มีมาแนะนำไว้ให้เห็นกันมากมายหลายสูตร
I should assert barely that its astounding! The blog is informational also always fabricate amazing entitys. 사설토토
It's superior, however , check out material at the street address. 안전놀이터
I recommend only good and reliable information, so see it: 벳삼
I recommend only good and reliable information, so see it: 꽁머니사이트
It is especially decent, though look into the tips during this home address. 링크찾기
Youre so cool! I dont suppose Ive read anything such as this before. So nice to discover somebody by original applying for grants this subject. realy thanks for beginning this up. this website is one thing that is required on-line, a person after a little originality. helpful problem for bringing new things for the world wide web! 사설토토
I do trust all the ideas you’ve introduced on your post. They’re really convincing and will definitely work. Nonetheless, the posts are very quick for starters. May just you please extend them a bit from subsequent time? Thanks for the post. 토토커뮤니티
An impressive share, I just now given this onto a colleague who was doing a little analysis on this. And hubby actually bought me breakfast since I ran across it for him.. smile. So let me reword that: Thnx to the treat! But yeah Thnkx for spending enough time go over this, Personally i think strongly regarding this and love reading more on this topic. When possible, as you grow expertise, does one mind updating your site with an increase of details? It is extremely useful for me. Big thumb up with this short article! 꽁머니
Although, regardless how positive you actually are in treating an actual platform, at some point there are an incident wherever you should want to do some people regular supervision; and in addition dependant on your real age and in addition wellbeing, and so the body-weight using the caravan it’s really a totally hard do physical exercise. caravan touch up paint brown puffer phone case
Glad to see you’re on top of things. You sound like you understand what you’re talking about! Thanks and good luck! x 먹튀검증
Merely wanna state that this is very beneficial , Thanks for taking your time to write this. black air pod pros
I believe other website owners should take this site as an example , very clean and wonderful user genial style . croxyproxy YouTube
Hi there! This is kind of off topic but I need some advice from an established blog. Is it very hard to set up your own blog? I’m not very techincal but I can figure things out pretty quick. I’m thinking about setting up my own but I’m not sure where to start. Do you have any points or suggestions? Thank you Cas9 ELISA kit
dude this just inspired a post of my own, thanks Sliding Glass Door Roller Replacement Palm Beach County FL
Unquestionably believe that which you stated. Your favorite justification appeared to be on the internet the easiest thing to be aware of. I say to you, I certainly get irked while people consider worries that they just don’t know about. You managed to hit the nail upon the top and also defined out the whole thing without having side effect , people can take a signal. Will probably be back to get more. Thanks unblocked games 66 ez
I genuinely treasure your work , Great post. cats with big noses
I genuinely treasure your work , Great post. cats with big noses
I wanted to thank you a lot more for this amazing web-site you have created here. It truly is full of useful tips for those who are genuinely interested in this subject, specifically this very post. Your all actually sweet plus thoughtful of others as well as reading your site posts is a wonderful delight in my experience. And what a generous reward! Mary and I will certainly have fun making use of your guidelines in what we must do in a few weeks. Our checklist is a mile long which means that your tips might be put to great use. Cas9 ELISA kit