Merge branch 'groups_reincarnation' into staging
Conflicts: app/models/ability.rb app/models/project.rb config/environments/production.rb
This commit is contained in:
commit
2f5ce7cebe
|
@ -15,8 +15,13 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
protected
|
||||
def get_owner
|
||||
params['user_id'] && User.find_by_id(params['user_id']) ||
|
||||
params['group_id'] && Group.find_by_id(params['group_id']) || current_user
|
||||
# params['user_id'] && User.find_by_id(params['user_id']) ||
|
||||
# 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
|
||||
|
||||
def layout_by_resource
|
||||
|
|
|
@ -29,8 +29,11 @@ class CollaboratorsController < ApplicationController
|
|||
|
||||
def update
|
||||
all_user_ids = []
|
||||
all_groups_ids = []
|
||||
puts params.inspect
|
||||
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
|
||||
|
@ -40,11 +43,17 @@ class CollaboratorsController < ApplicationController
|
|||
users_for_removing.each do |u|
|
||||
Relation.by_object(u).by_target(@project).each {|r| r.destroy}
|
||||
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
|
||||
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|
|
||||
params['user'][r.to_sym].keys.each { |u|
|
||||
if relation = @project.relations.find_by_object_id_and_object_type(u, 'User')
|
||||
relation.update_attribute(:role, r)
|
||||
else
|
||||
|
@ -53,7 +62,17 @@ class CollaboratorsController < ApplicationController
|
|||
puts r
|
||||
relation.save!
|
||||
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
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
# coding: UTF-8
|
||||
class GroupsController < ApplicationController
|
||||
is_related_controller!
|
||||
|
||||
belongs_to :user, :optional => true
|
||||
|
||||
before_filter :authenticate_user!
|
||||
before_filter :find_group, :only => [:show, :edit, :update, :destroy]
|
||||
|
||||
load_and_authorize_resource
|
||||
|
||||
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
|
||||
|
||||
def show
|
||||
|
@ -24,13 +39,18 @@ class GroupsController < ApplicationController
|
|||
|
||||
def create
|
||||
@group = Group.new params[:group]
|
||||
@group.owner = current_user
|
||||
@group.members << current_user
|
||||
if @group.save
|
||||
@group.owner = if parent? and parent.is_a? User
|
||||
parent
|
||||
else
|
||||
current_user
|
||||
end
|
||||
|
||||
if @group.save!
|
||||
flash[:notice] = t('flash.group.saved')
|
||||
redirect_to edit_group_path(@group)
|
||||
else
|
||||
flash[:error] = t('flash.group.save_error')
|
||||
flash[:warning] = @project.errors[:base]
|
||||
render :action => :new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,8 @@
|
|||
class ProjectsController < ApplicationController
|
||||
is_related_controller!
|
||||
|
||||
belongs_to :user, :group, :polymorphic => true, :optional => true
|
||||
|
||||
before_filter :authenticate_user!, :except => :auto_build
|
||||
before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork, :build, :process_build]
|
||||
before_filter :get_paths, :only => [:new, :create, :edit, :update]
|
||||
|
@ -6,11 +10,19 @@ class ProjectsController < ApplicationController
|
|||
load_and_authorize_resource
|
||||
|
||||
def index
|
||||
if params[:query]
|
||||
@projects = Project.accessible_by(current_ability).where(:name => params[:query]).paginate(:page => params[:project_page])
|
||||
# puts parent.inspect
|
||||
# puts parent.is_a? User
|
||||
@projects = if parent? and !parent.nil?
|
||||
parent.projects
|
||||
else
|
||||
@projects = Project.accessible_by(current_ability).paginate(:page => params[:project_page])
|
||||
end
|
||||
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
|
||||
@part_projects = current_user.projects - @own_projects
|
||||
|
@ -30,6 +42,7 @@ class ProjectsController < ApplicationController
|
|||
def create
|
||||
@project = Project.new params[:project]
|
||||
@project.owner = get_owner
|
||||
# puts @project.owner.inspect
|
||||
|
||||
if @project.save
|
||||
flash[:notice] = t('flash.project.saved')
|
||||
|
@ -46,7 +59,7 @@ class ProjectsController < ApplicationController
|
|||
flash[:notice] = t('flash.project.saved')
|
||||
redirect_to @project
|
||||
else
|
||||
@project.save!
|
||||
@project.save
|
||||
flash[:error] = t('flash.project.save_error')
|
||||
render :action => :edit
|
||||
end
|
||||
|
|
|
@ -29,12 +29,19 @@ class Ability
|
|||
can [:index, :destroy], AutoBuildList, :project_id => user.own_project_ids
|
||||
# If rules goes one by one CanCan joins them by 'OR' sql operator
|
||||
can :read, Project, :visibility => 'open'
|
||||
can :read, Group
|
||||
can :read, User
|
||||
can :manage_collaborators, Project do |project|
|
||||
project.relations.exists? :object_id => user.id, :object_type => 'User', :role => 'admin'
|
||||
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
|
||||
can :create, Project
|
||||
can :create, Group
|
||||
can :publish, BuildList do |build_list|
|
||||
build_list.can_published? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id)
|
||||
end
|
||||
|
@ -60,6 +67,10 @@ class Ability
|
|||
product.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id)
|
||||
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, 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)
|
||||
|
@ -128,7 +139,7 @@ class Ability
|
|||
|
||||
# Sub query for platforms, projects relations
|
||||
# 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|
|
||||
query = "#{ table_name }.id IN (SELECT target_id FROM relations WHERE relations.target_type = '#{ table_name.singularize.camelize }'"
|
||||
opts.each do |key, value|
|
||||
|
|
|
@ -20,6 +20,14 @@ class Group < ActiveRecord::Base
|
|||
|
||||
delegate :ssh_key, :to => :owner
|
||||
|
||||
after_create :add_owner_to_members
|
||||
|
||||
include Modules::Models::PersonalRepository
|
||||
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
|
||||
|
|
|
@ -5,6 +5,21 @@ class Relation < ActiveRecord::Base
|
|||
ROLES = %w[reader writer admin]
|
||||
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_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
|
||||
|
|
|
@ -22,28 +22,29 @@
|
|||
= link_to user.name, user_path(user)
|
||||
%td
|
||||
- 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"}
|
||||
= label_tag "#{ role }[#{user.id}]", t("layout.collaborators.roles.#{ role }")
|
||||
= 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 "user[#{ role }][#{user.id}]", t("layout.collaborators.roles.#{ role }")
|
||||
%td
|
||||
= user.uname
|
||||
/%h2.title= t("layout.groups.list_header")
|
||||
/%table.table
|
||||
/ %tr
|
||||
/ %th.first ID
|
||||
/ %th= t("activerecord.attributes.group.name")
|
||||
/ %th= t("activerecord.attributes.group.uname")
|
||||
/ %th.last= t("layout.collaborators.add")
|
||||
/ - @groups.each do |group|
|
||||
/ %tr{:class => cycle("odd", "even")}
|
||||
/ %td
|
||||
/ = group.id
|
||||
/ %td
|
||||
/ = link_to group.name, group_path(group)
|
||||
/ %td
|
||||
/ = group.uname
|
||||
/ %td.last
|
||||
/ = check_box_tag "group[#{group.id}]", '1', (@project.groups.include? group) ? :checked : nil
|
||||
/ = label_tag "group[#{group.id}]", t("layout.collaborators.add")
|
||||
%h2.title= t("layout.groups.list_header")
|
||||
%table.table
|
||||
%tr
|
||||
%th.first ID
|
||||
%th= t("activerecord.attributes.group.name")
|
||||
%th= t("activerecord.attributes.group.uname")
|
||||
%th.last= t("layout.collaborators.add")
|
||||
- @groups.each do |group|
|
||||
%tr{:class => cycle("odd", "even")}
|
||||
%td
|
||||
= group.id
|
||||
%td
|
||||
= link_to group.name, group_path(group)
|
||||
%td
|
||||
- Relation::ROLES.each do |role|
|
||||
= 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"}
|
||||
= label_tag "group[#{role}][#{group.id}]", t("layout.collaborators.roles.#{role}")
|
||||
%td
|
||||
= group.uname
|
||||
.group.navform.wat-cf
|
||||
%button.button{:type => "submit"}
|
||||
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
.block.notice
|
||||
%h3= t("layout.groups.members")
|
||||
.content
|
||||
- @group.members.each do |member|
|
||||
%p= link_to member.name, member
|
||||
%p
|
||||
%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
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
-if can? :index, Project
|
||||
%li{:class => controller.controller_path == 'projects' ? 'active' : '' }
|
||||
%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
|
||||
%li{:class => controller.controller_path == 'downloads' ? 'active' : '' }
|
||||
%a{:href => downloads_path}= t("layout.menu.downloads")
|
||||
|
|
|
@ -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"
|
||||
|
|
@ -13,8 +13,18 @@
|
|||
= user.name
|
||||
- if (@project.owner == user)
|
||||
= '(' + t("layout.owner") + ')'
|
||||
%br
|
||||
/%br
|
||||
-# 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
|
||||
|
||||
/ %p
|
||||
|
|
|
@ -223,6 +223,10 @@ ru:
|
|||
roles_header: Роли для
|
||||
add_role: Добавить/Удалить роль
|
||||
|
||||
members:
|
||||
back_to_group: Вернуться к группе
|
||||
edit: Редактировать список
|
||||
roles: Роли
|
||||
|
||||
groups:
|
||||
list: Список
|
||||
|
@ -235,6 +239,7 @@ ru:
|
|||
show: Группа
|
||||
back_to_the_list: ⇐ К списку групп
|
||||
confirm_delete: Вы уверены, что хотите удалить эту группу?
|
||||
edit_members: Изменить список участников
|
||||
|
||||
users:
|
||||
list: Список
|
||||
|
@ -321,6 +326,10 @@ ru:
|
|||
successfully_changed: Список коллабораторов успешно изменен
|
||||
error_in_changing: Ошибка изменения списка коллабораторов
|
||||
|
||||
members:
|
||||
successfully_changed: Список участников успешно изменен
|
||||
error_in_changing: Ошибка изменения списка участников
|
||||
|
||||
auto_build_list:
|
||||
success: Сборка проекта автоматизорована!
|
||||
failed: Не удалось автоматизировать сборку!
|
||||
|
|
|
@ -6,7 +6,9 @@ Rosa::Application.routes.draw do
|
|||
get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru'
|
||||
end
|
||||
|
||||
resources :users
|
||||
resources :users do
|
||||
resources :groups, :only => [:new, :create, :index]
|
||||
end
|
||||
|
||||
resources :event_logs, :only => :index
|
||||
|
||||
|
@ -105,10 +107,22 @@ Rosa::Application.routes.draw do
|
|||
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 :platforms, :only => [:new, :create]
|
||||
|
||||
resources :projects, :only => [:new, :create]
|
||||
resources :projects, :only => [:new, :create, :index]
|
||||
|
||||
resources :repositories, :only => [:new, :create]
|
||||
end
|
||||
|
|
|
@ -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.
|
|
@ -0,0 +1,13 @@
|
|||
RelatedModels
|
||||
=============
|
||||
|
||||
Introduction goes here.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Example goes here.
|
||||
|
||||
|
||||
Copyright (c) 2011 [name of plugin creator], released under the MIT license
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
# Include hook code here
|
||||
require 'related_models'
|
|
@ -0,0 +1 @@
|
|||
# Install hook code here
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
require 'active_support'
|
|
@ -0,0 +1 @@
|
|||
# Uninstall hook code here
|
Loading…
Reference in New Issue