introspective_grape 0.0.3

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 (165) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +35 -0
  4. data/Gemfile +15 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +103 -0
  7. data/Rakefile +26 -0
  8. data/app/assets/images/introspective_grape/.keep +0 -0
  9. data/app/assets/stylesheets/introspective_grape/.keep +0 -0
  10. data/app/controllers/.keep +0 -0
  11. data/app/helpers/.keep +0 -0
  12. data/app/mailers/.keep +0 -0
  13. data/app/models/.keep +0 -0
  14. data/app/views/.keep +0 -0
  15. data/bin/rails +12 -0
  16. data/introspective_grape.gemspec +49 -0
  17. data/lib/introspective_grape/api.rb +445 -0
  18. data/lib/introspective_grape/camel_snake.rb +71 -0
  19. data/lib/introspective_grape/version.rb +3 -0
  20. data/lib/introspective_grape.rb +4 -0
  21. data/lib/tasks/introspective_grape_tasks.rake +4 -0
  22. data/spec/dummy/Gemfile +4 -0
  23. data/spec/dummy/README.rdoc +28 -0
  24. data/spec/dummy/Rakefile +6 -0
  25. data/spec/dummy/app/api/active_record_helpers.rb +17 -0
  26. data/spec/dummy/app/api/api_helpers.rb +36 -0
  27. data/spec/dummy/app/api/dummy/chat_api.rb +108 -0
  28. data/spec/dummy/app/api/dummy/company_api.rb +8 -0
  29. data/spec/dummy/app/api/dummy/entities.rb +25 -0
  30. data/spec/dummy/app/api/dummy/location_api.rb +37 -0
  31. data/spec/dummy/app/api/dummy/project_api.rb +51 -0
  32. data/spec/dummy/app/api/dummy/role_api.rb +7 -0
  33. data/spec/dummy/app/api/dummy/sessions.rb +55 -0
  34. data/spec/dummy/app/api/dummy/user_api.rb +32 -0
  35. data/spec/dummy/app/api/dummy_api.rb +57 -0
  36. data/spec/dummy/app/api/error_handlers.rb +28 -0
  37. data/spec/dummy/app/api/permissions_helper.rb +7 -0
  38. data/spec/dummy/app/assets/images/.keep +0 -0
  39. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  40. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  41. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  42. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  43. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  44. data/spec/dummy/app/mailers/.keep +0 -0
  45. data/spec/dummy/app/models/.keep +0 -0
  46. data/spec/dummy/app/models/abstract_adapter.rb +13 -0
  47. data/spec/dummy/app/models/admin_user.rb +6 -0
  48. data/spec/dummy/app/models/chat.rb +18 -0
  49. data/spec/dummy/app/models/chat_message.rb +34 -0
  50. data/spec/dummy/app/models/chat_message_user.rb +17 -0
  51. data/spec/dummy/app/models/chat_user.rb +16 -0
  52. data/spec/dummy/app/models/company.rb +14 -0
  53. data/spec/dummy/app/models/concerns/.keep +0 -0
  54. data/spec/dummy/app/models/image.rb +21 -0
  55. data/spec/dummy/app/models/job.rb +10 -0
  56. data/spec/dummy/app/models/locatable.rb +6 -0
  57. data/spec/dummy/app/models/location.rb +26 -0
  58. data/spec/dummy/app/models/location_beacon.rb +16 -0
  59. data/spec/dummy/app/models/location_gps.rb +14 -0
  60. data/spec/dummy/app/models/project.rb +20 -0
  61. data/spec/dummy/app/models/project_job.rb +7 -0
  62. data/spec/dummy/app/models/role.rb +30 -0
  63. data/spec/dummy/app/models/super_user.rb +11 -0
  64. data/spec/dummy/app/models/team.rb +9 -0
  65. data/spec/dummy/app/models/team_user.rb +13 -0
  66. data/spec/dummy/app/models/user/chatter.rb +79 -0
  67. data/spec/dummy/app/models/user.rb +84 -0
  68. data/spec/dummy/app/models/user_location.rb +28 -0
  69. data/spec/dummy/app/models/user_project_job.rb +16 -0
  70. data/spec/dummy/app/policies/application_policy.rb +47 -0
  71. data/spec/dummy/app/policies/chat_policy.rb +22 -0
  72. data/spec/dummy/app/policies/company_policy.rb +32 -0
  73. data/spec/dummy/app/policies/location_policy.rb +29 -0
  74. data/spec/dummy/app/policies/project_policy.rb +42 -0
  75. data/spec/dummy/app/policies/role_policy.rb +33 -0
  76. data/spec/dummy/app/policies/user_location_policy.rb +12 -0
  77. data/spec/dummy/app/policies/user_policy.rb +8 -0
  78. data/spec/dummy/app/views/layouts/application.html.erb +13 -0
  79. data/spec/dummy/bin/bundle +3 -0
  80. data/spec/dummy/bin/rails +4 -0
  81. data/spec/dummy/bin/rake +4 -0
  82. data/spec/dummy/bin/setup +29 -0
  83. data/spec/dummy/config/application.rb +38 -0
  84. data/spec/dummy/config/boot.rb +6 -0
  85. data/spec/dummy/config/database.yml +23 -0
  86. data/spec/dummy/config/environment.rb +11 -0
  87. data/spec/dummy/config/environments/development.rb +41 -0
  88. data/spec/dummy/config/environments/production.rb +79 -0
  89. data/spec/dummy/config/environments/test.rb +43 -0
  90. data/spec/dummy/config/initializers/assets.rb +11 -0
  91. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  92. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  93. data/spec/dummy/config/initializers/devise.rb +262 -0
  94. data/spec/dummy/config/initializers/devise_async.rb +2 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/paperclip.rb +13 -0
  99. data/spec/dummy/config/initializers/paperclip_adapter.rb +13 -0
  100. data/spec/dummy/config/initializers/session_store.rb +3 -0
  101. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  102. data/spec/dummy/config/locales/devise.en.yml +60 -0
  103. data/spec/dummy/config/locales/en.yml +23 -0
  104. data/spec/dummy/config/routes.rb +8 -0
  105. data/spec/dummy/config/secrets.yml +22 -0
  106. data/spec/dummy/config.ru +4 -0
  107. data/spec/dummy/db/migrate/20141002205024_devise_create_users.rb +42 -0
  108. data/spec/dummy/db/migrate/20141002211055_devise_create_admin_users.rb +48 -0
  109. data/spec/dummy/db/migrate/20141002211057_create_active_admin_comments.rb +19 -0
  110. data/spec/dummy/db/migrate/20141002220722_add_lockable_to_users.rb +8 -0
  111. data/spec/dummy/db/migrate/20150406213646_create_companies.rb +11 -0
  112. data/spec/dummy/db/migrate/20150414213154_add_user_authentication_token.rb +11 -0
  113. data/spec/dummy/db/migrate/20150415222005_create_roles.rb +12 -0
  114. data/spec/dummy/db/migrate/20150505181635_create_chats.rb +9 -0
  115. data/spec/dummy/db/migrate/20150505181636_create_chat_users.rb +11 -0
  116. data/spec/dummy/db/migrate/20150505181640_create_chat_messages.rb +11 -0
  117. data/spec/dummy/db/migrate/20150507191529_create_chat_message_users.rb +11 -0
  118. data/spec/dummy/db/migrate/20150601200526_create_locations.rb +12 -0
  119. data/spec/dummy/db/migrate/20150601200533_create_locatables.rb +10 -0
  120. data/spec/dummy/db/migrate/20150601212924_create_location_beacons.rb +15 -0
  121. data/spec/dummy/db/migrate/20150601213542_create_location_gps.rb +12 -0
  122. data/spec/dummy/db/migrate/20150609201823_create_user_locations.rb +14 -0
  123. data/spec/dummy/db/migrate/20150616205336_add_role_user_constraint.rb +9 -0
  124. data/spec/dummy/db/migrate/20150617232519_create_projects.rb +10 -0
  125. data/spec/dummy/db/migrate/20150617232521_create_jobs.rb +9 -0
  126. data/spec/dummy/db/migrate/20150617232522_create_project_jobs.rb +11 -0
  127. data/spec/dummy/db/migrate/20150623170133_create_user_project_jobs.rb +12 -0
  128. data/spec/dummy/db/migrate/20150701234929_create_teams.rb +11 -0
  129. data/spec/dummy/db/migrate/20150701234930_create_team_users.rb +11 -0
  130. data/spec/dummy/db/migrate/20150727214950_add_confirmable_to_devise.rb +11 -0
  131. data/spec/dummy/db/migrate/20150820190524_add_user_names.rb +6 -0
  132. data/spec/dummy/db/migrate/20150824215701_create_images.rb +15 -0
  133. data/spec/dummy/db/migrate/20150909225019_add_password_to_project.rb +5 -0
  134. data/spec/dummy/db/schema.rb +278 -0
  135. data/spec/dummy/lib/assets/.keep +0 -0
  136. data/spec/dummy/log/.keep +0 -0
  137. data/spec/dummy/public/404.html +67 -0
  138. data/spec/dummy/public/422.html +67 -0
  139. data/spec/dummy/public/500.html +66 -0
  140. data/spec/dummy/public/favicon.ico +0 -0
  141. data/spec/fixtures/images/avatar.jpeg +0 -0
  142. data/spec/fixtures/images/exif.jpeg +0 -0
  143. data/spec/models/chat_spec.rb +32 -0
  144. data/spec/models/image_spec.rb +14 -0
  145. data/spec/models/locatable_spec.rb +10 -0
  146. data/spec/models/project_spec.rb +17 -0
  147. data/spec/models/role_spec.rb +63 -0
  148. data/spec/models/team_spec.rb +17 -0
  149. data/spec/models/team_user_spec.rb +20 -0
  150. data/spec/models/user_location_spec.rb +35 -0
  151. data/spec/models/user_project_job_spec.rb +30 -0
  152. data/spec/models/user_spec.rb +125 -0
  153. data/spec/rails_helper.rb +23 -0
  154. data/spec/requests/chat_api_spec.rb +174 -0
  155. data/spec/requests/company_api_spec.rb +61 -0
  156. data/spec/requests/location_api_spec.rb +96 -0
  157. data/spec/requests/project_api_spec.rb +151 -0
  158. data/spec/requests/role_api_spec.rb +37 -0
  159. data/spec/requests/sessions_api_spec.rb +55 -0
  160. data/spec/requests/user_api_spec.rb +191 -0
  161. data/spec/support/blueprints.rb +103 -0
  162. data/spec/support/location_helper.rb +56 -0
  163. data/spec/support/pundit_helpers.rb +13 -0
  164. data/spec/support/request_helpers.rb +22 -0
  165. metadata +562 -0
@@ -0,0 +1,108 @@
1
+ class Dummy::ChatAPI < DummyAPI
2
+ Chat.default_includes :chat
3
+
4
+ before do
5
+ authorize!
6
+ end
7
+
8
+ resource :chats do
9
+
10
+ desc "list the current user's existing chats"
11
+ get '/' do
12
+ authorize Chat.new, :index?
13
+ present current_user.chats.includes(:users), with: ChatEntity
14
+ end
15
+
16
+ desc "get new chat notifications"
17
+ params {
18
+ optional :id, type: Integer, desc: "Chat ID"
19
+ }
20
+ get '/notifications' do
21
+ authorize Chat.new, :show?
22
+ present current_user.new_messages?(params[:id])
23
+ end
24
+
25
+
26
+ desc "get messages"
27
+ params {
28
+ optional :id, type: Integer, desc: "Chat ID"
29
+ optional :new, type: Boolean, desc: "Get only new messages"
30
+ optional :mark_as_read, type: Boolean, desc: "Mark new messages as read"
31
+ }
32
+ get '/messages' do
33
+ authorize Chat.new, :show?
34
+ present current_user.read_messages(params[:id], mark_as_read: params[:mark_as_read], new: params[:new]), with: MessageEntity
35
+ end
36
+
37
+
38
+ desc "list the users in a chat"
39
+ params {
40
+ requires :id, type: Integer, desc: "Chat ID"
41
+ }
42
+ get '/users' do
43
+ authorize Chat.new, :show?
44
+ present current_user.chats.find(params[:id]).active_users, with: UserEntity
45
+ end
46
+
47
+
48
+ desc "add user(s) to a chat"
49
+ params {
50
+ requires :id, type: Integer, desc: "Chat ID"
51
+ requires :user_ids, desc: 'Comma separated list of User IDs', type: String, regexp: /^\d+(,\d+)*$/
52
+ }
53
+ post '/users' do
54
+ authorize Chat.new, :create?
55
+ user_ids = params[:user_ids].split(',')
56
+ present status: current_user.add_chatters(params[:id], user_ids)
57
+ end
58
+
59
+
60
+ desc "start a new chat"
61
+ params {
62
+ requires :message, type: String
63
+ requires :user_ids, desc: 'Comma separated list of User IDs', type: String, regexp: /^\d+(,\d+)*$/
64
+ }
65
+ post do
66
+ authorize Chat.new, :create?
67
+ user_ids = params[:user_ids].split(',')
68
+ present current_user.chat(user_ids, params[:message])
69
+ end
70
+
71
+
72
+ desc "reply to a chat"
73
+ params {
74
+ requires :id, type: Integer, desc: "Chat ID"
75
+ requires :message, type: String
76
+ }
77
+ put ':id' do
78
+ authorize Chat.new, :create?
79
+ current_user.reply(params[:id], params[:message])
80
+ end
81
+
82
+
83
+ desc "drop out of a chat"
84
+ params {
85
+ requires :id, type: Integer, desc: "Chat ID"
86
+ }
87
+ delete ':id' do
88
+ authorize Chat.new, :create?
89
+ present status: current_user.leave_chat(params[:id])
90
+ end
91
+ end
92
+
93
+
94
+ class UserEntity < Grape::Entity
95
+ expose :id, :name, :email, :avatar_url
96
+ end
97
+
98
+ class ChatEntity < Grape::Entity
99
+ expose :id, :creator_id
100
+ expose :users, using: UserEntity
101
+ end
102
+
103
+ class MessageEntity < Grape::Entity
104
+ expose :id, :chat_id, :message
105
+ expose :author, using: UserEntity
106
+ end
107
+
108
+ end
@@ -0,0 +1,8 @@
1
+ class Dummy::CompanyAPI < IntrospectiveGrape::API
2
+ restful Company
3
+
4
+ class CompanyEntity < Grape::Entity
5
+ expose :id, :name, :short_name, :created_at, :updated_at
6
+ end
7
+ end
8
+
@@ -0,0 +1,25 @@
1
+ # Grape Entity monkey patch to ensure that all keys are exposed as camelCased instead of the actual Ruby snake_case names.
2
+ # Note that the keys are not touched if the :as option is used, so
3
+ # expose :some_name, :as => 'some_name'
4
+ # will remain snake_cased
5
+
6
+ #class Grape::Entity
7
+ # protected
8
+ # def self.key_for(attribute)
9
+ # (exposures[attribute.to_sym][:as] || attribute).to_s.camelize(:lower).to_sym
10
+ # end
11
+ #end
12
+
13
+ module Dummy::Entities
14
+
15
+ # base class for entities
16
+ class DummyEntity < Grape::Entity
17
+ # common formatters can go here
18
+ end
19
+
20
+ # User entities
21
+ class User < DummyEntity
22
+ expose :id, :email, :first_name, :last_name, :avatar_url, :authentication_token
23
+ end
24
+ end
25
+
@@ -0,0 +1,37 @@
1
+ class Dummy::LocationAPI < IntrospectiveGrape::API
2
+
3
+ default_includes Location, :child_locations, :gps, :beacons, :locatables
4
+
5
+ exclude_actions LocationBeacon, :show,:create,:update,:destroy
6
+ exclude_actions LocationGps, :show,:create,:update,:destroy
7
+
8
+ restful Location, [:name, :kind,
9
+ {gps_attributes: [:id, :lat, :lng, :alt, :_destroy]},
10
+ {beacons_attributes: [:id, :company_id, :mac_address, :uuid, :major, :minor, :_destroy]},
11
+ ]
12
+
13
+ class Locatable < Grape::Entity
14
+ expose :id, :locatable_id, :locatable_type, :updated_at, :created_at
15
+ end
16
+
17
+ class LocationBeaconEntity < Grape::Entity
18
+ expose :id, :uuid, :major, :minor, :company_id, :mac_address, :created_at
19
+ end
20
+
21
+ class LocationGpsEntity < Grape::Entity
22
+ expose :id, :lat, :lng, :alt, :updated_at
23
+ end
24
+
25
+ class ChildLocationEntity < Grape::Entity
26
+ expose :id, :name, :kind, :created_at, :updated_at
27
+ end
28
+
29
+ class LocationEntity < Grape::Entity
30
+ expose :id, :name, :kind, :parent_location_id, :created_at, :updated_at
31
+ expose :locatables, using: Locatable
32
+ expose :child_locations, using: ChildLocationEntity
33
+ expose :gps, using: LocationGpsEntity
34
+ expose :beacons, using: LocationBeaconEntity
35
+ end
36
+ end
37
+
@@ -0,0 +1,51 @@
1
+ class Dummy::ProjectAPI < IntrospectiveGrape::API
2
+
3
+ default_includes Project, :owner, :admins, :user_project_jobs, project_jobs: [:job], teams: [:team_users]
4
+ default_includes Team, :team_users
5
+ default_includes TeamUser, user: [:projects], team: [:project]
6
+ default_includes ProjectJob, :job
7
+
8
+ exclude_actions Project, :create,:destroy,:update
9
+ exclude_actions Team, :show
10
+ exclude_actions TeamUser, :show,:update
11
+
12
+
13
+ restful Project, [:id, teams_attributes: [:id,:name,:_destroy, team_users_attributes: [:id, :user_id, :_destroy] ]]
14
+
15
+ class AdminEntity < Grape::Entity
16
+ expose :id, as: :user_id
17
+ expose :email, :name, :avatar_url
18
+ end
19
+
20
+ class UserJobEntity < Grape::Entity
21
+ expose :user_id, :name, :email, :avatar_url, :title, :created_at
22
+ end
23
+
24
+ class JobEntity < Grape::Entity
25
+ expose :title
26
+ end
27
+
28
+ class UserEntity < Grape::Entity
29
+ expose :id, :name, :avatar_url
30
+ end
31
+
32
+ class TeamUserEntity < Grape::Entity
33
+ expose :id
34
+ expose :user, using: UserEntity
35
+ end
36
+
37
+ class TeamEntity < Grape::Entity
38
+ expose :id, :name, :created_at, :updated_at
39
+ expose :team_users, using: TeamUserEntity
40
+ end
41
+
42
+ class ProjectEntity < Grape::Entity
43
+ expose :id, :name, :created_at, :updated_at
44
+ expose :owner, using: Dummy::CompanyAPI::CompanyEntity
45
+ expose :admins, using: AdminEntity
46
+ expose :project_jobs, as: :jobs, using: JobEntity
47
+ expose :user_project_jobs, as: :user_jobs, using: UserJobEntity
48
+ expose :teams, using: TeamEntity
49
+ end
50
+ end
51
+
@@ -0,0 +1,7 @@
1
+ class Dummy::RoleAPI < IntrospectiveGrape::API
2
+ restful Role
3
+
4
+ class RoleEntity < Grape::Entity
5
+ expose :id, :email, :ownable_type, :ownable_id, :created_at, :updated_at
6
+ end
7
+ end
@@ -0,0 +1,55 @@
1
+ class Dummy::Sessions < Grape::API
2
+
3
+ resource :sessions do
4
+
5
+ desc "create a user session" do
6
+ detail "sign in a user"
7
+ end
8
+ params do
9
+ requires :login, type: String, desc: "email address"
10
+ requires :password, type: String, desc: "password"
11
+ optional :token, type: Boolean, desc: "set to true to generate and return Firebase secure token", default: false
12
+ end
13
+ post '/' do
14
+ authorize User.new, :sessions?
15
+ user = User.find_first_by_auth_conditions({email: params[:login]})
16
+ if user && user.valid_password?(params[:password]) && user.valid_for_authentication?
17
+
18
+ # commented out for now, User model is not yet confirmable
19
+ #unauthorized! DummyAPI::USER_NOT_CONFIRMED unless user.confirmed?
20
+
21
+ token = nil
22
+ if params[:token]
23
+ payload = {
24
+ uid: "#{user.id}", # uid must be a string
25
+ email: user.email,
26
+ avatar_url: user.avatar_url
27
+ }
28
+ user.authentication_token = SecureRandom.urlsafe_base64(nil, false)
29
+ user.save
30
+ end
31
+
32
+ #user.ensure_authentication_token!
33
+ env['warden'].set_user(user, scope: :user)
34
+ present user, with: Dummy::Entities::User, token: token
35
+ else
36
+ unauthorized! DummyAPI::BAD_LOGIN
37
+ end
38
+ end
39
+
40
+
41
+ desc "delete a user session" do
42
+ detail "sign out the current user"
43
+ end
44
+ delete '/' do
45
+ authorize User.new, :sessions?
46
+ if u = User.find_by_authentication_token(params[:api_key])
47
+ u.authentication_token = nil
48
+ {status: u.save!}
49
+ else
50
+ {status: true } # the user is already logged out
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ class Dummy::UserAPI < IntrospectiveGrape::API
2
+
3
+ skip_presence_validations :password
4
+
5
+ exclude_actions Role, :show,:update
6
+ exclude_actions UserProjectJob, :show,:update
7
+
8
+ restful User, [:id, :email, :password, :first_name, :last_name, :skip_confirmation_email,
9
+ user_project_jobs_attributes: [:id, :job_id, :project_id, :_destroy],
10
+ roles_attributes: [:id, :ownable_type, :ownable_id, :_destroy],
11
+ avatar_attributes: [:id, :file, :_destroy]
12
+ ]
13
+
14
+ class RoleEntity < Grape::Entity
15
+ expose :id, :ownable_type, :ownable_id
16
+ end
17
+
18
+ class ImageEntity < Grape::Entity
19
+ expose :id, :file_processing #, 'file.url'
20
+ end
21
+
22
+ class UserProjectJobEntity < Grape::Entity
23
+ expose :id, :name, :title, :job_id, :project_id
24
+ end
25
+
26
+ class UserEntity < Grape::Entity
27
+ expose :id, :email, :first_name, :last_name, :avatar_url
28
+ expose :roles, as: :roles_attributes, using: RoleEntity
29
+ expose :user_project_jobs, as: :user_project_jobs_attributes, using: UserProjectJobEntity
30
+ end
31
+
32
+ end
@@ -0,0 +1,57 @@
1
+ #require 'grape-swagger'
2
+ #require 'grape-entity'
3
+ require 'active_record_helpers'
4
+ #require 'introspective_grape/camel_snake'
5
+
6
+ class DummyAPI < Grape::API
7
+ version 'v1', using: :path
8
+ format :json
9
+ default_format :json
10
+
11
+ include ErrorHandlers
12
+ helpers PermissionsHelper
13
+ helpers ApiHelpers
14
+
15
+ USER_NOT_CONFIRMED = 'user_not_confirmed' # "Your account must be confirmed, please check your email inbox."
16
+ BAD_LOGIN = 'bad_login' # incorrect username or password'
17
+
18
+ before do
19
+ # sets server date in response header. This can be used on the client side
20
+ header "X-Server-Date", "#{Time.now.to_i}"
21
+ header "Expires", 1.year.ago.httpdate
22
+ # Convert incoming camel case params to snake case: grape will totally blow this
23
+ # if the params hash is not a Hashie::Mash, so make it one of those:
24
+ #@params = Hashie::Mash.new(snake_keys(params))
25
+ end
26
+
27
+ before_validation do
28
+ Rails.logger.info "With params: #{params.to_hash.inspect}"
29
+ end
30
+
31
+ after do
32
+ unless self.options[:path].first =~ /swagger/
33
+ verify_authorized # Ensure that all endpoints are authorized by a policy class
34
+ end
35
+ end
36
+
37
+ # Load the in-memory database for the test app
38
+ load "#{Rails.root}/db/schema.rb"
39
+
40
+ # Mount every api endpoint under app/api/dummy/.
41
+ Dir.glob(Rails.root+"app"+"api"+'dummy'+'*.rb').each do |f|
42
+ api = "Dummy::#{File.basename(f, '.rb').camelize.sub(/Api$/,'API')}".constantize
43
+ mount api if api.respond_to? :endpoints
44
+ end
45
+
46
+ # configure grape-swagger to auto-generate swagger docs
47
+ protocol = Rails.application.config.force_ssl ? 'https' : 'http'
48
+ add_swagger_documentation({
49
+ base_path: "#{protocol}://localhost:3000/api",
50
+ api_version: 'v1',
51
+ hide_documentation_path: true,
52
+ format: :json,
53
+ hide_format: true
54
+ #markdown: true
55
+ })
56
+
57
+ end
@@ -0,0 +1,28 @@
1
+ require 'pundit'
2
+ module ErrorHandlers
3
+ def self.included(m)
4
+ m.rescue_from ActiveRecord::RecordInvalid do |e|
5
+ error_response message: e.record.errors.to_a.uniq.join(', '), status: 400
6
+ end
7
+
8
+ m.rescue_from Grape::Exceptions::ValidationErrors do |e|
9
+ error_response message: e.message, status: 400
10
+ end
11
+
12
+ m.rescue_from ActiveRecord::RecordNotFound do |e|
13
+ error_response message: "Record not found! #{e.message}", status: 404
14
+ end
15
+
16
+ m.rescue_from ActiveRecord::InvalidForeignKey do |e|
17
+ error_response message: "Join record not found! #{e.message}", status: 404
18
+ end
19
+
20
+ m.rescue_from Pundit::NotAuthorizedError do |e|
21
+ error_response message: "Forbidden", status: 403
22
+ end
23
+
24
+ m.rescue_from Pundit::NotDefinedError do |e|
25
+ error_response message: "Policy not implemented", status: 501
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ require 'pundit'
2
+ module PermissionsHelper
3
+ # Pundit won't import it's methods unless it sees a stub of ActionController's hide_action.
4
+ def hide_action; end
5
+ include Pundit
6
+
7
+ end
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
File without changes
File without changes
@@ -0,0 +1,13 @@
1
+ class AbstractAdapter < ActiveRecord::Base
2
+ self.abstract_class = true
3
+
4
+ class << self
5
+ def human_attribute_name(attr, options = {})
6
+ # The default formatting of validation errors sucks, this helps a little syntatically:
7
+ super.titleize+":"
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+
@@ -0,0 +1,6 @@
1
+ class AdminUser < ActiveRecord::Base
2
+ # Include default devise modules. Others available are:
3
+ # :confirmable, :lockable, :timeoutable and :omniauthable
4
+ devise :database_authenticatable,
5
+ :recoverable, :rememberable, :trackable, :validatable
6
+ end
@@ -0,0 +1,18 @@
1
+ class Chat < AbstractAdapter
2
+ belongs_to :creator, foreign_key: :creator_id, :class_name => "User", inverse_of: :own_chats
3
+
4
+ has_many :chat_users, dependent: :destroy
5
+ has_many :users, through: :chat_users
6
+ has_many :chat_messages, dependent: :destroy
7
+ has_many :messages, class_name: 'ChatMessage', dependent: :destroy
8
+
9
+ def active_users
10
+ chat_users.includes(:user).select {|cu| cu.departed_at.nil? }.map(&:user)
11
+ end
12
+
13
+ before_create :add_creator_to_conversation
14
+ def add_creator_to_conversation
15
+ users.push creator
16
+ end
17
+
18
+ end
@@ -0,0 +1,34 @@
1
+ class ChatMessage < AbstractAdapter
2
+ belongs_to :chat
3
+ belongs_to :author, class_name: 'User'
4
+
5
+ has_many :chat_users, through: :chat
6
+ has_many :recipients, lambda {|message| where(':created_at >= chat_users.created_at and (chat_users.departed_at IS NULL OR :created_at <= chat_users.departed_at)', created_at: message.created_at ) }, through: :chat_users, source: :user, class_name: 'User'
7
+
8
+ # Create ChatUserMessage records for each recipient to track read status
9
+ has_many :chat_message_users, dependent: :destroy
10
+
11
+ validate :author_in_chat
12
+
13
+ def author_in_chat
14
+ errors[:base] << 'User not in chat session.' unless chat.active_users.include? author
15
+ end
16
+
17
+ before_save :create_message_users, if: :new_record?
18
+ def create_message_users
19
+ chat_users.merge(ChatUser.current).each do |cu|
20
+ chat_message_users.build(user: cu.user)
21
+ end
22
+ end
23
+
24
+ def read_by?(user)
25
+ chat_message_users.merge(ChatMessageUser.read).map(&:user_id).include?(user.id)
26
+ end
27
+
28
+ def self.find_chat_for_users(users)
29
+ # presumably much more efficient ways to run an intersecton, we want to find the last
30
+ # exact match with the users being messaged to append to the existing chat.
31
+ Chat.eager_load(:chat_users).where("chat_users.departed_at IS NULL").order('chats.created_at desc').detect {|c| c.chat_users.map(&:user_id).uniq.sort == users.map(&:id).sort }
32
+ end
33
+
34
+ end
@@ -0,0 +1,17 @@
1
+ class ChatMessageUser < AbstractAdapter
2
+ belongs_to :chat_message
3
+ belongs_to :user
4
+ has_one :chat, through: :chat_message
5
+
6
+ scope :read, ->{ where('read_at IS NOT NULL' ) }
7
+ scope :unread, ->{ where('read_at IS NULL' ) }
8
+
9
+ before_save :author_reads_message
10
+ def author_reads_message
11
+ self.read_at = Time.now if user == chat_message.author
12
+ end
13
+
14
+ def read?
15
+ read_at.present?
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ class ChatUser < AbstractAdapter
2
+ belongs_to :chat
3
+ belongs_to :user
4
+
5
+ alias_attribute :joined_at, :created_at
6
+ alias_attribute :left_at, :departed_at
7
+
8
+ scope :current, ->{ where(departed_at: nil) }
9
+
10
+ validate :user_not_already_active, on: :create
11
+
12
+ def user_not_already_active
13
+ errors[:base] << "#{user.name} is already present in this chat." if chat.chat_users.where(user_id: user.id, departed_at: nil).count > 0 if user.persisted?
14
+ end
15
+
16
+ end
@@ -0,0 +1,14 @@
1
+ class Company < AbstractAdapter
2
+ has_many :roles, as: :ownable
3
+ has_many :admins, through: :roles, source: :user
4
+ accepts_nested_attributes_for :roles, allow_destroy: true
5
+
6
+ has_many :beacons, class_name: 'LocationBeacon', dependent: :destroy
7
+ has_many :locatables
8
+ has_many :locations, through: :locatables, source: :locatable, source_type: 'Company'
9
+
10
+ has_many :projects, foreign_key: :owner_id, dependent: :destroy, inverse_of: :owner
11
+
12
+ validates_length_of :name, maximum: 256
13
+ validates_length_of :short_name, maximum: 10
14
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ require 'paperclip'
2
+ #require 'delayed_paperclip'
3
+ class Image < ActiveRecord::Base
4
+ belongs_to :imageable, polymorphic: true
5
+
6
+ has_attached_file :file, styles: {medium: "300x300>", thumb: "100x100"},
7
+ url: "/system/:imageable_type/:imageable_id/:id/:style/:filename"
8
+
9
+ validates_attachment :file, content_type: {content_type: ["image/jpeg", "image/png", "image/gif"]}
10
+ validates_attachment_size :file, :less_than => 2.megabytes
11
+
12
+ #process_in_background :file, processing_image_url: 'empty_avatar.png'
13
+
14
+ Paperclip.interpolates :imageable_type do |attachment, style|
15
+ attachment.instance.imageable_type.try(:pluralize)
16
+ end
17
+ Paperclip.interpolates :imageable_id do |attachment, style|
18
+ attachment.instance.imageable_id
19
+ end
20
+
21
+ end
@@ -0,0 +1,10 @@
1
+ class Job < AbstractAdapter
2
+
3
+ has_many :project_jobs, dependent: :destroy
4
+ accepts_nested_attributes_for :project_jobs, allow_destroy: true
5
+
6
+ has_many :user_project_jobs, dependent: :destroy
7
+ has_many :users, through: :user_project_jobs
8
+ has_many :projects, through: :user_project_jobs
9
+
10
+ end
@@ -0,0 +1,6 @@
1
+ class Locatable < AbstractAdapter
2
+ belongs_to :location
3
+ belongs_to :locatable, polymorphic: true
4
+
5
+ validates_inclusion_of :locatable_type, in: %w(Company)
6
+ end