Rails中STI的RESTful routes问题

不解释,你是知道STI的——single table inheritance,我们看下问题是什么

现在有两个Model:AssetImage

class Asset < ActiveRecord::Base; end
class Image < Asset; end

我们定义了这样一个RESTful routes

resources :assets

然后在我们的AssetsController里面

class AssetsController < ApplicationController
  def create
    @asset = Asset.new params[:asset]
    @asset.save
    respond_with(@asset)
  end

  def update
    @asset.update_attributes params[:asset]
  end
end

@asset是一个Asset而不是Image时,一切正常,不过当@assetImage时。。。


我们如果创建一个新的Image对象,便会收到这样的错误

undefined method `image_url' for #<AssetsController:0xb495718>

分析下错误来源,Rails中这些url_helpers都是依靠polymorphic_path生成的,它会检测当前model是哪一类的对象,很显然@asset是一个Image,于是就杯具的生成了一个image_url方法,这一切都是因为polymorphic_path不懂的向上继续寻找

我觉得理想的状态应该是先寻找image_url,如果没有就继续找它的superclass,也就是asset_url

这里有一个ticket,其中给出了一个Rails2的patch,那我只好自己来给Rails3做补丁了

找到action_controller/polymorphic_routes.rb,然后你懂的

def build_named_route_call(records, inflection, options = {})
  unless records.is_a?(Array)
    record = extract_record(records)
    route  = ''
  else
    record = records.pop
    route = records.inject("") do |string, parent|
      if parent.is_a?(Symbol) || parent.is_a?(String)
        string << "#{parent}_"
      else
        string << RecordIdentifier.__send__("plural_class_name", parent).singularize
        string << "_"
      end
    end
  end

  if record.is_a?(Symbol) || record.is_a?(String)
    route << "#{record}_"
    action_prefix(options) + route + routing_type(options).to_s
  else
    record_class = record.class
    named_route = build_candidate_named_route_call(route, record_class, inflection, options)
    until(respond_to?(named_route) || record_class.superclass == ActiveRecord::Base) do
      record_class = record_class.superclass
      named_route = build_candidate_named_route_call(route, record_class, inflection, options)
    end
    named_route
  end
end

private
def build_candidate_named_route_call(route, record_class, inflection, options)
  origin = route.dup
  origin << ActionController::RecordIdentifier.__send__("plural_class_name", record_class)
  origin = origin.singularize if inflection == :singular
  action_prefix(options) + origin + "_" + routing_type(options).to_s
end

如此,我们就可以很爽的使用asset_url了,管你是不是个Image


还有就是我们update一个@asset时,如果这个@asset又不幸的是一个Image,那些很有用的form_helpers生成的表单就会是类似这样的

<input name="image[title]" id="image_title" />

是的,我们的AssetsController就无法得到params[:asset],而是一个很杯具的params[:image]

我没找到什么好的解决方案,只好给AssetsController加了一个before_filter

def justify_params
  if params[:image]
    params[:asset] = params[:image]
  elsif params[:raw]
    params[:asset] = params[:raw]
  end
end

都结束了,奖励那些看完这篇烂文的人,也就是我了^ ^

mm

4 COMMENTS >>LEAVE<<

  1. aoyoo

    我表示我只是来看看美女

  2. ABitNo
    @aoyoo

    你是不是应该弄个头像了--

  3. SunRise
  4. vzomik

    我一直拉~直到最后的时刻,停下了。。。

LEAVE A RESPONSE >>CANCEL<<

loader