Merge branch 'groups_reincarnation' into staging

Conflicts:
	app/models/ability.rb
	app/models/project.rb
	config/environments/production.rb
This commit is contained in:
George Vinogradov 2011-12-05 17:16:32 +04:00
commit 2f5ce7cebe
29 changed files with 614 additions and 42 deletions

View File

@ -15,8 +15,13 @@ class ApplicationController < ActionController::Base
protected protected
def get_owner def get_owner
params['user_id'] && User.find_by_id(params['user_id']) || # params['user_id'] && User.find_by_id(params['user_id']) ||
params['group_id'] && Group.find_by_id(params['group_id']) || current_user # params['group_id'] && Group.find_by_id(params['group_id']) || current_user
if parent and (parent.is_a? User or parent.is_a? Group)
parent
else
current_user
end
end end
def layout_by_resource def layout_by_resource

View File

@ -29,8 +29,11 @@ class CollaboratorsController < ApplicationController
def update def update
all_user_ids = [] all_user_ids = []
all_groups_ids = []
puts params.inspect
Relation::ROLES.each { |r| Relation::ROLES.each { |r|
all_user_ids = all_user_ids | params[r.to_sym].keys if params[r.to_sym] all_user_ids = all_user_ids | params['user'][r.to_sym].keys if params['user'][r.to_sym]
all_groups_ids = all_groups_ids | params['group'][r.to_sym].keys if params['group'][r.to_sym]
} }
# Remove relations # Remove relations
@ -40,11 +43,17 @@ class CollaboratorsController < ApplicationController
users_for_removing.each do |u| users_for_removing.each do |u|
Relation.by_object(u).by_target(@project).each {|r| r.destroy} Relation.by_object(u).by_target(@project).each {|r| r.destroy}
end end
groups_for_removing = @project.groups.select do |u|
!all_groups_ids.map{|k| k.to_i}.include? u.id and @project.owner != u
end
groups_for_removing.each do |u|
Relation.by_object(u).by_target(@project).each {|r| r.destroy}
end
# Create relations # Create relations
Relation::ROLES.each { |r| Relation::ROLES.each { |r|
#users_for_creating = users_for_creating params[:user].keys.map{|p| p.to_i} - @project.collaborators.map(&:id) #users_for_creating = users_for_creating params[:user].keys.map{|p| p.to_i} - @project.collaborators.map(&:id)
params[r.to_sym].keys.each { |u| params['user'][r.to_sym].keys.each { |u|
if relation = @project.relations.find_by_object_id_and_object_type(u, 'User') if relation = @project.relations.find_by_object_id_and_object_type(u, 'User')
relation.update_attribute(:role, r) relation.update_attribute(:role, r)
else else
@ -53,7 +62,17 @@ class CollaboratorsController < ApplicationController
puts r puts r
relation.save! relation.save!
end end
} if params[r.to_sym] } if params['user'][r.to_sym]
params['group'][r.to_sym].keys.each { |u|
if relation = @project.relations.find_by_object_id_and_object_type(u, 'Group')
relation.update_attribute(:role, r)
else
relation = @project.relations.build(:object_id => u, :object_type => 'Group', :role => r)
puts relation.inspect
puts r
relation.save!
end
} if params['group'][r.to_sym]
} }
if @project.save if @project.save

View File

@ -1,12 +1,27 @@
# coding: UTF-8 # coding: UTF-8
class GroupsController < ApplicationController class GroupsController < ApplicationController
is_related_controller!
belongs_to :user, :optional => true
before_filter :authenticate_user! before_filter :authenticate_user!
before_filter :find_group, :only => [:show, :edit, :update, :destroy] before_filter :find_group, :only => [:show, :edit, :update, :destroy]
load_and_authorize_resource load_and_authorize_resource
def index def index
@groups = Group.paginate(:page => params[:group_page]) puts parent.inspect
@groups = if parent? and !parent.nil?
parent.groups
else
Group
end.accessible_by(current_ability)
@groups = if params[:query]
@groups.where(["name LIKE ?", "%#{params[:query]}%"])
else
@groups
end.paginate(:page => params[:group_page])
end end
def show def show
@ -24,13 +39,18 @@ class GroupsController < ApplicationController
def create def create
@group = Group.new params[:group] @group = Group.new params[:group]
@group.owner = current_user @group.owner = if parent? and parent.is_a? User
@group.members << current_user parent
if @group.save else
current_user
end
if @group.save!
flash[:notice] = t('flash.group.saved') flash[:notice] = t('flash.group.saved')
redirect_to edit_group_path(@group) redirect_to edit_group_path(@group)
else else
flash[:error] = t('flash.group.save_error') flash[:error] = t('flash.group.save_error')
flash[:warning] = @project.errors[:base]
render :action => :new render :action => :new
end end
end end

View File

@ -0,0 +1,76 @@
class MembersController < ApplicationController
before_filter :authenticate_user!
is_related_controller!
belongs_to :group, :optional => true
# before_filter :find_target
before_filter :find_users
def index
redirect_to edit_group_members_path(parent)
end
def show
end
def new
end
def edit
if params[:id]
@user = User.find params[:id]
render :edit_rights and return
end
end
def create
end
def update
all_user_ids = []
Relation::ROLES.each { |r|
all_user_ids = all_user_ids | params[r.to_sym].keys if params[r.to_sym]
}
# Remove relations
users_for_removing = parent.members.select do |u|
!all_user_ids.map{|k| k.to_i}.include? u.id and parent.owner != u
end
users_for_removing.each do |u|
Relation.by_object(u).by_target(parent).each {|r| r.destroy}
end
# Create relations
Relation::ROLES.each { |r|
#users_for_creating = users_for_creating params[:user].keys.map{|p| p.to_i} - @project.collaborators.map(&:id)
params[r.to_sym].keys.each { |u|
if relation = parent.objects.find_by_object_id_and_object_type(u, 'User')
relation.update_attribute(:role, r)
else
relation = parent.objects.build(:object_id => u, :object_type => 'User', :role => r)
puts relation.inspect
puts r
relation.save!
end
} if params[r.to_sym]
}
if parent.save
flash[:notice] = t("flash.members.successfully_changed")
else
flash[:error] = t("flash.members.error_in_changing")
end
redirect_to parent_path
end
def destroy
end
protected
def find_users
@users = User.all
end
end

View File

@ -1,4 +1,8 @@
class ProjectsController < ApplicationController class ProjectsController < ApplicationController
is_related_controller!
belongs_to :user, :group, :polymorphic => true, :optional => true
before_filter :authenticate_user!, :except => :auto_build before_filter :authenticate_user!, :except => :auto_build
before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork, :build, :process_build] before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork, :build, :process_build]
before_filter :get_paths, :only => [:new, :create, :edit, :update] before_filter :get_paths, :only => [:new, :create, :edit, :update]
@ -6,12 +10,20 @@ class ProjectsController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
def index def index
if params[:query] # puts parent.inspect
@projects = Project.accessible_by(current_ability).where(:name => params[:query]).paginate(:page => params[:project_page]) # puts parent.is_a? User
else @projects = if parent? and !parent.nil?
@projects = Project.accessible_by(current_ability).paginate(:page => params[:project_page]) parent.projects
end else
Project
end.accessible_by(current_ability)
@projects = if params[:query]
@projects.where(["name LIKE ?", "%#{params[:query]}%"])
else
@projects
end.paginate(:page => params[:project_page])
@own_projects = current_user.own_projects @own_projects = current_user.own_projects
@part_projects = current_user.projects - @own_projects @part_projects = current_user.projects - @own_projects
end end
@ -30,6 +42,7 @@ class ProjectsController < ApplicationController
def create def create
@project = Project.new params[:project] @project = Project.new params[:project]
@project.owner = get_owner @project.owner = get_owner
# puts @project.owner.inspect
if @project.save if @project.save
flash[:notice] = t('flash.project.saved') flash[:notice] = t('flash.project.saved')
@ -46,7 +59,7 @@ class ProjectsController < ApplicationController
flash[:notice] = t('flash.project.saved') flash[:notice] = t('flash.project.saved')
redirect_to @project redirect_to @project
else else
@project.save! @project.save
flash[:error] = t('flash.project.save_error') flash[:error] = t('flash.project.save_error')
render :action => :edit render :action => :edit
end end

View File

@ -29,12 +29,19 @@ class Ability
can [:index, :destroy], AutoBuildList, :project_id => user.own_project_ids can [:index, :destroy], AutoBuildList, :project_id => user.own_project_ids
# If rules goes one by one CanCan joins them by 'OR' sql operator # If rules goes one by one CanCan joins them by 'OR' sql operator
can :read, Project, :visibility => 'open' can :read, Project, :visibility => 'open'
can :read, Group
can :read, User can :read, User
can :manage_collaborators, Project do |project| can :manage_collaborators, Project do |project|
project.relations.exists? :object_id => user.id, :object_type => 'User', :role => 'admin' project.relations.exists? :object_id => user.id, :object_type => 'User', :role => 'admin'
end end
can :manage_members, Group do |group|
group.objects.exists? :object_id => user.id, :object_type => 'User', :role => 'admin'
end
# Put here model names which objects can user create # Put here model names which objects can user create
can :create, Project can :create, Project
can :create, Group
can :publish, BuildList do |build_list| can :publish, BuildList do |build_list|
build_list.can_published? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id) build_list.can_published? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id)
end end
@ -60,6 +67,10 @@ class Ability
product.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) product.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id)
end end
can [:read, :update], Group, groups_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |group|
group.objects.exists?(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id)
end
can :manage, Platform, :owner_type => 'User', :owner_id => user.id can :manage, Platform, :owner_type => 'User', :owner_id => user.id
can :manage, Platform, platforms_in_relations_with(:role => 'admin', :object_type => 'User', :object_id => user.id) do |platform| can :manage, Platform, platforms_in_relations_with(:role => 'admin', :object_type => 'User', :object_id => user.id) do |platform|
platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id)
@ -128,7 +139,7 @@ class Ability
# Sub query for platforms, projects relations # Sub query for platforms, projects relations
# TODO: Replace table names list by method_missing way # TODO: Replace table names list by method_missing way
%w[platforms projects products repositories].each do |table_name| %w[platforms projects products repositories groups].each do |table_name|
define_method table_name + "_in_relations_with" do |opts| define_method table_name + "_in_relations_with" do |opts|
query = "#{ table_name }.id IN (SELECT target_id FROM relations WHERE relations.target_type = '#{ table_name.singularize.camelize }'" query = "#{ table_name }.id IN (SELECT target_id FROM relations WHERE relations.target_type = '#{ table_name.singularize.camelize }'"
opts.each do |key, value| opts.each do |key, value|

View File

@ -20,6 +20,14 @@ class Group < ActiveRecord::Base
delegate :ssh_key, :to => :owner delegate :ssh_key, :to => :owner
after_create :add_owner_to_members
include Modules::Models::PersonalRepository include Modules::Models::PersonalRepository
include Modules::Models::Owner include Modules::Models::Owner
protected
def add_owner_to_members
Relation.create_with_role(self.owner, self, 'admin')
# members << self.owner if !members.exists?(:id => self.owner.id)
end
end end

View File

@ -5,6 +5,21 @@ class Relation < ActiveRecord::Base
ROLES = %w[reader writer admin] ROLES = %w[reader writer admin]
validates :role, :inclusion => {:in => ROLES} validates :role, :inclusion => {:in => ROLES}
before_validation :add_default_role
scope :by_object, lambda {|obj| {:conditions => ['object_id = ? AND object_type = ?', obj.id, obj.class.to_s]}} scope :by_object, lambda {|obj| {:conditions => ['object_id = ? AND object_type = ?', obj.id, obj.class.to_s]}}
scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}} scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}}
def self.create_with_role(object, target, role)
r = new
r.object = object
r.target = target
r.role = role
r.save
end
protected
def add_default_role
self.role = ROLES.first if role.nil? || role.empty?
end
end end

View File

@ -22,28 +22,29 @@
= link_to user.name, user_path(user) = link_to user.name, user_path(user)
%td %td
- Relation::ROLES.each do |role| - Relation::ROLES.each do |role|
= check_box_tag "#{ role }[#{user.id}]", '1', ((@project.relations.exists? :object_id => user.id, :object_type => 'User', :role => role) ? :checked : nil), {:class => "user_role_chbx"} = check_box_tag "user[#{ role }][#{user.id}]", '1', ((@project.relations.exists? :object_id => user.id, :object_type => 'User', :role => role) ? :checked : nil), {:class => "user_role_chbx"}
= label_tag "#{ role }[#{user.id}]", t("layout.collaborators.roles.#{ role }") = label_tag "user[#{ role }][#{user.id}]", t("layout.collaborators.roles.#{ role }")
%td %td
= user.uname = user.uname
/%h2.title= t("layout.groups.list_header") %h2.title= t("layout.groups.list_header")
/%table.table %table.table
/ %tr %tr
/ %th.first ID %th.first ID
/ %th= t("activerecord.attributes.group.name") %th= t("activerecord.attributes.group.name")
/ %th= t("activerecord.attributes.group.uname") %th= t("activerecord.attributes.group.uname")
/ %th.last= t("layout.collaborators.add") %th.last= t("layout.collaborators.add")
/ - @groups.each do |group| - @groups.each do |group|
/ %tr{:class => cycle("odd", "even")} %tr{:class => cycle("odd", "even")}
/ %td %td
/ = group.id = group.id
/ %td %td
/ = link_to group.name, group_path(group) = link_to group.name, group_path(group)
/ %td %td
/ = group.uname - Relation::ROLES.each do |role|
/ %td.last = check_box_tag "group[#{role}][#{group.id}]", '1', ((@project.relations.exists? :object_id => group.id, :object_type => 'Group', :role => role) ? :checked : nil), {:class => "user_role_chbx"}
/ = check_box_tag "group[#{group.id}]", '1', (@project.groups.include? group) ? :checked : nil = label_tag "group[#{role}][#{group.id}]", t("layout.collaborators.roles.#{role}")
/ = label_tag "group[#{group.id}]", t("layout.collaborators.add") %td
= group.uname
.group.navform.wat-cf .group.navform.wat-cf
%button.button{:type => "submit"} %button.button{:type => "submit"}
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save")) = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))

View File

@ -1,5 +1,15 @@
.block.notice .block.notice
%h3= t("layout.groups.members") %h3= t("layout.groups.members")
.content .content
- @group.members.each do |member| %p
%p= link_to member.name, member %ul
- @group.members.each do |user|
%li
- if can? :read, user
= link_to user.name, user_path(user)
- else
= user.name
- if (@group.owner == user)
= '(' + t("layout.owner") + ')'
%br
= link_to t("layout.groups.edit_members"), edit_group_members_path(@group) if can? :manage_members, @group

View File

@ -25,6 +25,9 @@
-if can? :index, Project -if can? :index, Project
%li{:class => controller.controller_path == 'projects' ? 'active' : '' } %li{:class => controller.controller_path == 'projects' ? 'active' : '' }
%a{:href => projects_path}= t("layout.menu.projects") %a{:href => projects_path}= t("layout.menu.projects")
-if can? :index, Group
%li{:class => controller.controller_path == 'groups' ? 'active' : '' }
%a{:href => groups_path}= t("layout.menu.groups")
-if can? :index, Download -if can? :index, Download
%li{:class => controller.controller_path == 'downloads' ? 'active' : '' } %li{:class => controller.controller_path == 'downloads' ? 'active' : '' }
%a{:href => downloads_path}= t("layout.menu.downloads") %a{:href => downloads_path}= t("layout.menu.downloads")

View File

@ -0,0 +1,35 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.members.back_to_group"), parent_path
%li.active= link_to t("layout.members.edit"), edit_group_members_path(@group)
.content
.inner
= form_tag group_members_path(parent) do
%h2.title= t("layout.users.list_header")
%table.table
%tr
%th.first ID
%th= t("activerecord.attributes.user.name")
%th= t("activerecord.attributes.user.roles")
%th= t("activerecord.attributes.user.uname")
- #TODO: Replace this Chelyabinsk add/remove collaborators method by more human method
- @users.each do |user|
%tr{:class => cycle("odd", "even")}
%td
= user.id
%td
= link_to user.name, user_path(user)
%td
- Relation::ROLES.each do |role|
= check_box_tag "#{ role }[#{user.id}]", '1', ((parent.relations.exists? :object_id => user.id, :object_type => 'User', :role => role) ? :checked : nil), {:class => "user_role_chbx"}
= label_tag "#{ role }[#{user.id}]", t("layout.members.roles.#{ role }")
%td
= user.uname
.group.navform.wat-cf
%button.button{:type => "submit"}
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
= t("layout.save")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), group_path(parent), :class => "text_button_padding link_button"

View File

@ -13,8 +13,18 @@
= user.name = user.name
- if (@project.owner == user) - if (@project.owner == user)
= '(' + t("layout.owner") + ')' = '(' + t("layout.owner") + ')'
%br /%br
-# if can? :update, @project -# if can? :update, @project
%p
%b
= "#{t("layout.projects.groups")}:"
%ul
- @project.groups.each do |group|
%li
= link_to group.name, group_path(group)
- if (@project.owner == group)
= '(' + t("layout.owner") + ')'
%br
= link_to t("layout.projects.edit_collaborators"), edit_project_collaborators_path(@project) if can? :manage_collaborators, @project = link_to t("layout.projects.edit_collaborators"), edit_project_collaborators_path(@project) if can? :manage_collaborators, @project
/ %p / %p

View File

@ -223,6 +223,10 @@ ru:
roles_header: Роли для roles_header: Роли для
add_role: Добавить/Удалить роль add_role: Добавить/Удалить роль
members:
back_to_group: Вернуться к группе
edit: Редактировать список
roles: Роли
groups: groups:
list: Список list: Список
@ -235,6 +239,7 @@ ru:
show: Группа show: Группа
back_to_the_list: К списку групп back_to_the_list: К списку групп
confirm_delete: Вы уверены, что хотите удалить эту группу? confirm_delete: Вы уверены, что хотите удалить эту группу?
edit_members: Изменить список участников
users: users:
list: Список list: Список
@ -321,6 +326,10 @@ ru:
successfully_changed: Список коллабораторов успешно изменен successfully_changed: Список коллабораторов успешно изменен
error_in_changing: Ошибка изменения списка коллабораторов error_in_changing: Ошибка изменения списка коллабораторов
members:
successfully_changed: Список участников успешно изменен
error_in_changing: Ошибка изменения списка участников
auto_build_list: auto_build_list:
success: Сборка проекта автоматизорована! success: Сборка проекта автоматизорована!
failed: Не удалось автоматизировать сборку! failed: Не удалось автоматизировать сборку!

View File

@ -6,7 +6,9 @@ Rosa::Application.routes.draw do
get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru' get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru'
end end
resources :users resources :users do
resources :groups, :only => [:new, :create, :index]
end
resources :event_logs, :only => :index resources :event_logs, :only => :index
@ -105,10 +107,22 @@ Rosa::Application.routes.draw do
end end
end end
resources :groups do
resources :members, :only => [:index, :edit, :update] do
collection do
get :edit
post :update
end
member do
post :update
end
end
end
resources :users, :groups do resources :users, :groups do
resources :platforms, :only => [:new, :create] resources :platforms, :only => [:new, :create]
resources :projects, :only => [:new, :create] resources :projects, :only => [:new, :create, :index]
resources :repositories, :only => [:new, :create] resources :repositories, :only => [:new, :create]
end end

View File

@ -0,0 +1,20 @@
Copyright (c) 2011 [name of plugin creator]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

13
vendor/plugins/related_models/README vendored Normal file
View File

@ -0,0 +1,13 @@
RelatedModels
=============
Introduction goes here.
Example
=======
Example goes here.
Copyright (c) 2011 [name of plugin creator], released under the MIT license

23
vendor/plugins/related_models/Rakefile vendored Normal file
View File

@ -0,0 +1,23 @@
require 'rake'
require 'rake/testtask'
require 'rdoc/task'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the related_models plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the related_models plugin.'
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'RelatedModels'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View File

@ -0,0 +1,23 @@
module RelatedModels
class Base < ::ApplicationController
def self.is_child!(base)
base.class_eval do
# include InheritedResources::Actions
# include InheritedResources::BaseHelpers
extend RelatedModels::ClassMethods
extend RelatedModels::UrlHelpers
helper_method :parent_url, :parent_path
self.class_attribute :parents_symbols, :resources_configuration, :instance_writer => false
self.parents_symbols ||= []
self.resources_configuration ||= {}
protected :parents_symbols, :resources_configuration, :parents_symbols?, :resources_configuration?
end
end
is_child!(self)
end
end

2
vendor/plugins/related_models/init.rb vendored Normal file
View File

@ -0,0 +1,2 @@
# Include hook code here
require 'related_models'

View File

@ -0,0 +1 @@
# Install hook code here

View File

@ -0,0 +1,14 @@
# RelatedModels
module RelatedModels
autoload :ClassMethods, 'related_models/class_methods'
autoload :BelongsToHelpers, 'related_models/belongs_to_helpers'
autoload :PolymorphicHelpers, 'related_models/polymorphic_helpers'
autoload :UrlHelpers, 'related_models/url_helpers'
end
class ActionController::Base
#include ClassMethods
def self.is_related_controller!
RelatedModels::Base.is_child!(self)
end
end

View File

@ -0,0 +1,39 @@
module RelatedModels
module BelongsToHelpers
protected
def parent?
true
end
def parent
@parent ||= find_parent
end
def parent_type
parent.class.name.underscore.to_sym
end
private
def symbols_for_association_chain
parents_symbols.compact
end
def find_parent
k = params.symbolize_keys.keys
res = nil
symbols_for_association_chain.reverse.each do |sym|
if k.include? resources_configuration[sym][:param]
parent_config = resources_configuration[sym]
res = parent_config[:parent_class].send(parent_config[:finder], params[parent_config[:param]])
break
end
end
unless res
raise "Couldn't find parent"
end
return res
end
end
end

View File

@ -0,0 +1,68 @@
module RelatedModels
module ClassMethods
protected
def belongs_to(*symbols)
options = symbols.extract_options!
options.symbolize_keys!
options.assert_valid_keys(:polymorphic, :optional, :finder)
optional = options.delete(:optional)
polymorphic = options.delete(:polymorphic)
finder = options.delete(:finder)
include BelongsToHelpers if self.parents_symbols.empty?
acts_as_polymorphic! if polymorphic || optional
raise ArgumentError, 'You have to give me at least one association name.' if symbols.empty?
raise ArgumentError, 'You cannot define multiple associations with options: #{options.keys.inspect} to belongs to.' unless symbols.size == 1 || options.empty?
symbols.each do |symbol|
symbol = symbol.to_sym
if polymorphic || optional
self.parents_symbols << :polymorphic unless self.parents_symbols.include?(:polymorphic)
self.resources_configuration[:polymorphic] ||= {}
self.resources_configuration[:polymorphic][:symbols] ||= []
self.resources_configuration[:polymorphic][:symbols] << symbol
self.resources_configuration[:polymorphic][:optional] ||= optional
else
self.parents_symbols << symbol
end
config = self.resources_configuration[symbol] = {}
config[:parent_class] = begin
class_name = symbol.to_s.pluralize.classify
class_name.constantize
rescue NameError => e
raise unless e.message.include?(class_name)
nil
end
config[:collection_name] = symbol.to_s.pluralize.to_sym
config[:instance_name] = symbol
config[:param] = :"#{symbol}_id"
config[:route_name] = symbol
config[:finder] = finder || :find
end
create_resources_url_helpers!
helper_method :parent, :parent?
end
private
def acts_as_polymorphic! #:nodoc:
unless self.parents_symbols.include?(:polymorphic)
include PolymorphicHelpers
helper_method :parent_type, :parent_class
end
end
def inherited(base)
super(base)
end
end
end

View File

@ -0,0 +1,80 @@
module RelatedModels
module PolymorphicHelpers
protected
# Returns the parent type. A Comments class can have :task, :file, :note
# as parent types.
#
def parent_type
@parent_type
end
def parent_class
parent.class if @parent_type
end
# Returns the parent object. They are also available with the instance
# variable name: @task, @file, @note...
#
def parent
k = params.symbolize_keys.keys
res = nil
symbols_for_association_chain.reverse.each do |sym|
if k.include? resources_configuration[sym][:param]
parent_config = resources_configuration[sym]
res = parent_config[:parent_class].send(parent_config[:finder], params[parent_config[:param]])
break
end
end
return res
end
# If the polymorphic association is optional, we might not have a parent.
#
def parent?
if resources_configuration[:polymorphic][:optional]
parents_symbols.size > 1 || !@parent_type.nil?
else
true
end
end
private
# Maps parents_symbols to build association chain.
#
# If the parents_symbols find :polymorphic, it goes through the
# params keys to see which polymorphic parent matches the given params.
#
# When optional is given, it does not raise errors if the polymorphic
# params are missing.
#
def symbols_for_association_chain #:nodoc:
polymorphic_config = resources_configuration[:polymorphic]
parents_symbols.map do |symbol|
if symbol == :polymorphic
params_keys = params.keys
keys = polymorphic_config[:symbols].map do |poly|
params_keys.include?(resources_configuration[poly][:param].to_s) ? poly : nil
end.compact
if keys.empty?
raise ScriptError, "Could not find param for polymorphic association. The request" <<
"parameters are #{params.keys.inspect} and the polymorphic " <<
"associations are #{polymorphic_config[:symbols].inspect}." unless polymorphic_config[:optional]
nil
else
@parent_type = keys[-1].to_sym
@parent_types = keys.map(&:to_sym)
end
else
symbol
end
end.flatten.compact
end
end
end

View File

@ -0,0 +1,28 @@
module RelatedModels
module UrlHelpers
protected
def create_resources_url_helpers!
segment = if parents_symbols.include? :polymorphic
:polymorphic
else
resources_configuration[symbols_for_association_chain.first][:route_name]
end
unless parent.nil?
class_eval <<-URL_HELPERS, __FILE__, __LINE__
protected
def parent_path(*given_args)
given_options = given_args.extract_options!
#{segment}_path(parent, given_options)
end
def parent_url(*given_args)
given_options = given_args.extract_options!
#{segment}_url(parent, given_options)
end
URL_HELPERS
end
end
end
end

View File

@ -0,0 +1,8 @@
require 'test_helper'
class RelatedModelsTest < ActiveSupport::TestCase
# Replace this with your real tests.
test "the truth" do
assert true
end
end

View File

@ -0,0 +1,3 @@
require 'rubygems'
require 'test/unit'
require 'active_support'

View File

@ -0,0 +1 @@
# Uninstall hook code here