[Rails] STI with namespace

下面是一個簡單STI(Single Table Inheritance) 的範例

class Animal < ActiveRecord::Base
end

class Dog < Animal
end

class Cat < Animal
end

dog = Dog.create
dog.type #=> "Dog"

當如果子model有namespace時

class Animal < ActiveRecord::Base
end
#models/animal/dog.rb

class Animal::Dog < Animal
end

class Animal::Cat < Animal
end

dog = Animal::Dog.create
dog.type #=> "Animal:Dog"

但如果想要的type是不包含namespace的話就需要override sti_name 這個method, demodulize 是把namespace拿掉,這個method想放在parent model裡也是可以

class Animal::Cat < Animal
    def self.sti_name
    self.name.demodulize
  end
end

另一個做法是orveride store_full_sti_class method

class Animal < ActiveRecord::Base
    def self.store_full_sti_class
    false
  end
end

這樣子type的問題是解決了,但是如果要做操作的話就會有問題會出現下面的error message

ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'Dog'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Animal::Dog.inheritance_column to use another column for that information.

會出現這樣子的錯誤應該是因為他是照type name去找class,但因為已經沒有namespace了所以他找不到model
解決的方法是在parent model orveride find_sti_class method,這樣就可以順利操作了。

class Animal < ActiveRecord::Base
    def self.find_sti_class(type_name)
    type_name = self.name
    super
  end
end

Referance
Single Table Inheritance and 'type' value for namespaced classes

[Rails]Encoding::CompatibilityError (incompatible character encodings: ASCII-8BIT and UTF-8

problam:

最近在跑一個第三方的API,在post資料過去後,對方會打5~7次的post callback回來,並且每一次夾帶著一個記錄著片段PDF的tempfile,我需要讀取每一次tempfile的binary並且跟上一次的binary做concat然後存在postgrasql database裡(Column type is binary),最後再讀出來寫成PDF file。
但在concat binary string的時候出現 Encoding::CompatibilityError (incompatible character encodings: ASCII-8BIT and UTF-8 的error,下面是片段的code

data = IO.read(params[:file].tempfile.path)
my_model.pdf_data << data #=> raise error

solution

寫入時使用 escape_bytea method

my_model.pdf_data << ActiveRecord::Base.connection.escape_bytea(data)

剛剛有說到最後需要把database裡的binary撈出來然後寫成PDF file,這邊也有遇到另一個問題是如果只是單純的把資料讀出來寫成file的話,會造成檔案毀損錯誤無法讀取,這時候就要使用 unescape_bytea 方法把binary讀出來

 f = File.new("public/test.pdf", "w+b")
 f.write ActiveRecord::Base.connection.unescape_bytea(my_model.pdf_data)
 f.close

Referance

Storing binary objects in postgres using Ruby on Rails

[git]自定義git指令

有時候git指令很長要打很多字,為了節省時間(就是懶)這時候就可以透過 alias 去新增自定義的git指令

use command line

ex: git config --global alias.sl 'stash list'
上面的設定就是用 git sl 來取代 git stash list, alias.點的後面就是要新增的指令,字串裡就是要執行的指令。

setting .gitconfig file

可以用編輯器直接打開 .gitconfig 設定檔我是用atom所以指令會是atom . ~/.gitconfig,大概會長下面這樣

[user]
    email = sean.huang@tarokosoftware.com
    name = sean
[core]
    askpass = git-gui--askpass
[alias]
    sl = stash list
    ssp = "!f(){ git stash show -p stash@{$1}; };f"
    ss = stash save -a
    sp = stash pop
    sa = "!f(){ git stash apply stash@{$1}; };f"
    sd = "!f(){ git stash drop stash@{$1}; };f"

在alias底下的就是我設定一些關於 git stash的快捷指令,以 git stash pop 為例可以看到我用
sp = stash pop 的設定,所以之後我只要在terminal裡面輸入 git sp 就會執行 git stash pop

另一個方法是 shell function "!function(){};f" , 這個方法可以使用在需要帶參數或是做一些複合指令的時候
git stash apply stash@{1} 為例,在 stash apply後面需要加上stash@{1}的參數去要決定要apply在git stash list裡的第幾個stash編號,要用shell function來實現這個指令的話就會是這樣

sa = "!f(){ git stash apply stash@{$1}; };f"

把原本的指令寫在"!function(){};f"的大括號裡, $1 代表第一個參數,有多個參數的話就以此類推 $1 $2 $3 ...
所以在用shell function 之後縮短的指令就變成 git sa 1 跟原本的 git stash apply stash@{1} 比是不是簡潔很多了。

如果要用shell function做複合指令的話需要用 && 連接指令像是下面這樣,執行 git sss 就會幫你stash現在的進度並且在terminal上show出stash 的內容。

sss = "!f(){ git stash && git stash show -p ;};f"

shell function的其他用法可以參考下面的連結
Referance
One weird trick for powerful Git aliases
Git 工具 - 儲藏 (Stashing)
Creating a git alias with a parameter

how to run rake task for multiple rspec file

1.create a rake file first

require 'rake'
require 'rspec/core/rake_task'

desc 'Default: run specs.'
task default: :spec

desc 'Run specs'
RSpec::Core::RakeTask.new(:multiple_test) do |t|
  t.pattern = [
    'spec/controllers/api/v1/aa.rb',
    'spec/controllers/products/bb.rb',
    'spec/jobs/prodicuts/cc.rb',
    'spec/services/users/dd.rb',
  ]
end

2.run rake multiple_test

can't not create a rails app with rails new command

if you can't create a rails app Through rails new [app_name]

/Users/seanhuang/.rvm/gems/ruby-1.9.3-p551/gems/bundler-1.13.1/lib/bundler/rubygems_ext.rb:45:in `full_gem_path': uninitialized constant Bundler::Plugin::API::Source (NameError)

you can check your bundler version is v1.13 , if it's you can remove v1.13 and install v1.12 to fix this problm

sudo gem uninstall bundler -v1.13.1 or gem uninstall bundler
sudo gem install -n /usr/local/bin bundler -v 1.12

if you use rvm you can remove bundler follow this way

rm -rf ~/.rvm/gems/ruby-2.0.0-p647/gems/bundler-1.13.1

Refernace
fastlane init fails with `full_gem_path': uninitialized constant Bundler::Plugin::API::Source (NameError)

[Rails]activeadmin skip authentication check

如何跳過會員驗證進入activeadmin後台

1.把下面的code複製到controller的block裡(activeadmin register file)

 def self.filters(kind = nil)
    all_filters = _process_action_callbacks
    all_filters = all_filters.select{|f| f.kind == kind} if kind
    all_filters.map(&:filter)
  end

  def self.before_filters
    filters(:before)
  end

  def self.after_filters
    filters(:after)
  end

  def self.around_filters
    filters(:around)
  end

2.在上面這些method的後面下 binding.pry,重整頁面進到debug模式下然後輸入 filters(:before) 這樣就可以看到在before_filters的時候會呼叫到哪些callback,找有關鍵字 authentication 的callback ex: authenticate_active_admin_user
3.在controller裡的最上面加上 skip_before_filter :authenticate_active_admin_user ,這樣就可以跳過會員登入直接看到admin後台的某個頁面。
參考資料:http://stackoverflow.com/a/31077978

[Git] fast-forward merge跟 non-fast-forward的差別

在用 git merge 合併分支的時候有 fast-forwardnon-fast-forward兩種方式
下面的圖是在分支合併之前

使用non-fast-forward merge

git merge --no-ff branch_name做non-fast-forward merge 的話,會看到git線的右邊會另外開一條線然後保留在aa branch上所做的commit。

使用fast-forward merge

git merge branch_name 做fast-forward merge的話,所有的commit 都會被加到master的線上面,看起來就會像是全部的commit都是在master上做的一樣。

個人是偏好用non-fast-forward的方式做merge,因為這樣在線上可以清楚地看出哪幾個commit是另外開branch做的,然後也比較清楚哪些是新增的功能。

uninitialized constant CarrierWave::Storage::Fog

probulm

gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'

如果carrierwave是用Multiple file uploads這個版本的話,在使用Fog上傳至S3的時候會出現下面的錯誤訊息

uninitialized constant CarrierWave::Storage::Fog
# backtrace line: ~/app/config/initializers/carrierwave.rb:4 (see below)

solution

1.把Fog改成 gem "fog-aws"
2.touch lib/fog.rb 加一個叫fog的rb檔進lib資料夾
3.

lib.fog.eb
module Fog
  # :D 沒有內容

end

4.修改CarrierWave initializer file

config/initializers/carrier_wave.rb
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'
CarrierWave.configure do |config|

end

參考資料:
Declaring only fog-aws in Gemfile: uninitialized constant CarrierWave::Storage::Fog