voluntary_recruiting 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1 -0
  4. data/Rakefile +26 -0
  5. data/app/controllers/candidatures_controller.rb +84 -0
  6. data/app/controllers/vacancies_controller.rb +88 -0
  7. data/app/controllers/workflow/candidatures_controller.rb +20 -0
  8. data/app/controllers/workflow/vacancies_controller.rb +17 -0
  9. data/app/models/candidature.rb +38 -0
  10. data/app/models/product/recruiting.rb +2 -0
  11. data/app/models/state_machines/candidature.rb +64 -0
  12. data/app/models/state_machines/vacancy.rb +49 -0
  13. data/app/models/vacancy.rb +43 -0
  14. data/app/views/candidatures/_form.html.erb +13 -0
  15. data/app/views/candidatures/edit.html.erb +3 -0
  16. data/app/views/candidatures/index.html.erb +11 -0
  17. data/app/views/candidatures/new.html.erb +3 -0
  18. data/app/views/candidatures/show.html.erb +18 -0
  19. data/app/views/vacancies/_form.html.erb +15 -0
  20. data/app/views/vacancies/edit.html.erb +3 -0
  21. data/app/views/vacancies/index.html.erb +7 -0
  22. data/app/views/vacancies/new.html.erb +3 -0
  23. data/app/views/vacancies/show.html.erb +15 -0
  24. data/app/views/workflow/project_owner/_candidatures.html.erb +32 -0
  25. data/app/views/workflow/project_owner/_vacancies.html.erb +27 -0
  26. data/config/locales/general/en.yml +17 -0
  27. data/config/locales/resources/candidature/en.yml +18 -0
  28. data/config/locales/resources/vacancy/en.yml +33 -0
  29. data/config/routes.rb +67 -0
  30. data/db/migrate/20150711124651_add_recruiting.rb +60 -0
  31. data/lib/tasks/voluntary_recruiting_tasks.rake +4 -0
  32. data/lib/voluntary_recruiting.rb +13 -0
  33. data/lib/voluntary_recruiting/ability.rb +17 -0
  34. data/lib/voluntary_recruiting/concerns/model/project/recruitable.rb +15 -0
  35. data/lib/voluntary_recruiting/concerns/model/project_user/recruitable.rb +19 -0
  36. data/lib/voluntary_recruiting/concerns/model/user/recruitable.rb +17 -0
  37. data/lib/voluntary_recruiting/controller/project_owner_controller.rb +26 -0
  38. data/lib/voluntary_recruiting/engine.rb +22 -0
  39. data/lib/voluntary_recruiting/navigation.rb +106 -0
  40. data/lib/voluntary_recruiting/version.rb +3 -0
  41. 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,2 @@
1
+ class Product::Recruiting < ::Product
2
+ 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