introspective_grape 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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