voluntary_recruiting 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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