Rails add a search function controller and route best practice

1.Task description

I worked on a Ruby on Rails app, and one day I got a task to add this new feature:

Create a search page(So user can search items by category).

After hitting the “search” button, it takes the user to item list page.

 

For simplicity and to avoid dragging  myself away from the main topic I want to talk about:

The search page could simply look like this:

1

The item list page could just look like this:

1

 

2.My initial approach

2.1 Strategy:

Q: How to render the search page?

A: Create a search action.

Q: How to perform the search function?

A: Create a index action, which handles the search function. The search form should submit a post request to this action.

Q: How to render the item list page?

A: Let index action render item list page.

2.2 Implementation:

Route:

config/routes.rb:

get ‘items/search’, to: ‘items#search’
post ‘items/index’, to: ‘items#index’

1

Controller:

items_controller.rb:

class ItemsController < ApplicationController
def index
# perform search code goes here
render ‘index’
end

def search
render ‘search’
end
end

View:

/items/search.html.erb:

<%= form_tag items_index_path, method: ‘post’ do %>
<label>category:</label>
<input type=”text” name=”category”>
<button>search</button>
<% end %>

2.3 Implementation in action

1

after hitting the search button:

1

2.4 Problem

This works perfectly, until I manually refreshed the index page.

1

Since there’s no get route, it errors out! Of course I could add a get route, but in that way, I will have to store the form parameters in session. Is there another approach?

 

 

3. Second approach

3.1 Strategy:

Q: How to render the search page?

A: Create a search action.

Q: How to perform the search function?

A: Create a index action, which handles the search function. The search form should submit a get request to this action, so that the search parameters are in the URL, so that I don’t need to worry about storing them in session.

Q: How to render the item list page?

A: Let index action render item list page.

A note: 

In the future, there might be other tasks to add view item detail, edit item functions and on. So it’s better  use rails default resource: http://guides.rubyonrails.org/routing.html

3.2 Implementation:

Route:

config/routes.rb:

get ‘items/search’, to: ‘items#search’
resources :items, only: [:index] do
end

1

Controller:

The same as first approach.

 

View:

/items/search.html.erb:

<%= form_tag items_path, method: ‘get’ do %>
<label>category:</label>
<input type=”text” name=”category”>
<button>search</button>
<% end %>

3.3 Implementation in action

1

after hitting the search button:

1

 

3.4 Concern

We can see the parameters are passed in the URL, and I’m good even though I refresh.

This approach definitely works. The only concern is that it brought in a search action, which is not part of rails default resource; and the search page and item list page are on different URL.

I want to utilize rails default resource, and keep search and item list page on the same URL.

So here comes the third approach.

 

 

4. Third approach

4.1 Strategy:

Q: How to render the search page?

A: Use index action. Check if user is not performing a search, render search page. (Trick is add a hidden input in the form)

Q: How to perform the search function?

A: Perform it in index action.

Q: How to render the item list page?

A: Use index action. Check if user is performing a search, render index page.

 

 

4.2 Implementation:

Route:

config/routes.rb:

resources :items, only: [:index] do
end

Controller:

class ItemsController < ApplicationController
def index
if params[:search].blank?
render ‘search’
else
# perform search code goes here
render ‘index’
end
end
end

View:

/items/search.html.erb:

<%= form_tag items_path, method: ‘get’ do %>

<input type=”hidden” name=”search” value=”true”>
<label>category:</label>
<input type=”text” name=”category”>
<button>search</button>
<% end %>

3.3 Implementation in action

1

after hitting the search button:

1

 

 

Advertisements

Deploy rails app with GoCD and Capistrano simplified log file

Clean work directory   “/var/lib/go-agent/pipelines/ROMUI_DEV_DEPLOY”

Create git artifact    “pipelines/ROMUI_Dev_Deploy/Configuration”

Set GoCD ENV variable

Start to build task    ROMUI_Dev_Deploy/649/dev_deployment/1/ha-deploy-runOnAll-1 on lvsdevromuiapp04-01.us.gspt.net  [/var/lib/go-agent]

    Fetch Artifact from CI    

  • execute task: <fetchartifact pipeline=”ROMUI_CI_Deploy” stage=”ci_deployment” job=”run_tests” srcdir=”romui”/>
  • fetch artifact [romui] from [ROMUI_CI_Deploy/675/ci_deployment/run_tests
  • save artifact to [pipelines/ROMUI_Dev_Deploy]

Fetch Artifact from config repo

     bundle install 

bundle exec cap development_integration deploy target=localhost stack=04 branch=develop

  • Run passenger: running /usr/bin/env which passenger as rails@localhost
  • Run RVM:
  •       running /usr/bin/env [-d~/.rvm] as rails@localhost
  •       command: /usr/local/rvm/bin/rvm   default do ruby –version
  • Check git access:  command: (export GIT_ASKPASS=”bin/echo” GIT_SSH=”/tmp/romui/git-ssh.sh”; /usr/bin/env git ls-remote –heads https://gitlab.gspt.net/romui/romuiweb.git
  • INFO The repository mirror is at /rails/apps/deploy/repo
  • command: /usr/bin/env  ln -s /rails/apps/deploy/shared/vendor/bunlde   /rails/apps/deploy/releases/20160728145958/vendor/bundle
  • command: ln -nfs /rails/apps/config/current/.env   /rails/apps/deploy/releases/20160728145958/.env
  • Run ‘rake db:migrate’
  •      command: cd /rails/apps/deploy/releases/20160728145958 && (export RAILS_ENV=”development_integration”; /usr/local/rvm/bin/rvm    default to bundle exec rake db:migrate)
  • Restart app
  •      running /usr/bin/env passenger_config restart_app /rails/apps/deploy –ignore -app-not-running as rails@localhost
  •      command: cd /rails/apps/deploy/releases/20160728145958 && /usr/bin/env passenger-config restart-app /rails/apps/deploy –ignore-app-not-running
  •       restarting /rails/apps/deploy/current
  • Clean up
  •     INFO: keeping 5 of 6 deployed releases on localhost
  •     running /usr/local/rvm/bin/rvm    default to bundle exec rake tmp:clear as rails@localhost

 

Capistrano deploy flow outline

Official flow reference: http://capistranorb.com/documentation/getting-started/flow/

An example flow:

Deploy: starting

Rbenv ensures that the version we configured on config/deploy.rb is installed and that it can write on disc.

Checks git repository

Create needed folder tree

/var/www

App_name

Shared

Public

Assets

Releases

 

Deploy: updating

Clone the whole repo in /var/www/app_name/repo

Creates a new folder date-time named on /var/www/app_name/releases/20140717180754

Copies the project there

 

Deploy: publishing

Runs bundle install in last release folder

Runs assets:precompile copying our minimized assets to shared/public/assets

Creates a soft link from last release to /var/www/app_name/current

Restar server.

 

Deploy:finishing

Logs deployment to revisions.log

Capistrano commands

cap deploy:setup

This will SSH to your server and create some directories in the folder you specified with deploy_to, where Capistrano will store your releases, and your shared files (e.g., logs, configs, static assets like updates, etc.). If something goes wrong with permission or SSH access, you’ll see some error messages. Fix these before proceeding so you know you can actually make a connection to your server.

This should setup the following directories(if you’ve specified to install your app in /app_name/):

/app_name/current

/app_name/shared

/app_name/releases

 

The “releases” directory is where copies of all your actual code are stored.

“shared” is a place where you can put common, shared files like logs, static assets, and maybe config files like database.yml.

“current” is simply a symbolic link that points to the current release inside the “releases” directory (capistrano updates this on each deploy)

 

 

cap deploy:check

Check your local environment and your server and try to locate any possible issues.

 

 

cap deploy:update_code

Update application code