voluntary_recruiting 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1 -0
- data/Rakefile +26 -0
- data/app/controllers/candidatures_controller.rb +84 -0
- data/app/controllers/vacancies_controller.rb +88 -0
- data/app/controllers/workflow/candidatures_controller.rb +20 -0
- data/app/controllers/workflow/vacancies_controller.rb +17 -0
- data/app/models/candidature.rb +38 -0
- data/app/models/product/recruiting.rb +2 -0
- data/app/models/state_machines/candidature.rb +64 -0
- data/app/models/state_machines/vacancy.rb +49 -0
- data/app/models/vacancy.rb +43 -0
- data/app/views/candidatures/_form.html.erb +13 -0
- data/app/views/candidatures/edit.html.erb +3 -0
- data/app/views/candidatures/index.html.erb +11 -0
- data/app/views/candidatures/new.html.erb +3 -0
- data/app/views/candidatures/show.html.erb +18 -0
- data/app/views/vacancies/_form.html.erb +15 -0
- data/app/views/vacancies/edit.html.erb +3 -0
- data/app/views/vacancies/index.html.erb +7 -0
- data/app/views/vacancies/new.html.erb +3 -0
- data/app/views/vacancies/show.html.erb +15 -0
- data/app/views/workflow/project_owner/_candidatures.html.erb +32 -0
- data/app/views/workflow/project_owner/_vacancies.html.erb +27 -0
- data/config/locales/general/en.yml +17 -0
- data/config/locales/resources/candidature/en.yml +18 -0
- data/config/locales/resources/vacancy/en.yml +33 -0
- data/config/routes.rb +67 -0
- data/db/migrate/20150711124651_add_recruiting.rb +60 -0
- data/lib/tasks/voluntary_recruiting_tasks.rake +4 -0
- data/lib/voluntary_recruiting.rb +13 -0
- data/lib/voluntary_recruiting/ability.rb +17 -0
- data/lib/voluntary_recruiting/concerns/model/project/recruitable.rb +15 -0
- data/lib/voluntary_recruiting/concerns/model/project_user/recruitable.rb +19 -0
- data/lib/voluntary_recruiting/concerns/model/user/recruitable.rb +17 -0
- data/lib/voluntary_recruiting/controller/project_owner_controller.rb +26 -0
- data/lib/voluntary_recruiting/engine.rb +22 -0
- data/lib/voluntary_recruiting/navigation.rb +106 -0
- data/lib/voluntary_recruiting/version.rb +3 -0
- metadata +336 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 55641eb467512030495e3b1ba5ec426c2a6931c8
|
4
|
+
data.tar.gz: d862958729d6ef4314c52131cae3a7e12870ff3f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c6324e15b2116153d8238452a734d8cfe78149259831ce08d74a169ec74fec222751a9cdc15f0438241a42fb93ab9e2577d8abca87a99e1ef1b6206edd1d168d
|
7
|
+
data.tar.gz: 5d8ffcd55f363825e16f6f7b498ff010021af1922303e59968d268fc5ee923d67e0c45afb6085f24bc5d626ba77397c9ec8931f53414b10582bfd6177a5de43a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 Mathias Gawlista
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# voluntary_recruiting
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'VoluntaryRecruiting'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class CandidaturesController < ApplicationController
|
2
|
+
include Applicat::Mvc::Controller::Resource
|
3
|
+
|
4
|
+
before_filter :find_candidature, only: [:show, :edit, :update, :destroy]
|
5
|
+
before_filter :find_vacancy, only: [:index, :new, :edit]
|
6
|
+
|
7
|
+
load_and_authorize_resource
|
8
|
+
|
9
|
+
transition_actions Candidature::EVENTS
|
10
|
+
|
11
|
+
helper_method :parent
|
12
|
+
|
13
|
+
respond_to :html, :js, :json
|
14
|
+
|
15
|
+
def index
|
16
|
+
@candidatures = if @vacancy
|
17
|
+
@vacancy.candidatures.includes(:vacancy, :resource)
|
18
|
+
else
|
19
|
+
Candidature.includes(:vacancy, :resource).where(resource_type: 'User')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def show
|
24
|
+
@vacancy = @candidature.vacancy
|
25
|
+
@comments = @candidature.comments
|
26
|
+
end
|
27
|
+
|
28
|
+
def new
|
29
|
+
@candidature = Candidature.new
|
30
|
+
@candidature.vacancy = parent
|
31
|
+
end
|
32
|
+
|
33
|
+
def create
|
34
|
+
@candidature = Candidature.new(params[:candidature])
|
35
|
+
@candidature.resource_type = 'User'
|
36
|
+
@candidature.resource_id = current_user.id
|
37
|
+
|
38
|
+
if @candidature.save
|
39
|
+
redirect_to @candidature, notice: t('general.form.successfully_created')
|
40
|
+
else
|
41
|
+
render :new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def edit
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
if @candidature.update_attributes(params[:candidature])
|
50
|
+
redirect_to @candidature, notice: t('general.form.successfully_updated')
|
51
|
+
else
|
52
|
+
render :edit
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def destroy
|
57
|
+
@candidature.destroy
|
58
|
+
redirect_to candidatures_url, notice: t('general.form.destroyed')
|
59
|
+
end
|
60
|
+
|
61
|
+
def resource
|
62
|
+
@candidature
|
63
|
+
end
|
64
|
+
|
65
|
+
def parent
|
66
|
+
@vacancy
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def set_twitter_sidenav_level
|
72
|
+
@twitter_sidenav_level = 5
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def find_candidature
|
78
|
+
@candidature = Candidature.includes(:vacancy, :resource, :comments).friendly.find(params[:id])
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_vacancy
|
82
|
+
@vacancy = params[:vacancy_id].present? ? Vacancy.friendly.find(params[:vacancy_id]) : nil
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class VacanciesController < ApplicationController
|
2
|
+
include Applicat::Mvc::Controller::Resource
|
3
|
+
|
4
|
+
before_filter :find_vacancy
|
5
|
+
|
6
|
+
load_and_authorize_resource
|
7
|
+
|
8
|
+
before_filter :find_project, only: [:index, :new, :edit]
|
9
|
+
|
10
|
+
respond_to :html, :js, :json
|
11
|
+
|
12
|
+
def index
|
13
|
+
@vacancies = @project ? @project.vacancies : Vacancy.all
|
14
|
+
end
|
15
|
+
|
16
|
+
def show
|
17
|
+
@comments = @vacancy.comments
|
18
|
+
end
|
19
|
+
|
20
|
+
def new
|
21
|
+
@vacancy = Vacancy.new
|
22
|
+
|
23
|
+
@vacancy.project = @project if @project
|
24
|
+
end
|
25
|
+
|
26
|
+
def create
|
27
|
+
@vacancy = Vacancy.new(params[:vacancy])
|
28
|
+
|
29
|
+
if @vacancy.project.user_id == current_user.id
|
30
|
+
@vacancy.do_open
|
31
|
+
else
|
32
|
+
@vacancy.author_id = current_user.id
|
33
|
+
@vacancy.recommend
|
34
|
+
end
|
35
|
+
|
36
|
+
if @vacancy.save
|
37
|
+
redirect_to @vacancy, notice: t('general.form.successfully_created')
|
38
|
+
else
|
39
|
+
render :new
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def edit
|
44
|
+
@vacancy = Vacancy.friendly.find(params[:id])
|
45
|
+
end
|
46
|
+
|
47
|
+
def update
|
48
|
+
@vacancy = Vacancy.friendly.find(params[:id])
|
49
|
+
|
50
|
+
if @vacancy.update_attributes(params[:vacancy])
|
51
|
+
redirect_to @vacancy, notice: t('general.form.successfully_updated')
|
52
|
+
else
|
53
|
+
render :edit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def destroy
|
58
|
+
@vacancy = Vacancy.friendly.find(params[:id])
|
59
|
+
@vacancy.destroy
|
60
|
+
redirect_to vacancies_url, notice: t('general.form.destroyed')
|
61
|
+
end
|
62
|
+
|
63
|
+
def resource
|
64
|
+
@vacancy
|
65
|
+
end
|
66
|
+
|
67
|
+
transition_actions Vacancy::EVENTS
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def before_my_state
|
72
|
+
# e.g. set attributes of resource
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def find_vacancy
|
78
|
+
return unless params[:id].present?
|
79
|
+
|
80
|
+
@vacancy = Vacancy
|
81
|
+
@vacancy = @vacancy.includes(:project, :candidatures, :comments) if action_name == 'show'
|
82
|
+
@vacancy = @vacancy.friendly.find(params[:id])
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_project
|
86
|
+
@project = params[:project_id].present? ? Project.friendly.find(params[:project_id]) : nil
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Workflow::CandidaturesController < ApplicationController
|
2
|
+
def new
|
3
|
+
state_action
|
4
|
+
end
|
5
|
+
|
6
|
+
def accepted
|
7
|
+
state_action
|
8
|
+
end
|
9
|
+
|
10
|
+
def denied
|
11
|
+
state_action
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def state_action
|
17
|
+
@candidatures = current_user.offeror_candidatures.where(state: action_name).includes(:vacancy, :resource)
|
18
|
+
render 'candidatures/index'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Workflow::VacanciesController < ApplicationController
|
2
|
+
def open
|
3
|
+
@vacancies = Vacancy.where(state: 'open')
|
4
|
+
end
|
5
|
+
|
6
|
+
def recommended
|
7
|
+
@vacancies = Vacancy.where(state: 'recommended')
|
8
|
+
end
|
9
|
+
|
10
|
+
def denied
|
11
|
+
@vacancies = Vacancy.where(state: 'denied')
|
12
|
+
end
|
13
|
+
|
14
|
+
def closed
|
15
|
+
@vacancies = Vacancy.where(state: 'closed')
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Candidature < ActiveRecord::Base
|
2
|
+
include StateMachines::Candidature
|
3
|
+
|
4
|
+
belongs_to :vacancy
|
5
|
+
belongs_to :offeror, class_name: 'User'
|
6
|
+
belongs_to :resource, polymorphic: true
|
7
|
+
|
8
|
+
has_many :comments, as: :commentable, dependent: :destroy
|
9
|
+
|
10
|
+
scope :accepted, -> { where(state: 'accepted') }
|
11
|
+
|
12
|
+
validates :vacancy_id, presence: true
|
13
|
+
validates :offeror_id, presence: true
|
14
|
+
validates :resource_id, presence: true, uniqueness: { scope: [:resource_type, :vacancy_id] }
|
15
|
+
#validates :name, presence: true, uniqueness: { scope: :vacancy_id }
|
16
|
+
|
17
|
+
attr_accessible :vacancy, :vacancy_id, :name, :text
|
18
|
+
|
19
|
+
extend FriendlyId
|
20
|
+
friendly_id :name, use: :slugged
|
21
|
+
|
22
|
+
before_validation :set_offeror
|
23
|
+
|
24
|
+
# association shortcuts
|
25
|
+
def project
|
26
|
+
vacancy.project
|
27
|
+
end
|
28
|
+
|
29
|
+
def product
|
30
|
+
project.product
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def set_offeror
|
36
|
+
self.offeror_id = vacancy.project.user_id
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module StateMachines::Candidature
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
|
5
|
+
base.class_eval do
|
6
|
+
attr_accessor :current_user
|
7
|
+
|
8
|
+
const_set 'STATES', [:new, :accepted, :denied]
|
9
|
+
const_set 'EVENTS', [:accept, :deny, :quit]
|
10
|
+
|
11
|
+
after_initialize :set_initial_state
|
12
|
+
|
13
|
+
state_machine :state, initial: :new do
|
14
|
+
event :accept do
|
15
|
+
transition [:new, :denied] => :accepted
|
16
|
+
end
|
17
|
+
|
18
|
+
state :accepted do
|
19
|
+
validate :candidatures_limit_not_reached
|
20
|
+
end
|
21
|
+
|
22
|
+
event :deny do
|
23
|
+
transition [:new, :accepted] => :denied
|
24
|
+
end
|
25
|
+
|
26
|
+
event :quit do
|
27
|
+
transition :accepted => :denied
|
28
|
+
end
|
29
|
+
|
30
|
+
after_transition do |object, transition|
|
31
|
+
case transition.to
|
32
|
+
when 'accepted'
|
33
|
+
ProjectUser.find_or_create_by_project_id_and_vacancy_id_and_user_id!(
|
34
|
+
project_id: object.vacancy.project_id, vacancy_id: object.vacancy_id,
|
35
|
+
user_id: object.resource_id
|
36
|
+
)
|
37
|
+
|
38
|
+
if object.vacancy.limit == object.vacancy.candidatures.accepted.count
|
39
|
+
object.vacancy.close! unless object.vacancy.closed?
|
40
|
+
end
|
41
|
+
when 'denied'
|
42
|
+
# if comming from :accepted then the vacancy offerer has to reopen the vacancy manually
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# state validations
|
50
|
+
def candidatures_limit_not_reached
|
51
|
+
if vacancy.limit == vacancy.candidatures.where(state: 'accepted').count
|
52
|
+
errors[:state] << I18n.t('activerecord.errors.models.vacancy.attributes.limit.reached')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_initial_state
|
57
|
+
self.state ||= :new
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module ClassMethods
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module StateMachines::Vacancy
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
|
5
|
+
base.class_eval do
|
6
|
+
attr_accessor :current_user
|
7
|
+
|
8
|
+
const_set 'STATES', [:open, :recommended, :denied, :closed]
|
9
|
+
const_set 'EVENTS', [:accept_recommendation, :deny_recommendation, :close, :reopen]
|
10
|
+
|
11
|
+
after_initialize :set_initial_state
|
12
|
+
|
13
|
+
state_machine :state, initial: :new do
|
14
|
+
event :recommend do
|
15
|
+
transition :new => :recommended
|
16
|
+
end
|
17
|
+
|
18
|
+
event :accept_recommendation do
|
19
|
+
transition :recommended => :open
|
20
|
+
end
|
21
|
+
|
22
|
+
event :deny_recommendation do
|
23
|
+
transition :recommended => :denied
|
24
|
+
end
|
25
|
+
|
26
|
+
event :do_open do
|
27
|
+
transition :new => :open
|
28
|
+
end
|
29
|
+
|
30
|
+
event :close do
|
31
|
+
transition :open => :closed
|
32
|
+
end
|
33
|
+
|
34
|
+
event :reopen do
|
35
|
+
transition [:denied, :closed] => :open
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def set_initial_state
|
42
|
+
self.state ||= :new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module ClassMethods
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Vacancy < ActiveRecord::Base
|
2
|
+
include StateMachines::Vacancy
|
3
|
+
|
4
|
+
belongs_to :project
|
5
|
+
belongs_to :offeror, class_name: 'User'
|
6
|
+
belongs_to :author, class_name: 'User'
|
7
|
+
belongs_to :resource, polymorphic: true
|
8
|
+
belongs_to :project_user
|
9
|
+
|
10
|
+
has_many :candidatures, dependent: :destroy
|
11
|
+
has_many :comments, as: :commentable, dependent: :destroy
|
12
|
+
|
13
|
+
accepts_nested_attributes_for :candidatures, allow_destroy: true, reject_if: ->(t) { t['name'].blank? }
|
14
|
+
scope :open, -> { where(state: 'open') }
|
15
|
+
|
16
|
+
|
17
|
+
validates :project_id, presence: true
|
18
|
+
validates :offeror_id, presence: true
|
19
|
+
validates :name, presence: true, uniqueness: { scope: :project_id }
|
20
|
+
validates :text, presence: true
|
21
|
+
validates :limit, presence: true
|
22
|
+
|
23
|
+
attr_accessible :project_id, :name, :text, :limit, :candidatures_attributes
|
24
|
+
|
25
|
+
extend FriendlyId
|
26
|
+
|
27
|
+
friendly_id :name, use: :slugged
|
28
|
+
|
29
|
+
before_validation :set_defaults
|
30
|
+
|
31
|
+
def candidatures_left
|
32
|
+
limit - candidatures.accepted.count
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def set_defaults
|
38
|
+
if project
|
39
|
+
self.offeror_id = project.user_id
|
40
|
+
self.author_id = project.user_id unless self.author_id.present?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|