Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8475 (new defect)

Opened 3 years ago

Last modified 3 years ago

[PATCH] in_place_editor_field doesn't work for namespaced models

Reported by: TylerRick Assigned to: core
Priority: normal Milestone: 2.x
Component: ActionPack Version: edge
Severity: normal Keywords:
Cc: TylerRick

Description

I'm getting this error:

`@namespace/model' is not allowed as an instance variable name

When I try to do this:

<%= in_place_editor_field :'namespace/model', :name %>

To reproduce:

> rails t; cd t

> ./script/generate model Namespace::Model

class CreateNamespaceModels < ActiveRecord::Migration
  def self.up
    create_table :namespace_models do |t|
      t.column 'name', :text
    end
  end

  def self.down
    drop_table :namespace_models
  end
end

development:
  adapter: sqlite3
  database: db/development.sqlite

> rake db:migrate 

> ./script/generate controller whatever

class WhateverController < ApplicationController
  in_place_edit_for :'namespace/model', :name

  def index
    @namespace__model = Namespace::Model.find_or_create_by_name('Tyler')
  end
end

app/views/layouts/whatever.rhtml
<!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
  <%= javascript_include_tag :defaults %>
</head>
<body>
  <%= yield %>
</body>
</html>

app/views/whatever/index.rhtml
<%= in_place_editor_field :'namespace/model', :name %>

To test it against edge Rails, I also did this:

> rake rails:freeze:edge
Exported revision 6838.

/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- active_resource (MissingSourceFile)

Neat!

http://ryandaigle.com/articles/2007/4/26/activeresource-available-as-a-gem/comments/2362#comment-2362

> gem install activeresource --source http://gems.rubyonrails.org --backtrace
ERROR:  While executing gem ... (NoMethodError)
    undefined method `source_index' for #<Hash:0xb7d89bc4>

Neat!

  config.frameworks -= [ :action_web_service, :action_mailer, :active_resource ]
  config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase" }

Proposed solution:

# Based on lib/facets/core/string/methodize.rb
class String
  def methodize
    gsub(/([A-Z])/, '_\1').downcase.gsub(/^_/,'').gsub(/(::|\/)_?/, '__')
  end
end

# File vendor/rails/actionpack/lib/action_controller/macros/in_place_editing.rb, line 23
module ActionController::Macros::InPlaceEditing::ClassMethods
  def in_place_edit_for(model_class, attribute, options = {})
    model_class = model_class.to_s.camelize.constantize unless model_class.is_a?(Class)
    define_method("set_#{model_class.name.methodize}_#{attribute}") do
      @item = model_class.find(params[:id])
      @item.update_attribute(attribute, params[:value])
      render :text => @item.send(attribute)
    end
  end
end

# File vendor/rails/actionpack/lib/action_view/helpers/java_script_macros_helper.rb, line 78
module ActionView::Helpers::JavaScriptMacrosHelper
  def in_place_editor_field(model_name, method, tag_options = {}, in_place_editor_options = {})
    model_class = model_name.to_s.camelize.constantize unless model_class.is_a?(Class)
    methodized_name = model_class.name.methodize
    tag = ::ActionView::Helpers::InstanceTag.new(methodized_name, method, self)
    tag_options = {:tag => "span", :id => "#{methodized_name}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_opt
    in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{methodized_name}_#{method}", :id => tag.object.id })
    tag.to_content_tag(tag_options.delete(:tag), tag_options) +
    in_place_editor(tag_options[:id], in_place_editor_options)
  end
end

I don't feel up to the task of writing an automated test for it currently, but I have confirmed that it works in the test app described above.

Related error: Could not find table 'models'

  def index
    Namespace::Model.find_or_create_by_name('Tyler')
  end
Could not find table 'models'

Shouldn't it use 'namespace_models' by default? Or is that only if #6792 gets applied? The workaround for now is obviously:

# app/models/namespace/model.rb
class Namespace::Model < ActiveRecord::Base
  set_table_name 'namespace_models'
end

Related error: Called id for nil, which would mistakenly be 4

If you forget to set an instance variable (or misspell something), you currently get this really unhelpful error :

Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id

I propose this change:

# vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb
class ActionView::Helpers::InstanceTag
  def object
    @object || @template_object.instance_variable_get("@#{@object_name}") || raise("Could not find instance variable named @#{@object_name} in template")
  end
end

which would produce this:

Could not find instance variable named @namespace__model in template

(Should I open a separate ticket?)

See also:

#6792:

"Since 1.2 will offer better support for models placed in modules, ..."

Module names are now used as table_name prefixes, so Content::Folder.table_name will be 'content_folders'. The (last) module name will be singular, the class name will be plural by convention.

So are we moving to using demodulize on inheritance types, or towards not using it? I would prefer the latter.

We're not using demodulize on types from now on. That's how it should work and it truly allows namespaces, also at the db/table level.

#4484: "This is a patch to allow in_place_edit_for to support a :class_name option, allowing more flexibility for instance variable naming in views." (Might be another alternative solution to this problem. Although I'd prefer just to have a sensible default behavior.)

#7557: "Out of interest, why does ActiveRecord demodulize the class name before stuffing it in the type field?" (Why does it demodulize ... Wouldn't it be best to always keep the full scope?)

Change History

05/25/07 22:34:03 changed by TylerRick

Sorry, one of the lines got chopped off because I didn't have linewrap turned on (could this, just possibly, indicate that the line was too long to begin with?).

Here's the corrected version:

# File vendor/rails/actionpack/lib/action_view/helpers/java_script_macros_helper.rb, line 78
module ActionView::Helpers::JavaScriptMacrosHelper
  def in_place_editor_field(model_name, method, tag_options = {}, in_place_editor_options = {})
    model_class = model_name.to_s.camelize.constantize unless model_class.is_a?(Class)
    methodized_name = model_class.name.methodize
    tag = ::ActionView::Helpers::InstanceTag.new(methodized_name, method, self)
    tag_options = {:tag => "span", :id => "#{methodized_name}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
    in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{methodized_name}_#{method}", :id => tag.object.id })
    tag.to_content_tag(tag_options.delete(:tag), tag_options) +
    in_place_editor(tag_options[:id], in_place_editor_options)
  end
end