Welcome to APG-Inventory

Create a new repository on the command line

touch README.md

git init git add README.md git commit -m “first commit” git remote add origin github.com/ycchen/apg-inventory.git git push -u origin master Push an existing repository from the command line

git remote add origin github.com/ycchen/apg-inventory.git git push -u origin master

  1. create arl-inventory application

$ rails new arl-inventory

  1. added devise gem in Gemfile

User Model

  1. install devise gem

$ rails g devise:install

  1. create user model

$ rails g devise User

  • added :display_name field, and enable all of additional fields and indexes

$ rails db:migrate

  • Updated appliction.html.erb with layout

  • created flashes_helper in apphelpersapplication_helper.rb

def flashes_helper

results = []
flashes = [:alert, :error, :info, :notice, :success]

flashes.each do |name, msg|
  hidden = "hide" if flash[name].blank?
  results << content_tag(:div, content_tag(:p, flash[name]), class: "alert alter-#{name} #{hidden} alert-block").html_safe
end
results.join("").html_safe

end

  • created title and show_title? method in apphelpershome_helper.rb

def title(page_title, show_title=true)

content_for(:title){h(page_title.to_s)}
@show_title = show_title

end

def show_title?

@show_title

end

  • add twitter bootstrap library to application.css and application.js

  1. generated home controller

$ rails g controller home index

  • update configroutes.rb point root to home#index

  1. generated inventory model

$ rails g scaffold inventory stockenumber:string name:string description:text barcode:string purchase_date:date

$ rails g scaffold inventory_status name:string

$ rails g scaffold location name:string address:string latitude:float longitude:float

$ rails g scaffold inventory_record user:references inventory:references inventory_status:references location:references latitude:string longitude:string

  • Added relationship between all models and validations

  • models/user.rb

    validates :email, :presence => true validates :password, :presence => true validates :password_confirmation, :presence => true validates :display_name, :presence => true

    has_many :inventory_records has_many :inventories, :through => :inventory_records

  • models/inventory.rb validates :barcode, :presence => true validates :description, :presence => true validates :name, :presence => true validates :purchase_date, :presence => true

  • models/inventory_status.rb

    validates :name, :presence => true has_many :inventory_records

  • models/location.rb

    validates :name, :presence => true validates :latitude, :presence => true validates :longitude, :presence => true

    has_many :inventory_records

  • models/inventory_record.rb

    belongs_to :user belongs_to :inventory belongs_to :inventory_status belongs_to :location

User model

  • Added avatar method

def avatar

Gravatar.new(self.email).image_url

end

  1. generated pre-populate data set in dbseed.rb

  2. added RoutingError handler at the end of routes.rb

  • configroutes.rb

match ‘*path(.:format)’, :to=> ‘application#routing_error’

  • controllerapplication_controller.rb

def routing_error

render_not_found

end

def render_not_found

flash[:notice] = 'The object you tried to access does not exist!'
redirect_to root_path

end

  1. added ActiveRecord::RecordNotFound handler

  • controllerapplication_controller.rb

rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found

  1. generated User view for customization

$ rails g devise:views devise

  1. Added javascript to handle slidUp alert div in application.js

$(function(){

$('.alert').delay(5000).slideUp('slow');

});

  1. Added validation for InventoryRecord model and added more field in attr_accessible

/*

attr_accessible 

validates :user_id, :presence => true
validates :inventory_id, :presence => true
validates :inventory_status_id , :presence => true
validates :location_id, :presence => true

*/

  1. Added “rolify”, “cancan” gems to handle user roles

  • modified Gemfile

  • gem ‘rolify’ # role management

  • gem ‘cancan’ # authroization

  1. Create the Ability class from Cancan

$ rails generate cancan:ability

  1. Create the Role class from rolify

$ rails generate rolify:role

  1. Run migrations

$ rake db:migrate

Added “cancan” gem to handle user roles: cancan expect a curent_user method to exist in the controller, so we need to setup some authentication (such as Devise)

/*********** this is replaced by using rolify gem

  1. Added typical users HABTM (Has and belongs to many) roles relationship

$ rails g scaffold role name:string

$ rake db:migrate

$ rails g scaffold roles_users role:references user:references

$ rake db:migrate

  1. Added relationship between user and role model

  • user model

has_many :roles_users has_many :roles, :through => :roles_users

  • role model

has_many :roles_users has_many :users, :through => :roles_users

*****************/

************** READ THIS BEFORE ENABLE config.use_dynamic_shortcuts

stackoverflow.com/questions/12672229/heroku-doesnt-work-rake-command-pgerror-error-relation-roles-does-no

BY ENABLE config.use_dynamic_shortcuts will cause db:migrate raise exception of “could not find table roles”, you will need to run db:migrate first then uncomment dynamic_shortcuts each time you want to initial the application

danielboggs.com/articles/rails-authentication-and-user-management-continued

**************

  1. enable dynamic shortcuts for user class (user.is_admin? like methods), uncomment “config.use_dynamic_shortcuts

" the line in 'config/initializers/rolify.rb'
  1. added gmaps4rails gem to Gemfile for Google Maps for Rails

github.com/apneadiving/Google-Maps-for-Rails

learningpoint2.wordpress.com/2012/12/19/google-map-in-rails/

gem ‘gmaps4rails’

  • copy the assets to your app

$ rails g gmaps4rails:install

  • updated modelslocation.rb

acts_as_gmappable :latitude => ‘latitude’, :longitude => ‘longitude’, :process_geocoding => :geocode?, :address => ‘address’, :normalized_address => ‘address’,:msg => “Sorry, not even Google could figure out where that is”

def geocode?
  (!address.blank? && (latitude.blank? || longitude.blank?)) || address_changed?
end

def gmaps4rails_address
address     
end

def gmaps4rails_infowindow
  "<h4>#{name}</h4>" << "<h4>#{address}</h4>"
end
  • Added @json = Location.all_to_gmaps4rails in controllers/location.rb

  • Added following in layoutapplication.html.erb at the footer area

<%= yield :scripts %>

  • Added <%= gmaps4rails(@json) %> in views/locations/index.html.erb

  1. Added :unregistered scope in Inventory model modlesinventory.rb

scope :unregistered, where(“id NOT IN (select inventory_id from inventory_records where inventory_records.inventory_id = inventories.id)”)

  • modified viewinventory_form.html.erb to handle different params to user Inventory.all or Inventory.unregistered

  1. Added dashboard controller

$ rails g controller dashboards show

debug

guides.rubyonrails.org/debugging_rails_applications.html

  1. debug

<%= debug @post %>

  1. to_yaml

<%= simple_format @post.to_yaml %>

  1. inspect

<%= @location.inspect %>

  1. Rails.logger.debug()

  2. using irb console to test the condition

t = “hello” c = “world” # if (t == c )

  desc 
else 
  asc

v = (t == c) ? “desc” : “asc” # v = “asc”

  1. Generated following models

$ rails g scaffold category name:string description:text

$ rails g scaffold brand name:string

$ rails g scaffold group name:string description:text

generate join models =====================
  1. Generate user controller

$ rails g controller Users index

  1. Added isAdmin? in controllerapplication_controller.rb

def isAdmin?
  unless current_user && current_user.admin?
      flash[:notice]= "You are not authorize to access this page!"
      redirect_to root_path
  end
end

Add before_filter : isadmin? for all models controllers

  1. Added access restriction for controller - Added before_filter on all controllers that restrict only admin can get to it

before_filter :authenticate_user! before_filter :isAdmin?

  1. Added pagination for all models using Kaminari gem

Kaminari configuration options

$ rails g kaminari:config # generated config/initializers/kaminari_confifg.rb file

github.com/amatsuda/kaminari

If you want to change default “per_page” value, you can do it on each model

# Kaminari will crashed for ruby 1.9.3-p362, but works fine on ruby 1.9.3-p392

class User < ActiveRecord::Base

paginates_per 10

end

railscasts.com/episodes/254-pagination-with-kaminari?view=asciicast

  1. Added inventory_categories model

$ rails g model inventory_category inventory:references category:references

$ rake db:migrate

Added relationship between category, inventory, and inventory_category models

# category model has_many :inventory_categorys has_many :inventories, :through => :inventory_categorys

# inventory model has_many :inventory_categorys has_many :categories, :though => :inventory_categorys

# inventory_category model belongs_to :inventory belongs_to :category

Added following method to modelsinventory.rb, and added :category_ids in attr_accessible

def category_ids=(params)

self.categories =[]
params.each do |id|
  unless id.to_s.empty?
    category = Category.find(id)
    categories << category
  end
  save
end

end

  1. Updated seed.rb file for pre-populate inventory data with assigned categories.

Multiple select box #1

<%= f.collection_select(:inventory_id, Inventory.unregistered, :id, :name, {include_blank: true}, {multiple: true}) %>

Multiple select box #2

<%= f.select(:inventory_id, Inventory.unregistered.collect{|x| [x.name, x.id]}, {}, {:multiple => true, :size => 10}) %>

Multiple select box #3

<%= select_tag(“inventory_record[]”, options_from_collection_for_select(Inventory.unregistered,“id”, “name”), :multiple => true) %>

Checkboxes

<% Inventory.unregistered.each do |item| %>

<%= check_box_tag "inventory_record[inventory_id][]", item.id %>
<%= lable_tag dom_id(ite), item.name %>

<% end %>

  1. Generate Unique Random string in ruby

$ require ‘securerandom’ $ SecureRandom.hex(13) $ SecureRandom.hex(15) $ SecureRandom.hex(32)

api_string = Array.new(32){rand(36).to_s(36)}.join

ActiveSupport::SecureRandom.hex(32)

  1. Added handreciept model/controller/view

$ rails g scaffold hand_reciept reciept:string user:references

/*

  1. Added handreciept_detail model/controller/view

$ rails g scaffold handreciept_detail hand_reciept:references inventory:references starts_at:datetime ends_at:datetime out_at:datetime in_at:datetime out_assistant_id:integer in_assistant_id:integer request_note:text approver_id:integer approval_note:text location:references inventory_status:references */

apg

  1. Generated models

$ rails g scaffold component_model brand:references name:string description:text training_required:boolean autocomplete:string

$ rails g scaffold budget name:string number:string starts_at:date ends_at:date

$ rails g scaffold kit location:references budget:references tombstoned:boolean checkoutable:boolean cost:decimal insured:boolean

$ rails g scaffold component kit:references component_model:references asset_tag:string serial_number:string barcode:string

regenerated schema.rb

$ rake db:schema:dump

  1. Added validations for budget model

custom method validation for date range

validate :starts_at_must_be_before_ends_at, :fields =>[:starts_at, :ends_at]

def starts_at_must_be_before_ends_at

errors.add(:starts_at, "must be before ends_at") unless starts_at < ends_at

end

  1. Added nested_form gem

$ rails g nested_form:install

change_column :products, :price, :decimal, :precision => 6, :scale => 2

  1. Added draper gem for model/view decorators (presenters)

$ rails g resource Article $ rails g decorator kit

railscasts.com/episodes/17-habtm-checkboxes?view=asciicast

  1. product[]

to check if the product[] is pass in the update method do this This will set the params[:category_ids][] to an empty [] array, if the params[:category_ids][] does not pass in

def update

params[:product][:category_ids][] ||=[]

end

params[:category_ids][] will only generate from has_and_belongs_to_many, if we set up as has_many :through, we will have to generate category_ids manually.

  1. Generated hand_reciepts_inventories model

$ rails g scaffold hand_reciept_details hand_reciept:references inventory:references inventory_status:references location:references user:references

$ rake db:migrate

Nested_form example

stackoverflow.com/questions/9649063/autocomplete-using-nested-form-on-has-many-through-relationship

stackoverflow.com/questions/11305133/rails-nested-form-with-has-many-through-not-saving-the-data-to-joining-table

stackoverflow.com/questions/13506735/rails-has-many-through-nested-form

38. $ rails g scaffold physician name:string $ rails g scaffold patient name:string $ rails g scaffold appointment physician:references patient:references appt_date:datetime note:text

added nested_form for hand reciepts model

stackoverflow.com/questions/7000605/how-to-implement-has-many-through-relationships-with-mongoid-and-mongodb

stackoverflow.com/questions/11480939/many-to-many-has-many-through-association-form-with-data-assigned-to-linking-m

  1. Generated migration script to add hand_reciept_id to Inventory Record model

$ rails g migration add_hand_reciept_id_to_inventory_records hand_reciept_id:integer

  1. Generated migration script to add appt_time and modify appt_date to date

$ rails g migration add_appt_time_to_appointments appt_time:time $ rails g migration change_appt_date_to_appointments appt_date:date

  1. To concatenate 2 files in select box

<%= f.select(:hand_reciept_id, HandReciept.includes(:user).all.collect{|hr| [(hr.user.display_name + “ - ” + hr.reciept), hr.id]}) %>

<%= f.select(:hand_reciept_id, HandReciept.includes(:user).all.collect{|hr| [(hr.user.display_name+ “ - ” + hr.reciept), hr.id]}, {:include_blank => ‘Select Hand Reciept’}) %>

<%= f.select(:hand_reciept_id, HandReciept.includes(:user).all.map{ |c| [(c.user.display_name + “-” + c.reciept), c.id] }, :include_blank => “- Choose Name -”) %>

  1. How to reference a scope in other models

defined a scope in user model called “no_hand_reciept_yet” This is out you reference to “no_hand_reciept_yet” in viewshand_reciepts_form.html.erb

<%= f.select(:user_id, User.no_hand_reciept_yet.map{|c|})%>

  1. Added sort option on inventory records page.

index: appcontrollersinventory_records_controller.rb params ||= “created_at desc”

  1. Added sortable helper method in apphelpersapplication_helper.rb

def sortable(column, title = nil)
  title ||= column.titleize
  direction = (column == params[:sort] && params[:direction] == "asc") ? "desc" : "asc"
  link_to title, :sort => column, :direction => direction
end

<%= link_to "User", :sort =>"users.display_name %>
Replace with 
<%= sortable("users.display_name","User") %>

app\controllers\inventory_records_controller.rb

def index
  @inventory_records = InventoryRecord.includes([:user, :inventory,:location, :inventory_status]).order(params[:sort] + ' ' + params[:direction])  
end

# Re-factor the sortable method

app\controllers\inventory_records_controller.rb

helper_method :sort_column, :sort_direction
def index
  @inventory_records = InventoryRecord.includes([:user, :inventory,:location, :inventory_status]).order(sort_column + ' ' + sort_direction)  
end

# In order to access those 2 method thru the view, we need to enable them to be accessable by the view, we need to call the helper_method to make them avaliable by view

private
  def sort_column
    params[:sort] ||= "created_at"
  end
  def sort_direction
    params[:direction] ||= "desc"
  end

  app\helpers\application_helper.rb
    def sortable(column, title = nil)
  title ||= column.titleize
  direction = (column == sort_column && sort_direction =="asc") ? "desc" : "asc"
  link_to title.titleize, :sort => column, :direction => direction
end
  1. Make rails console just output the query only

# add nil at the end of each query commands/statements $ users = User.all; nil

# best output on rails console using awesome_print and hirb (hirb does not work with rails 3.2)

  1. use Ruby constant “RUBY_PLATFORM” to find out which OS ruby is runnin gon

<%= RUBY_PLATFORM %> i386-mingw32, i686-linux

# add show_debug method to dump request scope variables in helpersapplication_helper.rb

def show_debug
  if Rails.env.development?
    request.env.each do |e|
      debug("#{e[0]} =  #{e[1]}")
    end
  end
end

<% if Rails.env.development? %>
  <% request.env.each do |e| %>
    <%= debug(e[0].to_s + ' = ' + e[1].to_s) %>
  <% end %>
<% end %>
  1. committed for updated sortable method to ensure the arrow image shows up

  2. restrict access for inventory controller on edit, update, and destroy, and allows users can view inventory list and inventory details, hidde edit option for non admin user

add to all view links where you want to have access control

<% if can? :manage, @inventory %>

<%= link_to  %>

<% end %>

  1. modified search method to add couple more fields to be search. Enable search form for all users.

modelsinventory_record.rb

def self.search(search)

end

  1. committed - Modified search query, added can? on views