introspective_grape 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +35 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +103 -0
- data/Rakefile +26 -0
- data/app/assets/images/introspective_grape/.keep +0 -0
- data/app/assets/stylesheets/introspective_grape/.keep +0 -0
- data/app/controllers/.keep +0 -0
- data/app/helpers/.keep +0 -0
- data/app/mailers/.keep +0 -0
- data/app/models/.keep +0 -0
- data/app/views/.keep +0 -0
- data/bin/rails +12 -0
- data/introspective_grape.gemspec +49 -0
- data/lib/introspective_grape/api.rb +445 -0
- data/lib/introspective_grape/camel_snake.rb +71 -0
- data/lib/introspective_grape/version.rb +3 -0
- data/lib/introspective_grape.rb +4 -0
- data/lib/tasks/introspective_grape_tasks.rake +4 -0
- data/spec/dummy/Gemfile +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/api/active_record_helpers.rb +17 -0
- data/spec/dummy/app/api/api_helpers.rb +36 -0
- data/spec/dummy/app/api/dummy/chat_api.rb +108 -0
- data/spec/dummy/app/api/dummy/company_api.rb +8 -0
- data/spec/dummy/app/api/dummy/entities.rb +25 -0
- data/spec/dummy/app/api/dummy/location_api.rb +37 -0
- data/spec/dummy/app/api/dummy/project_api.rb +51 -0
- data/spec/dummy/app/api/dummy/role_api.rb +7 -0
- data/spec/dummy/app/api/dummy/sessions.rb +55 -0
- data/spec/dummy/app/api/dummy/user_api.rb +32 -0
- data/spec/dummy/app/api/dummy_api.rb +57 -0
- data/spec/dummy/app/api/error_handlers.rb +28 -0
- data/spec/dummy/app/api/permissions_helper.rb +7 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/abstract_adapter.rb +13 -0
- data/spec/dummy/app/models/admin_user.rb +6 -0
- data/spec/dummy/app/models/chat.rb +18 -0
- data/spec/dummy/app/models/chat_message.rb +34 -0
- data/spec/dummy/app/models/chat_message_user.rb +17 -0
- data/spec/dummy/app/models/chat_user.rb +16 -0
- data/spec/dummy/app/models/company.rb +14 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/image.rb +21 -0
- data/spec/dummy/app/models/job.rb +10 -0
- data/spec/dummy/app/models/locatable.rb +6 -0
- data/spec/dummy/app/models/location.rb +26 -0
- data/spec/dummy/app/models/location_beacon.rb +16 -0
- data/spec/dummy/app/models/location_gps.rb +14 -0
- data/spec/dummy/app/models/project.rb +20 -0
- data/spec/dummy/app/models/project_job.rb +7 -0
- data/spec/dummy/app/models/role.rb +30 -0
- data/spec/dummy/app/models/super_user.rb +11 -0
- data/spec/dummy/app/models/team.rb +9 -0
- data/spec/dummy/app/models/team_user.rb +13 -0
- data/spec/dummy/app/models/user/chatter.rb +79 -0
- data/spec/dummy/app/models/user.rb +84 -0
- data/spec/dummy/app/models/user_location.rb +28 -0
- data/spec/dummy/app/models/user_project_job.rb +16 -0
- data/spec/dummy/app/policies/application_policy.rb +47 -0
- data/spec/dummy/app/policies/chat_policy.rb +22 -0
- data/spec/dummy/app/policies/company_policy.rb +32 -0
- data/spec/dummy/app/policies/location_policy.rb +29 -0
- data/spec/dummy/app/policies/project_policy.rb +42 -0
- data/spec/dummy/app/policies/role_policy.rb +33 -0
- data/spec/dummy/app/policies/user_location_policy.rb +12 -0
- data/spec/dummy/app/policies/user_policy.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +13 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config/application.rb +38 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +23 -0
- data/spec/dummy/config/environment.rb +11 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +43 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/devise.rb +262 -0
- data/spec/dummy/config/initializers/devise_async.rb +2 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/paperclip.rb +13 -0
- data/spec/dummy/config/initializers/paperclip_adapter.rb +13 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/devise.en.yml +60 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +8 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20141002205024_devise_create_users.rb +42 -0
- data/spec/dummy/db/migrate/20141002211055_devise_create_admin_users.rb +48 -0
- data/spec/dummy/db/migrate/20141002211057_create_active_admin_comments.rb +19 -0
- data/spec/dummy/db/migrate/20141002220722_add_lockable_to_users.rb +8 -0
- data/spec/dummy/db/migrate/20150406213646_create_companies.rb +11 -0
- data/spec/dummy/db/migrate/20150414213154_add_user_authentication_token.rb +11 -0
- data/spec/dummy/db/migrate/20150415222005_create_roles.rb +12 -0
- data/spec/dummy/db/migrate/20150505181635_create_chats.rb +9 -0
- data/spec/dummy/db/migrate/20150505181636_create_chat_users.rb +11 -0
- data/spec/dummy/db/migrate/20150505181640_create_chat_messages.rb +11 -0
- data/spec/dummy/db/migrate/20150507191529_create_chat_message_users.rb +11 -0
- data/spec/dummy/db/migrate/20150601200526_create_locations.rb +12 -0
- data/spec/dummy/db/migrate/20150601200533_create_locatables.rb +10 -0
- data/spec/dummy/db/migrate/20150601212924_create_location_beacons.rb +15 -0
- data/spec/dummy/db/migrate/20150601213542_create_location_gps.rb +12 -0
- data/spec/dummy/db/migrate/20150609201823_create_user_locations.rb +14 -0
- data/spec/dummy/db/migrate/20150616205336_add_role_user_constraint.rb +9 -0
- data/spec/dummy/db/migrate/20150617232519_create_projects.rb +10 -0
- data/spec/dummy/db/migrate/20150617232521_create_jobs.rb +9 -0
- data/spec/dummy/db/migrate/20150617232522_create_project_jobs.rb +11 -0
- data/spec/dummy/db/migrate/20150623170133_create_user_project_jobs.rb +12 -0
- data/spec/dummy/db/migrate/20150701234929_create_teams.rb +11 -0
- data/spec/dummy/db/migrate/20150701234930_create_team_users.rb +11 -0
- data/spec/dummy/db/migrate/20150727214950_add_confirmable_to_devise.rb +11 -0
- data/spec/dummy/db/migrate/20150820190524_add_user_names.rb +6 -0
- data/spec/dummy/db/migrate/20150824215701_create_images.rb +15 -0
- data/spec/dummy/db/migrate/20150909225019_add_password_to_project.rb +5 -0
- data/spec/dummy/db/schema.rb +278 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/fixtures/images/avatar.jpeg +0 -0
- data/spec/fixtures/images/exif.jpeg +0 -0
- data/spec/models/chat_spec.rb +32 -0
- data/spec/models/image_spec.rb +14 -0
- data/spec/models/locatable_spec.rb +10 -0
- data/spec/models/project_spec.rb +17 -0
- data/spec/models/role_spec.rb +63 -0
- data/spec/models/team_spec.rb +17 -0
- data/spec/models/team_user_spec.rb +20 -0
- data/spec/models/user_location_spec.rb +35 -0
- data/spec/models/user_project_job_spec.rb +30 -0
- data/spec/models/user_spec.rb +125 -0
- data/spec/rails_helper.rb +23 -0
- data/spec/requests/chat_api_spec.rb +174 -0
- data/spec/requests/company_api_spec.rb +61 -0
- data/spec/requests/location_api_spec.rb +96 -0
- data/spec/requests/project_api_spec.rb +151 -0
- data/spec/requests/role_api_spec.rb +37 -0
- data/spec/requests/sessions_api_spec.rb +55 -0
- data/spec/requests/user_api_spec.rb +191 -0
- data/spec/support/blueprints.rb +103 -0
- data/spec/support/location_helper.rb +56 -0
- data/spec/support/pundit_helpers.rb +13 -0
- data/spec/support/request_helpers.rb +22 -0
- metadata +562 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
class Location < AbstractAdapter
|
2
|
+
has_many :locatables, dependent: :destroy
|
3
|
+
has_many :companies, through: :locatables, source: :locatable, source_type: 'Company'
|
4
|
+
|
5
|
+
has_many :beacons, class_name: 'LocationBeacon', dependent: :destroy
|
6
|
+
has_one :gps, class_name: 'LocationGps', dependent: :destroy
|
7
|
+
delegate :lat,:lng,:alt, to: :gps
|
8
|
+
|
9
|
+
belongs_to :parent_location, foreign_key: :parent_location_id, class_name: 'Location', inverse_of: :child_locations
|
10
|
+
has_many :child_locations, foreign_key: :parent_location_id, class_name: 'Location', dependent: :destroy, inverse_of: :parent_location
|
11
|
+
|
12
|
+
has_many :user_locations, dependent: :destroy
|
13
|
+
|
14
|
+
# isn't this list going to be kinda long? are there any reasonable constraints to put
|
15
|
+
# on this random bit of metadata?
|
16
|
+
validates_inclusion_of :kind, in: %w(airport terminal gate plane)
|
17
|
+
|
18
|
+
accepts_nested_attributes_for :child_locations, allow_destroy: true
|
19
|
+
accepts_nested_attributes_for :gps, allow_destroy: true
|
20
|
+
accepts_nested_attributes_for :beacons, allow_destroy: true
|
21
|
+
|
22
|
+
def coords
|
23
|
+
[gps.lat, gps.lng, gps.alt]
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class LocationBeacon < AbstractAdapter
|
2
|
+
belongs_to :location
|
3
|
+
has_many :gps, through: :location
|
4
|
+
belongs_to :company
|
5
|
+
|
6
|
+
# B9407F30-F5F8-466E-AFF9-25556B57FE6D
|
7
|
+
validates_format_of :uuid, with: /[0-9a-fA-F]{32}/ # 32 digit hexadecimal UUID
|
8
|
+
validates_format_of :mac_address, with: /[0-9a-fA-F]{12}/ # 16 digit hexadecimal bluetooth MAC address
|
9
|
+
|
10
|
+
before_validation :massage_ids
|
11
|
+
def massage_ids
|
12
|
+
self.uuid = (uuid||'').gsub(/[^0-9a-fA-F]+/,'').upcase
|
13
|
+
self.mac_address = (mac_address||'').gsub(/[^0-9a-fA-F]+/,'').upcase
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class LocationGps < AbstractAdapter
|
2
|
+
belongs_to :location
|
3
|
+
has_many :beacons, through: :location
|
4
|
+
|
5
|
+
# lat and lng in degrees altitude in meters
|
6
|
+
validates_numericality_of :lat, greater_than_or_equal_to: -90.0, less_than_or_equal_to: 90.0
|
7
|
+
validates_numericality_of :lng, greater_than_or_equal_to: -180.0, less_than_or_equal_to: 180.0
|
8
|
+
validates_numericality_of :alt
|
9
|
+
|
10
|
+
def distance_from(lat,lng) # calc distance between this location and the passed coords
|
11
|
+
Haversine.distance(self.lat,self.lng, lat,lng).to_meters
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Project < AbstractAdapter
|
2
|
+
belongs_to :owner, foreign_key: :owner_id, class_name: 'Company'
|
3
|
+
|
4
|
+
has_many :roles, as: :ownable
|
5
|
+
has_many :admins, through: :roles, source: :user
|
6
|
+
accepts_nested_attributes_for :roles, allow_destroy: true
|
7
|
+
|
8
|
+
has_many :project_jobs, dependent: :destroy, inverse_of: :project
|
9
|
+
has_many :jobs, through: :project_jobs
|
10
|
+
accepts_nested_attributes_for :project_jobs, allow_destroy: true
|
11
|
+
|
12
|
+
has_many :user_project_jobs, dependent: :destroy, inverse_of: :project
|
13
|
+
has_many :users, through: :user_project_jobs, inverse_of: :projects
|
14
|
+
accepts_nested_attributes_for :user_project_jobs, allow_destroy: true
|
15
|
+
|
16
|
+
has_many :teams, dependent: :destroy, inverse_of: :project
|
17
|
+
accepts_nested_attributes_for :teams, allow_destroy: true
|
18
|
+
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Role < AbstractAdapter
|
2
|
+
belongs_to :user
|
3
|
+
belongs_to :ownable, polymorphic: true
|
4
|
+
|
5
|
+
validates_uniqueness_of :user_id, scope: [:ownable_type,:ownable_id], unless: "user_id.nil?", message: "user has already been assigned that role"
|
6
|
+
validates_inclusion_of :ownable_type, in: ['SuperUser', 'Company', 'Project']
|
7
|
+
|
8
|
+
delegate :email, to: :user, allow_nil: true
|
9
|
+
def attributes
|
10
|
+
super.merge(email: email)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ownable
|
14
|
+
# return the SuperUser null object
|
15
|
+
ownable_type == 'SuperUser' ? SuperUser.new : super
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.ownable_assign_options(model=nil)
|
19
|
+
([SuperUser.new] + Company.all + Project.all).map { |i| [ "#{i.class}: #{i.name}", "#{i.class}-#{i.id}"] }
|
20
|
+
end
|
21
|
+
|
22
|
+
def ownable_assign
|
23
|
+
ownable.present? ? "#{ownable_type}-#{ownable_id}" : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def ownable_assign=(value)
|
27
|
+
self.ownable_type,self.ownable_id = value.split('-')
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#require 'activerecord-tableless'
|
2
|
+
class SuperUser < AbstractAdapter
|
3
|
+
# An empty ActiveRecord association for the polymorphic identity on Role.
|
4
|
+
# We could also just create a SuperUser table with one record to do this...
|
5
|
+
has_no_table :database => :pretend_success
|
6
|
+
|
7
|
+
def name
|
8
|
+
'Admin'
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class TeamUser < AbstractAdapter
|
2
|
+
belongs_to :user
|
3
|
+
belongs_to :team
|
4
|
+
|
5
|
+
validate :user_on_project
|
6
|
+
|
7
|
+
def user_on_project
|
8
|
+
unless user && team && user.projects.include?(team.project)
|
9
|
+
errors.add(:user, "#{user.try(:name)} is not on the #{team.try(:project).try(:name)} project")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module User::Chatter
|
2
|
+
|
3
|
+
def message_query(chat_id, new = true)
|
4
|
+
messages.joins(:chat_message_users)
|
5
|
+
.where('chat_message_users.user_id'=> id)
|
6
|
+
.where(new ? {'chat_message_users.read_at'=>nil} : '')
|
7
|
+
.where(chat_id ? {'chat_messages.chat_id'=> chat_id} : '')
|
8
|
+
.order('') # or it will add an order by id clause that breaks the count query.
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_messages?(chat=nil) # returns a hash of chat_ids with new message counts
|
12
|
+
chat_id = chat.kind_of?(Chat) ? chat.id : chat
|
13
|
+
new = message_query(chat_id, new = true)
|
14
|
+
.select("chat_messages.chat_id, count(chat_messages.id) as count")
|
15
|
+
.group('chat_id')
|
16
|
+
|
17
|
+
chat ? { chat_id => new.first.try(:count)||0 } : Hash[new.map {|c| [c.chat_id, c.count]} ]
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_messages(chat= nil, mark_as_read= false, new= true)
|
21
|
+
chat_id = chat.kind_of?(Chat) ? chat.id : chat
|
22
|
+
new = message_query(chat_id, new).order('chat_messages.created_at').includes(:author) # :chat?
|
23
|
+
new.map(&:chat).uniq.each {|chat| mark_as_read(chat) } if mark_as_read
|
24
|
+
new
|
25
|
+
end
|
26
|
+
|
27
|
+
def chat(users, message)
|
28
|
+
users = [users].flatten
|
29
|
+
users = users.first.kind_of?(User) ? users : User.where(id: users)
|
30
|
+
chat = Chat.create(creator: self)
|
31
|
+
chat.users.push users
|
32
|
+
chat.messages.build(message: message, author: self)
|
33
|
+
chat.save!
|
34
|
+
chat
|
35
|
+
end
|
36
|
+
|
37
|
+
def reply(chat, message)
|
38
|
+
chat = chat.kind_of?(Chat) ? chat : Chat.find(chat)
|
39
|
+
mark_as_read(chat) # a reply implies that the thread has been read
|
40
|
+
chat.messages.build(message: message, author: self)
|
41
|
+
chat.save!
|
42
|
+
chat
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_chatters(chat, users)
|
46
|
+
users = [users].flatten
|
47
|
+
users = users.first.kind_of?(User) ? users : User.where(id: users)
|
48
|
+
chat = chat.kind_of?(Chat) ? chat : Chat.find(chat)
|
49
|
+
|
50
|
+
if chat.active_users.include?(self) # only current participants can add new users
|
51
|
+
chat.users.push users
|
52
|
+
chat.messages.build(chat: chat, author: self, message: "#{self.name} [[ADDED_USER_MESSAGE]] #{users.map(&:name).join(',')}")
|
53
|
+
chat.save!
|
54
|
+
else
|
55
|
+
chat.errors[:base] << "Only current chat participants can add users."
|
56
|
+
raise ActiveRecord::RecordInvalid.new(chat)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def leave_chat(chat)
|
61
|
+
chat = chat.kind_of?(Chat) ? chat : Chat.find(chat)
|
62
|
+
|
63
|
+
if chat.active_users.include?(self)
|
64
|
+
reply(chat, "#{name} [[DEPARTS_MESSAGE]]")
|
65
|
+
chat.chat_users.detect {|cu| cu.user_id == self.id}.update_attributes(departed_at: Time.now)
|
66
|
+
else
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def mark_as_read(chat)
|
72
|
+
ChatMessageUser.joins(:chat_message).where('read_at IS NULL AND chat_messages.chat_id = ? AND user_id = ?', chat.id, id).update_all(read_at: Time.now)
|
73
|
+
end
|
74
|
+
|
75
|
+
def mark_messages_as_read(messages)
|
76
|
+
chat_message_users.where("chat_message_id in (?) and read_at IS NULL", messages.map(&:id)).update_all(read_at: Time.now)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#require 'devise/async'
|
2
|
+
class User < AbstractAdapter
|
3
|
+
# Include default devise modules. Others available are:
|
4
|
+
# :confirmable, :lockable, :timeoutable and :omniauthable
|
5
|
+
devise :database_authenticatable, :registerable, :async, :confirmable,
|
6
|
+
:recoverable, :rememberable, :trackable, :validatable, :lockable
|
7
|
+
|
8
|
+
scope :active, -> { where(:locked_at => nil) }
|
9
|
+
scope :inactive, -> { where('locked_at is not null') }
|
10
|
+
|
11
|
+
has_many :user_locations, dependent: :destroy
|
12
|
+
|
13
|
+
has_many :user_project_jobs, dependent: :destroy, inverse_of: :user
|
14
|
+
has_many :jobs, through: :user_project_jobs, inverse_of: :users
|
15
|
+
has_many :projects, through: :user_project_jobs, inverse_of: :users
|
16
|
+
accepts_nested_attributes_for :user_project_jobs
|
17
|
+
|
18
|
+
before_validation :set_default_password_from_project, on: :create
|
19
|
+
|
20
|
+
has_many :team_users
|
21
|
+
has_many :teams, through: :team_users
|
22
|
+
|
23
|
+
has_one :avatar, class_name: 'Image', as: :imageable
|
24
|
+
accepts_nested_attributes_for :avatar, allow_destroy: true
|
25
|
+
|
26
|
+
|
27
|
+
has_many :own_chats, foreign_key: :creator_id, class_name: 'Chat'
|
28
|
+
has_many :chat_users
|
29
|
+
has_many :chats, through: :chat_users
|
30
|
+
has_many :chat_message_users
|
31
|
+
has_many :messages, ->{ where('chat_messages.created_at >= chat_users.created_at and (chat_users.departed_at IS NULL OR chat_messages.created_at <= chat_users.departed_at)') }, through: :chats
|
32
|
+
include User::Chatter
|
33
|
+
|
34
|
+
has_many :roles, dependent: :destroy, inverse_of: :user
|
35
|
+
accepts_nested_attributes_for :roles, allow_destroy: true
|
36
|
+
has_many :admin_companies, through: :roles, source: :ownable, source_type: Company
|
37
|
+
has_many :admin_projects, through: :roles, source: :ownable, source_type: Project
|
38
|
+
|
39
|
+
def all_admin_projects # aggregate companies' projects with project admin roles
|
40
|
+
(admin_companies.map(&:projects)+admin_projects).flatten
|
41
|
+
end
|
42
|
+
|
43
|
+
def superuser?
|
44
|
+
roles && roles.detect{|r| r.ownable_type == 'SuperUser' }.present?
|
45
|
+
end
|
46
|
+
|
47
|
+
def admin?(record)
|
48
|
+
superuser? || roles.detect{|r| r.ownable == record }.present?
|
49
|
+
end
|
50
|
+
|
51
|
+
def company_admin? # an admin of any company
|
52
|
+
superuser? || roles.detect{|r| r.ownable_type == 'Company' }.present?
|
53
|
+
end
|
54
|
+
|
55
|
+
def project_admin? # an admin of any project
|
56
|
+
superuser? || company_admin? || roles.detect{|r| r.ownable_type == 'Project' }.present?
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def set_default_password_from_project
|
61
|
+
self.password = user_project_jobs.first.try(:project).try(:default_password) if password.blank?
|
62
|
+
end
|
63
|
+
|
64
|
+
def name
|
65
|
+
[first_name,last_name].delete_if(&:blank?).join(' ')
|
66
|
+
end
|
67
|
+
|
68
|
+
def avatar_url(size='medium')
|
69
|
+
avatar.try(:file).try(:url,size)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.attribute_param_types
|
73
|
+
{ "skip_confirmation_email" => Virtus::Attribute::Boolean }
|
74
|
+
end
|
75
|
+
|
76
|
+
def skip_confirmation_email=(s)
|
77
|
+
return unless s.to_s == "true"
|
78
|
+
# skip_confirmation! does not work with update_attributes, a work-around:
|
79
|
+
self.update_column(:email, email) && self.reload if self.valid? && self.id
|
80
|
+
# devise: confirm the user without requiring a confirmation email
|
81
|
+
self.skip_confirmation!
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class UserLocation < AbstractAdapter
|
2
|
+
belongs_to :user
|
3
|
+
belongs_to :location
|
4
|
+
belongs_to :detectable, polymorphic: true
|
5
|
+
|
6
|
+
validates_inclusion_of :detectable_type, in: %w(LocationBeacon LocationGps)
|
7
|
+
|
8
|
+
default_scope { includes(:detectable).order("created_at desc") }
|
9
|
+
|
10
|
+
def coords=(c) # convenience method to set coordinates by an array of [lat,lng,alt]
|
11
|
+
self.lat = c[0]
|
12
|
+
self.lng = c[1]
|
13
|
+
self.alt = c[2]
|
14
|
+
end
|
15
|
+
|
16
|
+
def beacon
|
17
|
+
detectable.is_a?(LocationBeacon) ? detectable : {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def distance
|
21
|
+
if location.gps && lat && lng
|
22
|
+
location.gps.distance_from(lat,lng)
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class UserProjectJob < AbstractAdapter
|
2
|
+
belongs_to :user, inverse_of: :user_project_jobs
|
3
|
+
belongs_to :project, inverse_of: :user_project_jobs
|
4
|
+
belongs_to :job, inverse_of: :user_project_jobs
|
5
|
+
|
6
|
+
validates_inclusion_of :job, in: proc {|r| r.project.try(:jobs) || [] }
|
7
|
+
|
8
|
+
delegate :email, :avatar_url, to: :user
|
9
|
+
delegate :title, to: :job
|
10
|
+
delegate :name, to: :project
|
11
|
+
|
12
|
+
def self.options_for_job(project=nil)
|
13
|
+
project.jobs
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class ApplicationPolicy
|
2
|
+
attr_reader :user, :record
|
3
|
+
|
4
|
+
def initialize(user, record)
|
5
|
+
@user = user
|
6
|
+
@record = record
|
7
|
+
end
|
8
|
+
|
9
|
+
def index?
|
10
|
+
@user.superuser?
|
11
|
+
end
|
12
|
+
|
13
|
+
def show?
|
14
|
+
@user.superuser? || scope.where(:id => record.id).exists?
|
15
|
+
end
|
16
|
+
|
17
|
+
def create?
|
18
|
+
@user.superuser?
|
19
|
+
end
|
20
|
+
|
21
|
+
def update?
|
22
|
+
@user.superuser?
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy?
|
26
|
+
@user.superuser?
|
27
|
+
end
|
28
|
+
|
29
|
+
def scope
|
30
|
+
Pundit.policy_scope!(user, record.class)
|
31
|
+
end
|
32
|
+
|
33
|
+
class Scope
|
34
|
+
attr_reader :user, :scope
|
35
|
+
|
36
|
+
def initialize(user, scope)
|
37
|
+
@user = user
|
38
|
+
@scope = scope
|
39
|
+
end
|
40
|
+
|
41
|
+
def resolve
|
42
|
+
scope.all
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class CompanyPolicy < ApplicationPolicy
|
2
|
+
def index?
|
3
|
+
@user
|
4
|
+
end
|
5
|
+
|
6
|
+
def show?
|
7
|
+
@user && @user.admin?(record)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update?
|
11
|
+
@user && @user.admin?(record)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create?
|
15
|
+
@user && @user.admin?(record)
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
class Scope < ApplicationPolicy::Scope
|
23
|
+
def resolve
|
24
|
+
if user.superuser?
|
25
|
+
scope.all
|
26
|
+
else
|
27
|
+
scope.find( user.companies.map{|c| c.id} )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class LocationPolicy < ApplicationPolicy
|
2
|
+
def index?
|
3
|
+
@user
|
4
|
+
end
|
5
|
+
|
6
|
+
def show?
|
7
|
+
@user
|
8
|
+
end
|
9
|
+
|
10
|
+
def update?
|
11
|
+
@user && @user.superuser?
|
12
|
+
end
|
13
|
+
|
14
|
+
def create?
|
15
|
+
@user && @user.superuser?
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy?
|
19
|
+
@user && @user.superuser?
|
20
|
+
end
|
21
|
+
|
22
|
+
class Scope < ApplicationPolicy::Scope
|
23
|
+
def resolve
|
24
|
+
scope.where(parent_location_id: nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class ProjectPolicy < ApplicationPolicy
|
2
|
+
def index?
|
3
|
+
@user
|
4
|
+
end
|
5
|
+
|
6
|
+
def project_user?
|
7
|
+
project_manager? || record.users.include?(@user)
|
8
|
+
end
|
9
|
+
|
10
|
+
def project_manager?
|
11
|
+
@user.superuser? || @user.all_admin_projects.include?(record)
|
12
|
+
end
|
13
|
+
|
14
|
+
def show?
|
15
|
+
@user && project_user?
|
16
|
+
end
|
17
|
+
|
18
|
+
def update?
|
19
|
+
@user && project_manager?
|
20
|
+
end
|
21
|
+
|
22
|
+
def create?
|
23
|
+
@user && project_manager?
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy?
|
27
|
+
@user && project_manager?
|
28
|
+
end
|
29
|
+
|
30
|
+
class Scope < ApplicationPolicy::Scope
|
31
|
+
def resolve
|
32
|
+
if user.superuser?
|
33
|
+
scope.all
|
34
|
+
elsif user.project_admin?
|
35
|
+
scope.find( (user.all_admin_projects+user.projects).map(&:id) )
|
36
|
+
else
|
37
|
+
scope.find( user.projects )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class RolePolicy < ApplicationPolicy
|
2
|
+
|
3
|
+
def index?
|
4
|
+
@user
|
5
|
+
end
|
6
|
+
|
7
|
+
def show?
|
8
|
+
@user.admin?(record.ownable)
|
9
|
+
end
|
10
|
+
|
11
|
+
def update?
|
12
|
+
@user.admin?(record.ownable)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create?
|
16
|
+
@user.admin?(record.ownable)
|
17
|
+
end
|
18
|
+
|
19
|
+
def destroy?
|
20
|
+
@user.admin?(record.ownable)
|
21
|
+
end
|
22
|
+
|
23
|
+
class Scope < ApplicationPolicy::Scope
|
24
|
+
def resolve
|
25
|
+
if user.superuser?
|
26
|
+
scope.all
|
27
|
+
else
|
28
|
+
scope.where(ownable: user.admin_companies )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class UserLocationPolicy < ApplicationPolicy
|
2
|
+
def index?
|
3
|
+
# This will need further specifications once the user-location relationship via
|
4
|
+
# their companies is defined via project etc.
|
5
|
+
@user && @user.company_admin?
|
6
|
+
end
|
7
|
+
|
8
|
+
def create?
|
9
|
+
@user
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
data/spec/dummy/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
# path to your application root.
|
5
|
+
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
|
6
|
+
|
7
|
+
Dir.chdir APP_ROOT do
|
8
|
+
# This script is a starting point to setup your application.
|
9
|
+
# Add necessary setup steps to this file:
|
10
|
+
|
11
|
+
puts "== Installing dependencies =="
|
12
|
+
system "gem install bundler --conservative"
|
13
|
+
system "bundle check || bundle install"
|
14
|
+
|
15
|
+
# puts "\n== Copying sample files =="
|
16
|
+
# unless File.exist?("config/database.yml")
|
17
|
+
# system "cp config/database.yml.sample config/database.yml"
|
18
|
+
# end
|
19
|
+
|
20
|
+
puts "\n== Preparing database =="
|
21
|
+
system "bin/rake db:setup"
|
22
|
+
|
23
|
+
puts "\n== Removing old logs and tempfiles =="
|
24
|
+
system "rm -f log/*"
|
25
|
+
system "rm -rf tmp/cache"
|
26
|
+
|
27
|
+
puts "\n== Restarting application server =="
|
28
|
+
system "touch tmp/restart.txt"
|
29
|
+
end
|