Permalink
Browse files

Add support for Mongoid embedded relations

  • Loading branch information...
mshibuya committed Mar 16, 2012
1 parent ddd8934 commit 163da3e95a1afcf770e8b6dba8a4b82ad3634647
View
@@ -12,6 +12,7 @@
/spec/generators/tmp
/spec/lib/tmp
Gemfile.lock
+Gemfile31.lock
coverage/*
db/*.sqlite3
db/*.sqlite3-journal
@@ -2,13 +2,15 @@
.btn-group
%a.btn.btn-info.toggler{:'data-toggle' => "button", :'data-target' => "#{form.jquery_namespace(field)} > .tab-content, #{form.jquery_namespace(field)} > .controls > .nav", :class => (field.active? ? 'active' : '')}
%i.icon-white
- = form.link_to_add "<i class=\"icon-plus icon-white\"></i> #{wording_for(:link, :new, field.associated_model_config.abstract_model)}".html_safe, field.name, { :class => 'btn btn-info' }
+ - unless field.nested_form[:update_only]

This comment has been minimized.

Show comment
Hide comment
@bbenezech

bbenezech Mar 16, 2012

Collaborator

Haha, nice! I totally overlooked the :update_only option. Cool :-)

@bbenezech

bbenezech Mar 16, 2012

Collaborator

Haha, nice! I totally overlooked the :update_only option. Cool :-)

+ = form.link_to_add "<i class=\"icon-plus icon-white\"></i> #{wording_for(:link, :new, field.associated_model_config.abstract_model)}".html_safe, field.name, { :class => 'btn btn-info' }
= form.errors_for(field)
= form.help_for(field)
%ul.nav.nav-tabs{ :style => 'margin-top:5px' }
.tab-content
= form.fields_for field.name do |nested_form|
- = nested_form.link_to_remove '<span class="btn btn-small btn-danger"><i class="icon-trash icon-white"></i></span>'.html_safe
+ - if field.nested_form[:allow_destroy]

This comment has been minimized.

Show comment
Hide comment
@bbenezech

bbenezech Mar 16, 2012

Collaborator

Less sure about that one. Newly added nested forms should be removable in all cases.

What do you think?

@bbenezech

bbenezech Mar 16, 2012

Collaborator

Less sure about that one. Newly added nested forms should be removable in all cases.

What do you think?

This comment has been minimized.

Show comment
Hide comment
@mshibuya

mshibuya Mar 16, 2012

Collaborator

You're right, certainly. I've missed that case.
I'll try to find a way to handle that.

@mshibuya

mshibuya Mar 16, 2012

Collaborator

You're right, certainly. I've missed that case.
I'll try to find a way to handle that.

+ = nested_form.link_to_remove '<span class="btn btn-small btn-danger"><i class="icon-trash icon-white"></i></span>'.html_safe
= nested_form.generate({:action => :nested, :model_config => field.associated_model_config, :nested_in => field.name })
= form.javascript_for(field) do
@@ -1,4 +1,5 @@
if defined?(::Mongoid::Document)
require 'rails_admin/adapters/mongoid/extension'
Mongoid::Document.send(:include, RailsAdmin::Adapters::Mongoid::Extension)
+ Mongoid::NestedAttributes::ClassMethods.send(:include, RailsAdmin::Adapters::Mongoid::NestedAttributesExtension)
end
@@ -97,6 +97,10 @@ def encoding
Rails.configuration.database_configuration[Rails.env]['encoding']
end
+ def embedded?
+ false
+ end
+
private
def query_conditions(query, fields = config.list.fields.select(&:queryable?))
@@ -70,7 +70,7 @@ def associations
:polymorphic => association_polymorphic_lookup(association),
:inverse_of => association_inverse_of_lookup(association),
:read_only => nil,
- :nested_form => nil
+ :nested_form => association_nested_attributes_options_lookup(association)
}
end
end
@@ -128,6 +128,10 @@ def encoding
'UTF-8'
end
+ def embedded?
+ @embedded ||= !!model.associations.values.find{|a| a.macro.to_sym == :embedded_in }
+ end
+
private
def query_conditions(query, fields = config.list.fields.select(&:queryable?))
@@ -260,6 +264,10 @@ def association_foreign_type_lookup(association)
end
end
+ def association_nested_attributes_options_lookup(association)
+ model.nested_attributes_options.try { |o| o[association.name.to_sym] }
+ end
+
def association_as_lookup(association)
association.as.try :to_sym
end
@@ -22,6 +22,26 @@ def safe_send(value)
end
end
end
+
+ module NestedAttributesExtension
+ extend ActiveSupport::Concern
+
+ included do
+ attr_reader :nested_attributes_options
+ alias_method_chain :accepts_nested_attributes_for, :rails_admin
+ end
+
+ # Mongoid accepts_nested_attributes_for does not store options in accessible scope,
+ # so we intercept the call and store it in instance variable which can be accessed from outside
+ def accepts_nested_attributes_for_with_rails_admin(*args)

This comment has been minimized.

Show comment
Hide comment
@bbenezech

bbenezech Mar 16, 2012

Collaborator

FieldTest.nested_attributes_options[:nested_field_tests] works out of the box and is part of ActiveModel's API, which is fine for us.

Did I miss smtg?

@bbenezech

bbenezech Mar 16, 2012

Collaborator

FieldTest.nested_attributes_options[:nested_field_tests] works out of the box and is part of ActiveModel's API, which is fine for us.

Did I miss smtg?

This comment has been minimized.

Show comment
Hide comment
@mshibuya

mshibuya Mar 16, 2012

Collaborator

accepts_nested_attributes_for appears to be implemented independently (and slightly differently) between AR and Mongoid, not being a functionality of ActiveModel.

Out-of-the-box Mongoid model doesn't have nested_attributes_options method, and stores options which is passed to accepts_nested_attributes_for in local block scope which is not accessible from outside.
I wish I could avoid this patchy method, but I could not find the way to bring out that options inside a block.

@mshibuya

mshibuya Mar 16, 2012

Collaborator

accepts_nested_attributes_for appears to be implemented independently (and slightly differently) between AR and Mongoid, not being a functionality of ActiveModel.

Out-of-the-box Mongoid model doesn't have nested_attributes_options method, and stores options which is passed to accepts_nested_attributes_for in local block scope which is not accessible from outside.
I wish I could avoid this patchy method, but I could not find the way to bring out that options inside a block.

This comment has been minimized.

Show comment
Hide comment
@bbenezech

bbenezech Mar 16, 2012

Collaborator

I assumed there was an ActiveModel's compatibility layer, I got it all the wrong. Sorry I wasted your time !

@bbenezech

bbenezech Mar 16, 2012

Collaborator

I assumed there was an ActiveModel's compatibility layer, I got it all the wrong. Sorry I wasted your time !

+ @nested_attributes_options = {}
+ options = args.extract_options!
+ args.each do |arg|
+ @nested_attributes_options[arg.to_sym] = options.reverse_merge(:allow_destroy=>false, :update_only=>false)
+ end
+ accepts_nested_attributes_for_without_rails_admin(*args, options)
+ end
+ end
end
end
end
@@ -304,7 +304,7 @@ def reset_model(model)
# @see RailsAdmin::Config::Hideable
def visible_models(bindings)
- models.map{|m| m.with(bindings) }.select{|m| m.visible? && bindings[:controller].authorized?(:index, m.abstract_model)}.sort do |a, b|
+ models.map{|m| m.with(bindings) }.select{|m| m.visible? && bindings[:controller].authorized?(:index, m.abstract_model) && !m.abstract_model.embedded?}.sort do |a, b|
(weight_order = a.weight <=> b.weight) == 0 ? a.label.downcase <=> b.label.downcase : weight_order
end
end
@@ -21,7 +21,7 @@ def association
amc = polymorphic? ? RailsAdmin::Config.model(associated) : associated_model_config # perf optimization for non-polymorphic associations
am = amc.abstract_model
wording = associated.send(amc.object_label_method)
- can_see = v.authorized?(:show, am, associated)
+ can_see = v.authorized?(:show, am, associated) && !am.embedded?
can_see = can_see && (show_action = RailsAdmin::Config::Actions.find(:show, { :controller => v.controller, :abstract_model => am, :object => associated }))
can_see ? v.link_to(wording, v.url_for(:action => show_action.action_name, :model_name => am.to_param, :id => associated.id)) : wording
end.to_sentence.html_safe
@@ -6,4 +6,7 @@ class Article
referenced_in :author
references_and_referenced_in_many :tags
+
+ embeds_many :notes
+ accepts_nested_attributes_for :notes, :allow_destroy => true
end
@@ -0,0 +1,7 @@
+class Note
+ include Mongoid::Document
+
+ embedded_in :article
+ field :subject, :type => String
+ field :description, :type => String
+end
@@ -814,6 +814,16 @@ class Team
visit new_path(:model_name => "field_test")
should have_no_selector('.add_nested_fields')
end
+
+ it 'should work with Mongoid' do
+ RailsAdmin::Config.excluded_models = [RelTest]
+ @record = Article.create :notes => [{:subject => 'nested'}]
+ visit edit_path(:model_name => "article", :id => @record.id)
+ fill_in "article_notes_attributes_0_subject", :with => 'note subject 1 edited'
+ click_button "Save"
+ @record.reload
+ @record.notes[0].subject.should == 'note subject 1 edited'
+ end
end

0 comments on commit 163da3e

Please sign in to comment.