@@ -0,0 +1,317 @@ |
||
1 |
+# API |
|
2 |
+ |
|
3 |
+Neste documento vou explicar como criar uma **API** (Application Programming Interface) para um aplicativo **Rails**. Vou começar criando uma **API** simples, depois mostrando como criar versões dela, depois vou mostrar como criar um sistema de tokens para adicionar segurança e por ultimo vou criar um sistema de autenticação de usúarios via oAuth. |
|
4 |
+ |
|
5 |
+## Criando uma API basica |
|
6 |
+ |
|
7 |
+Em Qualquer controlador do rails, é possivel criar uma resposta em **JSON** além da resposta em **HTML**. |
|
8 |
+ |
|
9 |
+<pre><code class="ruby"> |
|
10 |
+class ProductsController < ApplicationController |
|
11 |
+ def index |
|
12 |
+ @products = Products.all |
|
13 |
+ respond_to do |format| |
|
14 |
+ format.html |
|
15 |
+ format.json { render json @products } |
|
16 |
+ end |
|
17 |
+ end |
|
18 |
+end |
|
19 |
+</code></pre> |
|
20 |
+ |
|
21 |
+Ao entrar na página ```http://localhost:300/products.json```, você vai receber um arquivo em **JSON** com o conteúdo dos produtos. |
|
22 |
+ |
|
23 |
+## Criando rotas para a API |
|
24 |
+ |
|
25 |
+Para criar uma rota como ```http://localhost:3000/api```, modifique o arquivo ```config/rountes.rb``` |
|
26 |
+ |
|
27 |
+ Store::Application.routes.draw do |
|
28 |
+ namespace :api do |
|
29 |
+ # /api/... API:: |
|
30 |
+ namespace :api, defaults: {format: 'json'} do |
|
31 |
+ namespace :v1 do |
|
32 |
+ resources :products |
|
33 |
+ end |
|
34 |
+ namespace :v2 do |
|
35 |
+ resources :products |
|
36 |
+ end |
|
37 |
+ end |
|
38 |
+ end |
|
39 |
+ |
|
40 |
+ resources :products |
|
41 |
+ root to: 'products#index' |
|
42 |
+ end |
|
43 |
+ |
|
44 |
+## O Controlador da API |
|
45 |
+ |
|
46 |
+Cria o arquivo ```/app/controllers/api/v1/products_controller.rb```: |
|
47 |
+ |
|
48 |
+ module Api |
|
49 |
+ module V1 |
|
50 |
+ class ProductsController < ApplicationController # ou API::BaseController |
|
51 |
+ |
|
52 |
+ # Hacks para que certos atributos continuem `backword compatiple` |
|
53 |
+ # Aqui estamos modificando a funcionalidade da classe neste controlador |
|
54 |
+ class Product < ::Product |
|
55 |
+ def as_json(options = {}) |
|
56 |
+ super.merge(released_on released_at.to_date) |
|
57 |
+ end |
|
58 |
+ end |
|
59 |
+ |
|
60 |
+ respond_to :json |
|
61 |
+ |
|
62 |
+ def index |
|
63 |
+ respond_with Product.all |
|
64 |
+ end |
|
65 |
+ |
|
66 |
+ def show |
|
67 |
+ respond_with Product.find(params[:id]) |
|
68 |
+ end |
|
69 |
+ |
|
70 |
+ def create |
|
71 |
+ respond_with Product.cerate(params[:product]) |
|
72 |
+ end |
|
73 |
+ |
|
74 |
+ def update |
|
75 |
+ respond_with Product.update(params[:id], params[:product]) |
|
76 |
+ end |
|
77 |
+ |
|
78 |
+ def destroy |
|
79 |
+ respond_with Product.destroy(params[:id]) |
|
80 |
+ end |
|
81 |
+ end |
|
82 |
+ end |
|
83 |
+ end |
|
84 |
+ |
|
85 |
+## Usando accept headers para versão da API |
|
86 |
+ |
|
87 |
+Em vez de usar um endereço como ```http://localhost:3000/api/v1/```, alguns sites como o [github](http://github.com) usam o **accept header** para especificar a versão do API. |
|
88 |
+ |
|
89 |
+Para configurar as rotas para ceitar o **accept header**, modifique o arquivo ```config/rountes.rb``` |
|
90 |
+ |
|
91 |
+ require 'api_constraints' |
|
92 |
+ |
|
93 |
+ Store::Application.routes.draw do |
|
94 |
+ namespace :api, defaults: {format: 'json'} do |
|
95 |
+ scope module: :v1, constraints: ApiContraints.new(version: 1) do |
|
96 |
+ resources :products |
|
97 |
+ end |
|
98 |
+ scope module: :v2, constraints: ApiConstraints.new(version: 2, default: true) do |
|
99 |
+ resources :products |
|
100 |
+ end |
|
101 |
+ end |
|
102 |
+ |
|
103 |
+ resources :products |
|
104 |
+ root to: 'products#index' |
|
105 |
+ end |
|
106 |
+ |
|
107 |
+Depois no arquivo ```lib/api_constraints.rb```: |
|
108 |
+ |
|
109 |
+ class ApiConstraints |
|
110 |
+ def initialize(options) |
|
111 |
+ @version = options[:version] |
|
112 |
+ @default = options[:default] |
|
113 |
+ end |
|
114 |
+ |
|
115 |
+ def matches?(req) |
|
116 |
+ @default || req.headers['Accept'].include?("application/vnd.example.v#{@version}") |
|
117 |
+ end |
|
118 |
+ end |
|
119 |
+ |
|
120 |
+Agora ao visitar a url ```http://localhost:3000/api/products```, você vai acessar o **API** v2 e receber o arquivo em **JSON**. |
|
121 |
+ |
|
122 |
+Para acessar outra versão da **API** via *curl* no *terminal*: |
|
123 |
+ |
|
124 |
+``` curl -H 'Accept: application.vnd.example.v1' http://localhost:3000/api.products ``` |
|
125 |
+ |
|
126 |
+## Autenticação basica |
|
127 |
+ |
|
128 |
+O jeito mas simples de restringir acesso ao **API** é usando a autenticação basica provida pelo **rails**. |
|
129 |
+ |
|
130 |
+ |
|
131 |
+No arquivo ```/app/controllers/api/v1/products_controller.rb```: |
|
132 |
+ |
|
133 |
+ module Api |
|
134 |
+ module V1 |
|
135 |
+ class ProductsController < ApplicationController |
|
136 |
+ http_basic_authenticate_with name: "admin", password: "secret" |
|
137 |
+ respond_to :json |
|
138 |
+ ... |
|
139 |
+ |
|
140 |
+Para testar, no terminal, digite o comando: |
|
141 |
+ |
|
142 |
+<pre><code class="bash"> |
|
143 |
+ $ curl http://localhost:3000/api/products |
|
144 |
+ HTTP Basic: Access denied. |
|
145 |
+ |
|
146 |
+ $ curl http://localhost:3000/api/products -I |
|
147 |
+ HTTP/1.1 401 Unauthorized |
|
148 |
+ WWW-Authenticate: Basic realm="Application" |
|
149 |
+ Content-Type: text/html; charset=utf-8 |
|
150 |
+ ... |
|
151 |
+ |
|
152 |
+ $ curl http://localhost:3000/api/products -u 'admin:secret' |
|
153 |
+ JSON response |
|
154 |
+ ... |
|
155 |
+</code></pre> |
|
156 |
+ |
|
157 |
+## Access Token |
|
158 |
+ |
|
159 |
+Em vez de usar um sistema de autenticação, podemos restringir acesso a API requerindo um **token de acesso**. Se na chamada para o API o cliente não enviar o **token**, o acesso será negado. |
|
160 |
+ |
|
161 |
+Primeiro vamos criar um modelo para guardar os **tokens**. Execute o comando: |
|
162 |
+ |
|
163 |
+ ```rails generate model api_key access_token``` |
|
164 |
+ |
|
165 |
+É possivel tb adicionar outras colunas como ```user_id``` para guardar o dono do **token**, ou ```expires_at``` para destruir chaves antigas ou a colona ```role``` para adicionar permissões diferentes para cada **token**. |
|
166 |
+ |
|
167 |
+Em seguida vamos modificar o modelo em ```app/models/api_key.rb```. Quando um novo *api_key* for criado, ele vai gerar um número randomico unico para está chave. |
|
168 |
+ |
|
169 |
+ class ApiKey < ActiveRecord::Base |
|
170 |
+ before_create :generate_access_token |
|
171 |
+ |
|
172 |
+ private |
|
173 |
+ |
|
174 |
+ def generate_access_token |
|
175 |
+ begin |
|
176 |
+ self.access_token = SecureRandom.hex |
|
177 |
+ end while self.class.exists?(access_token: access_token) |
|
178 |
+ end |
|
179 |
+ end |
|
180 |
+ |
|
181 |
+Para testar, entre no [console](rails_console.md) e digite o comando: ```ApiKey.create!```. Isso vai gerar um novo **access_token** com um número hexadecimal unico. |
|
182 |
+ |
|
183 |
+Agora vamos restringir o acesso ao **API** modificando o controlador. No arquivo ```/app/controllers/api/v1/products_controller.rb```: |
|
184 |
+ |
|
185 |
+ module Api |
|
186 |
+ module V1 |
|
187 |
+ class ProductsController < ApplicationController |
|
188 |
+ before_filter :restrict_access |
|
189 |
+ respond_to :json |
|
190 |
+ |
|
191 |
+ ... |
|
192 |
+ |
|
193 |
+ private |
|
194 |
+ |
|
195 |
+ def restrict_access |
|
196 |
+ api_key = ApiKey.find_by_access_token(params[:access_token]) |
|
197 |
+ head :unauthorized unless api_key |
|
198 |
+ end |
|
199 |
+ end |
|
200 |
+ end |
|
201 |
+ end |
|
202 |
+ |
|
203 |
+ |
|
204 |
+Para acessar o **API** com o **access_token**, podemos utilizar a seguinte url: |
|
205 |
+ |
|
206 |
+```http://localhost:3000/api/products?access_token=c576f0136149a2e2d9127b3901015545``` |
|
207 |
+ |
|
208 |
+Este metodo não é muito seguro, por que as pessoas podem copiar e colar os codigos de acesso junto com a url sem querer. Para melhorar este exemplo, vamos passar o **access_token** atravez de um parametro no *head* do *request* para o servidor. |
|
209 |
+ |
|
210 |
+Vamos mudar de novo o arquivo ```/app/controllers/api/v1/products_controller.rb```: |
|
211 |
+ |
|
212 |
+ def restrict_access |
|
213 |
+ authenticate_or_request_with_http_token do |token, options| |
|
214 |
+ ApiKey.exists?(access_token: token) |
|
215 |
+ end |
|
216 |
+ end |
|
217 |
+ |
|
218 |
+Para testar, no terminal vamos accessar de novo a url do **API** mas desta vez passando o **access_token** junto com o *header*: |
|
219 |
+ |
|
220 |
+```curl http://localhost:3000/api/products -H 'Authorization: Token token="c576f0136149a2e2d9127b3901015545"'``` |
|
221 |
+ |
|
222 |
+## Autenticação de usuários com OAuth |
|
223 |
+ |
|
224 |
+A questão das soluções anteriores é que estamos criando um sistema de autenticação global. Não temos como saber exatamente qual usuário está acessando a **API**. Então se você necessista de autenticação de usuário via o **API**, o jeito mais comum para fazer isso é utilizando o [OAuth2](http://oauth.net/2/). |
|
225 |
+ |
|
226 |
+> **OAauth** é um protocolo aberto para autorização segura de um **API** de um jeito simples e comum para aplicativos *web*, *mobile* e *desktop*. |
|
227 |
+ |
|
228 |
+O protocolo **OAuth** foi criado pela equipe do twitter e várias outras empresas adotaram e usam o protocolo como o Google, Facebook e GitHub. Ele é utilizado para que um aplicavo possa acessar informações de outro aplicativo através da autorização de um usúario. |
|
229 |
+ |
|
230 |
+O *gem* [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) serve para simplificar a criação de um serviço de **OAuth** em um aplicativo *rails*. |
|
231 |
+ |
|
232 |
+### Configurando o DoorKeeper |
|
233 |
+ |
|
234 |
+Primeiro, mude para ```false``` a configuração ```whitelist_attributes``` no arquivo ```config/application.rb```: |
|
235 |
+ |
|
236 |
+ config_active_record.whitelist_attributes = false |
|
237 |
+ |
|
238 |
+Depois no ```gemfile``` adicione no fim: |
|
239 |
+ |
|
240 |
+ gem 'doorkeeper' |
|
241 |
+ |
|
242 |
+Execute o comando ```bundle install``` para instalar o *gem* e depois rode os comandos para instalar o **doorkeeper** e migrar o banco de dados: |
|
243 |
+ |
|
244 |
+ rails g doorkeeper:install |
|
245 |
+ rake db:migrate |
|
246 |
+ |
|
247 |
+Varias novos arquivos e tabelas no banco de dados vão ser criados. Em seguida modifique o arquivo ```config/initializers/doorkeeper.rb```: |
|
248 |
+ |
|
249 |
+ DoorKeeper.configure do |
|
250 |
+ |
|
251 |
+ resource_owner_authenticator do |routes| |
|
252 |
+ #raise "Please configure doorkeeper" |
|
253 |
+ # Put your resource owner authentication logic here |
|
254 |
+ # If you want to use named routes from your app you need |
|
255 |
+ # to call them on routes object eg. |
|
256 |
+ # routes.new_user_session_path |
|
257 |
+ User.find_by_id(session[:user_id]) || redirect_to(routes.new_user_session_url) |
|
258 |
+ end |
|
259 |
+ |
|
260 |
+ ... |
|
261 |
+ |
|
262 |
+ |
|
263 |
+### Repositorios |
|
264 |
+ |
|
265 |
+* [doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) - Doorkeeper is an OAuth 2 provider for Rails and Grape |
|
266 |
+* [ionic-doorkeeper](https://github.com/emilsoman/ionic-doorkeeper) - Sample ionic app that authenticates with a Rails API protected with Doorkeeper |
|
267 |
+ |
|
268 |
+### Links |
|
269 |
+ |
|
270 |
+* [OAuth Implicit Grant with Grape, Doorkeeper and AngularJS](http://codetunes.com/2014/oauth-implicit-grant-with-grape-doorkeeper-and-angularjs/) |
|
271 |
+* [How can I pre-authorize a client app for my user on my oauth provider that uses doorkeeper?](http://stackoverflow.com/questions/11129676/how-can-i-pre-authorize-a-client-app-for-my-user-on-my-oauth-provider-that-uses) |
|
272 |
+* [Using Resource Owner Password Credentials flow](https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow) |
|
273 |
+* [Doorkeeper + Devise](http://morodeercoding.blogspot.ru/) |
|
274 |
+* [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749) |
|
275 |
+ |
|
276 |
+ |
|
277 |
+## Criando um API com o Grape gem |
|
278 |
+ |
|
279 |
+### Repositorios |
|
280 |
+ |
|
281 |
+* [grape](https://github.com/intridea/grape) - An opinionated micro-framework for creating REST-like APIs in Ruby. |
|
282 |
+* [wine_bouncer](https://github.com/antek-drzewiecki/wine_bouncer) - A Ruby gem that allows Oauth2 protection with Doorkeeper for Grape Api's |
|
283 |
+* [grape-doorkeeper](https://github.com/fuCtor/grape-doorkeeper) - Integration Grape with Doorkeeper* |
|
284 |
+ |
|
285 |
+### Links |
|
286 |
+ |
|
287 |
+* [Build Great APIS with Grape](http://www.sitepoint.com/build-great-apis-grape/) |
|
288 |
+* [Introduction to building APIs with Grape](http://codetunes.com/2014/introduction-to-building-apis-with-grape/) |
|
289 |
+* [Building RESTful API using Grape in Rails](http://funonrails.com/2014/03/building-restful-api-using-grape-in-rails/) |
|
290 |
+* [OAuth 2.0 Tutorial: Protect Grape API with Doorkeeper](http://blog.yorkxin.org/posts/2013/11/05/oauth2-tutorial-grape-api-doorkeeper-en/) |
|
291 |
+* [authenticate grape api with doorkeeper](http://stackoverflow.com/questions/26472217/authenticate-grape-api-with-doorkeeper) |
|
292 |
+* [Grape API authentication using Devise Auth Token](http://funonrails.com/2014/03/api-authentication-using-devise-token/) |
|
293 |
+ |
|
294 |
+## Autenticação apenas com o Devise |
|
295 |
+ |
|
296 |
+### Links |
|
297 |
+ |
|
298 |
+* [APIs with Devise](http://soryy.com/blog/2014/apis-with-devise/) |
|
299 |
+* [API JSON authentication with Devise](https://gist.github.com/jwo/1255275) |
|
300 |
+ |
|
301 |
+## API Reference |
|
302 |
+ |
|
303 |
+* [A Well Designed API Approach](https://www.airpair.com/rest/posts/a-well-designed-api-approach) |
|
304 |
+ |
|
305 |
+ |
|
306 |
+ |
|
307 |
+----------------- |
|
308 |
+ |
|
309 |
+[Index](index.md) |
|
310 |
+ |
|
311 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
312 |
+ |
|
313 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
314 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
315 |
+<script> |
|
316 |
+ hljs.initHighlightingOnLoad(); |
|
317 |
+</script> |
@@ -1,9 +1,65 @@ |
||
1 |
-# Links |
|
1 |
+# Links e URLs |
|
2 | 2 |
|
3 |
-Em um **View**, para criar um **link** utilize a função ```link_to```. |
|
3 |
+## links |
|
4 | 4 |
|
5 |
-## Exemplo |
|
5 |
+Em um **View**, para criar um **link** utilize a função ```link_to```. Essa função gera uma tag ```<a>``` em HTML. |
|
6 |
+ |
|
7 |
+#### Exemplo |
|
6 | 8 |
|
7 | 9 |
<%= link_to("click here", {:action => 'index'}) %> |
8 | 10 |
|
9 |
-<a class="btn btn-mini" href="readme.md">voltar</a> |
|
11 |
+## URLs |
|
12 |
+ |
|
13 |
+Para gerar apenas uma URL, utilize o comando ```url_for```. |
|
14 |
+ |
|
15 |
+ |
|
16 |
+### Opções |
|
17 |
+ |
|
18 |
+* ```:anchor``` - Especifica o *anchor* na url. Pode ser usada para ir direto a um *ID* na proxima pagina. |
|
19 |
+* ```:only_path``` - Boolean que especifica se o metodo deve retornar apenas a URL ou todas as informações (protocol, host, port). |
|
20 |
+* ```:format``` - Adiciona uma extensão ao final da URL |
|
21 |
+ |
|
22 |
+### Exemplos de URLs |
|
23 |
+ |
|
24 |
+ # http://www.example.com/posts |
|
25 |
+ url_for :controller => 'posts', :action => 'index' |
|
26 |
+ |
|
27 |
+ # http://www.example.com/posts/5 |
|
28 |
+ url_for :controller => 'posts', :action => 'index', :id => 5 |
|
29 |
+ |
|
30 |
+ # http://www.example.com/posts/5/edit |
|
31 |
+ url_for :controller => 'posts', :action => 'edit', :id => 5 |
|
32 |
+ |
|
33 |
+ # http://www.example.com/posts.xml |
|
34 |
+ url_for :controller=>'posts', :action=>'index', :format=>:xml |
|
35 |
+ |
|
36 |
+### namespace |
|
37 |
+ |
|
38 |
+Se você estiver usando um *namespace*, é possivel gerar uma url do seguinte jeito: |
|
39 |
+ |
|
40 |
+ namespace :admin do |
|
41 |
+ resources :products |
|
42 |
+ end |
|
43 |
+ then you can: |
|
44 |
+ |
|
45 |
+ url_for([:admin, @product]) |
|
46 |
+ and: |
|
47 |
+ |
|
48 |
+ url_for([:edit, :admin, @product]) |
|
49 |
+ |
|
50 |
+----------------- |
|
51 |
+## Informações adicionais |
|
52 |
+ |
|
53 |
+* [Rails routing - RailsGuides](http://guides.rubyonrails.org/routing.html) |
|
54 |
+ |
|
55 |
+----------------- |
|
56 |
+ |
|
57 |
+[Index](index.md) |
|
58 |
+ |
|
59 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
60 |
+ |
|
61 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
62 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
63 |
+<script> |
|
64 |
+ hljs.initHighlightingOnLoad(); |
|
65 |
+</script> |
@@ -1,5 +1,7 @@ |
||
1 | 1 |
# Postgres |
2 | 2 |
|
3 |
+## Configuração do BD para projeto Rails |
|
4 |
+ |
|
3 | 5 |
Para criar um banco de dados postgres primeiro faça o login no banco de dados: |
4 | 6 |
|
5 | 7 |
``$ psql --username=admin`` |
@@ -45,3 +47,10 @@ Por ultimo configure o arquivo ``database.yml `` com as informações de login e |
||
45 | 47 |
pool: 5 |
46 | 48 |
username: admin |
47 | 49 |
password: password1 |
50 |
+ |
|
51 |
+## Outros comandos |
|
52 |
+ |
|
53 |
+* ``\l`` - Listar bancos de dados |
|
54 |
+* ``\c database_name`` - selecionar banco de dados |
|
55 |
+* ``\d`` - Listar tabelas de um banco de dados |
|
56 |
+* ``DROP TABLE table_name`` - Deletar uma tabela de um banco de dados |
@@ -182,4 +182,22 @@ Neste exemplo vamos criar um log de edição das páginas do sistema. |
||
182 | 182 |
belongs_to :editor, :class_name => "User", :foreign_key => "user_id" |
183 | 183 |
end |
184 | 184 |
|
185 |
-#### Traversing |
|
185 |
+#### Traversing |
|
186 |
+ |
|
187 |
+ |
|
188 |
+----------------- |
|
189 |
+## Informações adicionais |
|
190 |
+ |
|
191 |
+* [A Rule of Thumb for Strong Parameters](http://patshaughnessy.net/2014/6/16/a-rule-of-thumb-for-strong-parameters) |
|
192 |
+ |
|
193 |
+----------------- |
|
194 |
+ |
|
195 |
+[Index](index.md) |
|
196 |
+ |
|
197 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
198 |
+ |
|
199 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
200 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
201 |
+<script> |
|
202 |
+ hljs.initHighlightingOnLoad(); |
|
203 |
+</script> |
@@ -0,0 +1,24 @@ |
||
1 |
+# Terminal/Bash |
|
2 |
+ |
|
3 |
+ |
|
4 |
+* ```TAB``` - Autocompleta o atual comando |
|
5 |
+* ```!!``` - Repedir o ultimo comando |
|
6 |
+* ```control-r``` - Buscar por um comando anterior |
|
7 |
+* ```source ~/bash.profile``` - Recarrega o *bash.profile* na atual aba do terminal. Util para quando você está editando susas preferencias. |
|
8 |
+* ```$EDITOR``` variavel que determina qual editor de texto o terminal deve usar. |
|
9 |
+ |
|
10 |
+#### Links |
|
11 |
+ |
|
12 |
+* [Know your tools](http://www.spacecowboyrocketcompany.com/2015/02/know-your-tools/) |
|
13 |
+ |
|
14 |
+----------------- |
|
15 |
+ |
|
16 |
+[Index](index.md) |
|
17 |
+ |
|
18 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
19 |
+ |
|
20 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
21 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
22 |
+<script> |
|
23 |
+ hljs.initHighlightingOnLoad(); |
|
24 |
+</script> |
@@ -0,0 +1,70 @@ |
||
1 |
+# Console |
|
2 |
+ |
|
3 |
+## Configurações |
|
4 |
+ |
|
5 |
+O arquivo ```~/.irbrc``` serve para configurar o console. Ele fica no root da pasta de usuário no sistema operacional. Esse arquivo funciona como um arquvo de *.rb* comum e vai ser rodado toda vez que o IRB iniciar. |
|
6 |
+ |
|
7 |
+Para editar ou criar esse arquivo, digite no terminal: |
|
8 |
+ |
|
9 |
+```pico ~/.irbrc``` |
|
10 |
+ |
|
11 |
+Para sair do editor **pico** aperte **ctrl + x** para fechar o arquivo e confirme que quer salvar e o nome do arquivo apertando **enter**. |
|
12 |
+ |
|
13 |
+## Truques |
|
14 |
+ |
|
15 |
+### HIRB |
|
16 |
+ |
|
17 |
+O HIRB serve para melhorar a visualização do *ActiveRecord*. Ele muda o jeito como o IRB mostra os dados retornados para o usuário, criando uma tabela que pode ser customizada. |
|
18 |
+ |
|
19 |
+Muito util para poder visualizar os dados do que você está fazendo no console do rails e não ficar precisando caçar as informações perdidas dentro de um *hash* gigante. |
|
20 |
+ |
|
21 |
+#### Instalação |
|
22 |
+ |
|
23 |
+Adicione ao ```/Gemfile```: |
|
24 |
+ |
|
25 |
+ gem 'hirb' |
|
26 |
+ |
|
27 |
+Depois adicione ao arquivo ```~/.irbrc``` as seguintes configurações: |
|
28 |
+ |
|
29 |
+ require 'hirb' |
|
30 |
+ Hirb.enable |
|
31 |
+ |
|
32 |
+### Wirble |
|
33 |
+ |
|
34 |
+O wirble é um **gem** que serve para melhorar o *console* do rails. |
|
35 |
+ |
|
36 |
+#### Instalação |
|
37 |
+ |
|
38 |
+Adicione ao ```/Gemfile```: |
|
39 |
+ |
|
40 |
+ gem 'wirble' |
|
41 |
+ |
|
42 |
+Depois adicione ao arquivo ```~/.irbrc``` as seguintes configurações: |
|
43 |
+ |
|
44 |
+ begin |
|
45 |
+ require 'wirble' |
|
46 |
+ # init wirble |
|
47 |
+ Wirble.init |
|
48 |
+ # enable color |
|
49 |
+ Wirble.colorize |
|
50 |
+ Wirble::Colorize::DEFAULT_COLORS |
|
51 |
+ rescue LoadError => err |
|
52 |
+ $stderr.puts "Couldn't load Wirble: #{err}" |
|
53 |
+ end |
|
54 |
+ |
|
55 |
+#### Links |
|
56 |
+ |
|
57 |
+* [Mais informações](http://pablotron.org/software/wirble/) |
|
58 |
+* [Readme.md](http://pablotron.org/software/wirble/README) |
|
59 |
+ |
|
60 |
+----------------- |
|
61 |
+ |
|
62 |
+[Index](index.md) |
|
63 |
+ |
|
64 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
65 |
+ |
|
66 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
67 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
68 |
+<script> |
|
69 |
+ hljs.initHighlightingOnLoad(); |
|
70 |
+</script> |
@@ -1,8 +1,10 @@ |
||
1 | 1 |
# Cucumber |
2 | 2 |
|
3 |
-Behavior driven development |
|
3 |
+*Behavior driven development* |
|
4 | 4 |
|
5 |
-### Instalação |
|
5 |
+O Cucumber é um framework de alto nivel para criação de testes baseado em historias de interações de usuários. Sua grande vantagem é que os testes são escritos em uma lingua comum como o inglês (ou outras linguas utilizando plugins). |
|
6 |
+ |
|
7 |
+## Instalação |
|
6 | 8 |
|
7 | 9 |
1- no arquivo ```Gemfile```: |
8 | 10 |
|
@@ -29,11 +31,27 @@ Uma pasta chamada ```app/features``` foi criada. |
||
29 | 31 |
5- Para rodar o cucumber, utilize o comando rake: ```$ cucumber features -n``` |
30 | 32 |
|
31 | 33 |
|
32 |
-### Features |
|
34 |
+## Como funciona |
|
33 | 35 |
|
34 | 36 |
1. Cenario |
35 | 37 |
2. Feature |
36 | 38 |
3. Steps |
37 | 39 |
4. Background |
38 | 40 |
5. Paths |
39 |
-6. Factories |
|
41 |
+6. Factories |
|
42 |
+ |
|
43 |
+#### Links |
|
44 |
+ |
|
45 |
+* [RailsCast Cucumber Tutorial](https://www.evernote.com/l/AALzqmI0XeBD7rP9wL83vkSYnKMwjdSvduA) [(local version)](evernote:///view/124245/s2/f3aa6234-5de0-43ee-b3fd-c0bf37be4498/f3aa6234-5de0-43ee-b3fd-c0bf37be4498/) |
|
46 |
+ |
|
47 |
+----------------- |
|
48 |
+ |
|
49 |
+[Index](index.md) |
|
50 |
+ |
|
51 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
52 |
+ |
|
53 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
54 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
55 |
+<script> |
|
56 |
+ hljs.initHighlightingOnLoad(); |
|
57 |
+</script> |
@@ -0,0 +1,26 @@ |
||
1 |
+# Devise Invitable |
|
2 |
+ |
|
3 |
+O gem [devise_invitable](https://github.com/scambra/devise_invitable) serve para estender a funcionalidade do devise para possibilitar a criação de usuários através de convites. |
|
4 |
+ |
|
5 |
+#### Links |
|
6 |
+ |
|
7 |
+* [Using Devise Invitable](http://aaronmiler.com/blog/using-devise-invitable/) |
|
8 |
+* [Customizing for different Invite use cases](https://github.com/scambra/devise_invitable/wiki/Customizing-for-different-Invite-use-cases-(emails-etc.)) |
|
9 |
+* [The best way to approach handling invitations of existing users](https://github.com/scambra/devise_invitable/issues/506) |
|
10 |
+* [Devise invitable: invite existing user](http://stackoverflow.com/questions/18226102/devise-invitable-invite-existing-user) |
|
11 |
+* [Overide mailer in devise_invitable?](http://www.ciiycode.com/06izzgPPqqPQ/overide-mailer-in-deviseinvitable) |
|
12 |
+* [Devise Invitable optional custom message](http://stackoverflow.com/questions/9841700/devise-invitable-optional-custom-message) |
|
13 |
+* [How do I delete an invitation with devise_invitable?](http://stackoverflow.com/questions/19646175/how-do-i-delete-an-invitation-with-devise-invitable) |
|
14 |
+* [How to override devise invitable actions](http://stackoverflow.com/questions/18191010/how-to-override-devise-invitable-actions) |
|
15 |
+ |
|
16 |
+----------------- |
|
17 |
+ |
|
18 |
+[Index](index.md) |
|
19 |
+ |
|
20 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
21 |
+ |
|
22 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
23 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
24 |
+<script> |
|
25 |
+ hljs.initHighlightingOnLoad(); |
|
26 |
+</script> |
@@ -0,0 +1,170 @@ |
||
1 |
+# Faye |
|
2 |
+ |
|
3 |
+[Faye](http://faye.jcoglan.com/) é um sistema para subscrever e enviar mensagens baseado no protocolo *Bayeux* (Pub/Sub). O **Faye** providencia um **servidor** de mensagens para ser usado com *node.js* ou *Ruby* além de uma biblioteca em *Javascript* para os **clientes** que funciona em todos os browsers modernos. |
|
4 |
+ |
|
5 |
+## Instalação |
|
6 |
+ |
|
7 |
+Primeiro instale o gem executando o comando: ```gem install faye``` |
|
8 |
+ |
|
9 |
+Adicione o **faye** e o servidor **thin** ao gemfile: |
|
10 |
+ |
|
11 |
+ gem 'faye' |
|
12 |
+ gem 'thin' |
|
13 |
+ |
|
14 |
+Em seguida rode o comando ```bundle install``` para instalar os *gems*. |
|
15 |
+ |
|
16 |
+Crie o arquivo ```faye.ru``` na raiz da pasta do projeto. Este arquivo vai inicializar o servidor **faye**. |
|
17 |
+ |
|
18 |
+ require 'faye' |
|
19 |
+ Faye::WebSocket.load_adapter('thin') |
|
20 |
+ faye_server = Faye::RackAdapter.new(:mount => 'faye', :timeout => 45) |
|
21 |
+ run faye_server |
|
22 |
+ |
|
23 |
+Depois rode o seguinte comando para inicializar o servidor: |
|
24 |
+ |
|
25 |
+```rackup faye.ru -s thin -E product``` |
|
26 |
+ |
|
27 |
+Para fazer os dois servidores iniciarem juntos em *development*, modifique o arquivo ```Procfile```: |
|
28 |
+ |
|
29 |
+ web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb |
|
30 |
+ faye: rackup faye.ru -s thin -E production |
|
31 |
+ |
|
32 |
+Depois execute o comando ```foreman start``` para iniciar o servidor |
|
33 |
+ |
|
34 |
+O servidor **faye** vai iniciar em ```0.0.0.0:9292```. Este servidor vai estar servidondo um arquivo *javascript* que precisa ser adicionado no layout da página no rails: |
|
35 |
+ |
|
36 |
+ <%= javascript_include_tag :defaults, "http://localhost:9292/faye.js" %> |
|
37 |
+ |
|
38 |
+ |
|
39 |
+Em seguida, vamos criar a função para subscrever e enviar mensagens para o servidor. Abra o arquivo ```app/assets/javascripts/application.js```: |
|
40 |
+ |
|
41 |
+ $(function() { |
|
42 |
+ var faye = new Faye.Client('http://localhost:9292/faye'); |
|
43 |
+ faye.subscribe("messages/new", function(data){ |
|
44 |
+ alert(data); |
|
45 |
+ }); |
|
46 |
+ }); |
|
47 |
+ |
|
48 |
+Para fazer um teste, é possivel enviar uma mensagem via **curl**. Primeiro entre ná página no navegador, depois no terminal execute o comando: |
|
49 |
+ |
|
50 |
+``` curl http://localhost:9292/faye -d 'message={"channel":"/messages/new", "data":"hello" }'``` |
|
51 |
+ |
|
52 |
+Ao executar esse comando, o navegador ira receber uma mensagem com o conteúdo do comando. |
|
53 |
+ |
|
54 |
+## Exemplo: Chat |
|
55 |
+ |
|
56 |
+Adicione um *view* com uma lista e um formulário para novas mensagens: |
|
57 |
+ |
|
58 |
+ <ul id="chat"> |
|
59 |
+ <%= render @messages %> |
|
60 |
+ </ul> |
|
61 |
+ |
|
62 |
+ <%= form_for Message.new, remote => true do |f| %> |
|
63 |
+ <%= f.text_field :content %> |
|
64 |
+ <%= f.submit "Send" %> |
|
65 |
+ <% end %> |
|
66 |
+ |
|
67 |
+Crie mais um *view* com o nome de ```create.js.erb``` para resposta do servidor em *JSON*: |
|
68 |
+ |
|
69 |
+ <% broadcast "/messages/new" do %> |
|
70 |
+ $("#chat").append("<%= escape_javascript render(@message) %>"); |
|
71 |
+ <% end %> |
|
72 |
+ $("#new_message")[0].reset(); |
|
73 |
+ |
|
74 |
+Toda vez que uma mensagem for enviada pela página e criada no servidor, o bloco de codigo dentro da função ```broadcast``` vai ser enviado via o faye para todos os clientes que estão assinando este canal. |
|
75 |
+ |
|
76 |
+Agora precisamos criar a função ```broadcast```. No arquivo ```app/helpers/application_helper.rb``` adicione a função: |
|
77 |
+ |
|
78 |
+ module ApplicationHelper |
|
79 |
+ def broadcast(channel, &block) |
|
80 |
+ message = { :channel => channel, :data => capture(&block)} |
|
81 |
+ uri = URI.parse("http://localhost:9292/faye") |
|
82 |
+ Net::HTTP.post_form(uri, :message => message.to_json) |
|
83 |
+ end |
|
84 |
+ end |
|
85 |
+ |
|
86 |
+Pelo fato de estarmos usando a biblioteca **Net:HTTP**, precisamos requerer ela em algum lugar do aplicativo rails. Um bom lugar para se fazer isso é no arquivo ```config/application.rb```. |
|
87 |
+ |
|
88 |
+ require "rails/all" |
|
89 |
+ require "net/http" |
|
90 |
+ |
|
91 |
+Por ultimo temos que fazer uma mudança no arquivo ```app/assets/javascripts/application.js```. Em vez de um alerta, o navegador precisa executar o codigo que for recebido do servidor **faye**: |
|
92 |
+ |
|
93 |
+ $(function() { |
|
94 |
+ var faye = new Faye.Client('http://localhost:9292/faye'); |
|
95 |
+ faye.subscribe("messages/new", function(data){ |
|
96 |
+ eval(data); |
|
97 |
+ }); |
|
98 |
+ }); |
|
99 |
+ |
|
100 |
+Agora ao enviar uma mensagem no formulário da página, todas as instancias abertas da página iram receber a modificação instantaneamente. |
|
101 |
+ |
|
102 |
+## Segurança |
|
103 |
+ |
|
104 |
+É necessário criar um protocolo de segurança, se não qualquer um pode tentar enviar uma mensagem via **curl** para o servidor e rodar *javascript* em todos os clientes que estnao subscrevendo ao sistema do **Faye**. |
|
105 |
+ |
|
106 |
+Para resolver isso, precisamos criar uma extenção no servidor **Faye** para lidar com a segurança. Veja mais informações [aqui](http://faye.jcolan.com/ruby.html). |
|
107 |
+ |
|
108 |
+Basicamente vamos criar uma nova classe que vai conter funções para mensagens entrando e saindo do servidor. Cada uma dessas funções vai fazer um cheque e se algo estiver errado a função levanta um *erro*. Depois criamos uma instancia dessa classe e adicionamos ela como uma extenção do servidor **Faye**. |
|
109 |
+ |
|
110 |
+Primeiro vamos criar um **token** que vai ser enviado junto com as mensagens pelo servidor. Se a mensagem não conter o **token**, o servidor vai levantar um erro. |
|
111 |
+ |
|
112 |
+No arquivo ```config/application.yml``` adicione uma nova **env variable**: |
|
113 |
+ |
|
114 |
+ FAYE_SECRET_OKEN: 12345678901234567890 |
|
115 |
+ |
|
116 |
+Depois precisamos modificar a função **broadcast** em ```app/helpers/application_helper.rb```: |
|
117 |
+ |
|
118 |
+ module ApplicationHelper |
|
119 |
+ def broadcast(channel, &block) |
|
120 |
+ message = { :channel => channel, :data => capture(&block), ext => {:auth_token => env['FAYE_SECRET_OKEN']}} |
|
121 |
+ uri = URI.parse("http://localhost:9292/faye") |
|
122 |
+ Net::HTTP.post_form(uri, :message => message.to_json) |
|
123 |
+ end |
|
124 |
+ end |
|
125 |
+ |
|
126 |
+Por último, no arquivo ```faye.ru``` precisamos criar a classe que vai lidar com a autenticação: |
|
127 |
+ |
|
128 |
+ require 'faye' |
|
129 |
+ |
|
130 |
+ class ServerAuth |
|
131 |
+ def incoming(message, callback) |
|
132 |
+ if message['channel'] !~ %r{^/meta/} |
|
133 |
+ if message['ext']['auth_token'] != env['FAYE_SECRET_OKEN'] |
|
134 |
+ message['error'] = 'Invalid authentication token' |
|
135 |
+ end |
|
136 |
+ end |
|
137 |
+ callback.call(message) |
|
138 |
+ end |
|
139 |
+ end |
|
140 |
+ |
|
141 |
+ faye_server = Faye::RackAdapter.new(:mount => 'faye', :timeout => 45) |
|
142 |
+ faye_server.add_extension(ServerAuth.new) |
|
143 |
+ run faye_server |
|
144 |
+ |
|
145 |
+Ao reiniciar o servidor e tentar enviar uma nova mensagem via **curl** sem o token de autenticação, vamos receber um erro: |
|
146 |
+ |
|
147 |
+ $ curl http://localhost:9292/faye -d 'message={"channel":"/messages/new", "data":"hello" } |
|
148 |
+ HTTP/1.1 400 Bad Request |
|
149 |
+ Content-Type: application/json |
|
150 |
+ Connection: close |
|
151 |
+ Server: thin 1.2.11 codename Bat-Shit Crazy |
|
152 |
+ content-Length: 11 |
|
153 |
+ |
|
154 |
+Mas ao enviar a mensagem atravez da página em rails, a autenticação vai funcionar e a mensagem vai ser recebida por todos que estão subscrevendo a página. |
|
155 |
+ |
|
156 |
+## Outros recursos |
|
157 |
+ |
|
158 |
+ |
|
159 |
+ |
|
160 |
+----------------- |
|
161 |
+ |
|
162 |
+[Index](index.md) |
|
163 |
+ |
|
164 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
165 |
+ |
|
166 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
167 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
168 |
+<script> |
|
169 |
+ hljs.initHighlightingOnLoad(); |
|
170 |
+</script> |
@@ -2,15 +2,14 @@ |
||
2 | 2 |
|
3 | 3 |
## Instalação do [Font Custom](http://fontcustom.com/) |
4 | 4 |
|
5 |
-1. Primeiro instale o [XQuartz](https://xquartz.macosforge.org) |
|
5 |
+Primeiro instale o [XQuartz](https://xquartz.macosforge.org) |
|
6 | 6 |
|
7 |
-2. Rode os seguintes comandos para instalar a ferramenta: |
|
8 |
- |
|
9 |
-```bash |
|
7 |
+Depois rode os seguintes comandos para instalar a ferramenta: |
|
8 |
+ |
|
10 | 9 |
brew install fontforge --with-python |
11 | 10 |
brew install eot-utils |
12 | 11 |
gem install fontcustom |
13 |
-``` |
|
12 |
+ |
|
14 | 13 |
|
15 | 14 |
## Utilização |
16 | 15 |
|
@@ -21,4 +20,16 @@ Para criar um arquivo de configuração, rode o comando ```fontcustom config /pa |
||
21 | 20 |
## Links |
22 | 21 |
|
23 | 22 |
* [FontCustom](http://fontcustom.com/) |
24 |
-* [FontCustom GitHub Page](https://github.com/FontCustom/fontcustom/) |
|
23 |
+* [FontCustom GitHub Page](https://github.com/FontCustom/fontcustom/) |
|
24 |
+ |
|
25 |
+----------------- |
|
26 |
+ |
|
27 |
+[Index](index.md) |
|
28 |
+ |
|
29 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
30 |
+ |
|
31 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
32 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
33 |
+<script> |
|
34 |
+ hljs.initHighlightingOnLoad(); |
|
35 |
+</script> |
@@ -91,6 +91,10 @@ Para ver informações sobre uma tag: |
||
91 | 91 |
|
92 | 92 |
```git show v1.4``` |
93 | 93 |
|
94 |
+Ao usar o comando ```git push```, as **tags** não são transferidas junto com o resto do projeto. É necessario transferir cada **tag** separadamente. Exemplo: |
|
95 |
+ |
|
96 |
+```git push origin v1.4``` |
|
97 |
+ |
|
94 | 98 |
### Links |
95 | 99 |
|
96 | 100 |
- [12 curated git tips and workflow](http://durdn.com/blog/2012/12/05/git-12-curated-git-tips-and-workflows/) |
@@ -27,7 +27,8 @@ |
||
27 | 27 |
22. [Helpers](helpers.md) |
28 | 28 |
23. [SimpleForm](SimpleForm.md) |
29 | 29 |
24. [Nested Model Forms](nested_model.md) |
30 |
-25. [Autenticação de usuários com o Devise](devise.md) |
|
30 |
+25. [Devise](devise.md) |
|
31 |
+26. [Devise Invitable](devise_invitable.md) |
|
31 | 32 |
26. [Upload de arquivos com o CarrierWave](CarrierWave.md) |
32 | 33 |
27. [jQuery File Upload](jQuery_file_upload.md) |
33 | 34 |
28. [Testes](testes.md) |
@@ -37,5 +38,10 @@ |
||
37 | 38 |
32. [Funções de tempo](time_ago.md) |
38 | 39 |
33. [Font Custom](fontcustom.md) |
39 | 40 |
33. [Mailer](mailer.md) |
41 |
+34. [Console](console.md) |
|
42 |
+35. [Resque](resque.md) |
|
43 |
+36. [Bash](bash.md) |
|
44 |
+37. [Faye](faye.md) |
|
45 |
+38. [API](API.md) |
|
40 | 46 |
|
41 | 47 |
*[Links de referencia](links_referencias.md)* |
@@ -40,6 +40,23 @@ Vamos agora criar uma novo metodo para enviar um email de boas vindas aos usuár |
||
40 | 40 |
end |
41 | 41 |
end |
42 | 42 |
|
43 |
+## Criando um view |
|
44 |
+ |
|
45 |
+Crie os seguintes arquivos para os *Views*: |
|
46 |
+ |
|
47 |
+* ```app/views/user_mailer/welcome_email.html.erb``` |
|
48 |
+* ```app/views/user_mailer/welcome_email.text.erb``` |
|
49 |
+ |
|
50 |
+Esses arquivos correspondem as duas versões do email, uma em HTML e outra em texto. As duas são enviadas e o programa em que o usuário está lendo o email decide qual versão mostrar. |
|
51 |
+ |
|
52 |
+#### Links |
|
53 |
+ |
|
54 |
+* [ActionMailer Basics - RailsGuides](http://guides.rubyonrails.org/action_mailer_basics.html) |
|
55 |
+ |
|
56 |
+----------------- |
|
57 |
+ |
|
58 |
+[Index](index.md) |
|
59 |
+ |
|
43 | 60 |
<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
44 | 61 |
|
45 | 62 |
<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
@@ -2,7 +2,7 @@ |
||
2 | 2 |
|
3 | 3 |
Rails **gem** para simplificar o manuseio de varios modelos associados em um único formulario. Ele faz isso de um jeito discreto utilizando *jQuery*. |
4 | 4 |
|
5 |
-#### Instalação |
|
5 |
+## Instalação |
|
6 | 6 |
|
7 | 7 |
Adicione o **gem** no ```gemfile``` e rode o comando ```$ bundle install```: |
8 | 8 |
|
@@ -20,9 +20,31 @@ Inclua o *JavaScript* gerado no layout: |
||
20 | 20 |
|
21 | 21 |
<%= javascript_include_tag :defaults, "nested_form" %> |
22 | 22 |
|
23 |
-#### Utilização |
|
23 |
+## Utilização |
|
24 | 24 |
|
25 |
-#### Links |
|
25 |
+Escrever... |
|
26 | 26 |
|
27 |
-- [nested_form (GitHub)](https://github.com/ryanb/nested_form) |
|
28 |
-- [Dynamic Nested Forms in Rails 3 (madebydna blog)](http://blog.madebydna.com/all/code/2010/10/07/dynamic-nested-froms-with-the-nested-form-gem.html) |
|
27 |
+----------------- |
|
28 |
+ |
|
29 |
+## Informações adicionais |
|
30 |
+ |
|
31 |
+- [Working with nested forms and a many-to-many association in Rails 4](http://www.createdbypete.com/articles/working-with-nested-forms-and-a-many-to-many-association-in-rails-4/) |
|
32 |
+- [Dynamic Nested Forms in Rails 3 (madebydna blog)](http://blog.madebydna.com/all/code/2010/10/07/dynamic-nested-froms-with-the-nested-form-gem.html) |
|
33 |
+- [ActiveRecord nested attributes](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for) |
|
34 |
+ |
|
35 |
+## Recursos |
|
36 |
+ |
|
37 |
+- [ryanb/nested_form](https://github.com/ryanb/nested_form) - ruby gem para simplificar o processo de criação de nested forms |
|
38 |
+ |
|
39 |
+ |
|
40 |
+----------------- |
|
41 |
+ |
|
42 |
+[Index](index.md) |
|
43 |
+ |
|
44 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
45 |
+ |
|
46 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
47 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
48 |
+<script> |
|
49 |
+ hljs.initHighlightingOnLoad(); |
|
50 |
+</script> |
@@ -0,0 +1,17 @@ |
||
1 |
+# Resque |
|
2 |
+ |
|
3 |
+ |
|
4 |
+ Resque.enqueue(WorkerName, vars) |
|
5 |
+ |
|
6 |
+ |
|
7 |
+----------------- |
|
8 |
+ |
|
9 |
+[Index](index.md) |
|
10 |
+ |
|
11 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
12 |
+ |
|
13 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
14 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
15 |
+<script> |
|
16 |
+ hljs.initHighlightingOnLoad(); |
|
17 |
+</script> |
@@ -46,4 +46,14 @@ Exitem varios tipos de testes: |
||
46 | 46 |
- Rcov |
47 | 47 |
- Autotest |
48 | 48 |
|
49 |
-<hr><a class="btn btn-mini" href="readme.md">voltar</a> |
|
49 |
+----------------- |
|
50 |
+ |
|
51 |
+[Index](index.md) |
|
52 |
+ |
|
53 |
+<!-- Highlight syntax for Mou.app, insert at the bottom of the markdown document --> |
|
54 |
+ |
|
55 |
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script> |
|
56 |
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/github.min.css"> |
|
57 |
+<script> |
|
58 |
+ hljs.initHighlightingOnLoad(); |
|
59 |
+</script> |