introspective_admin 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +279 -0
- data/LICENSE +22 -0
- data/MIT-LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +26 -0
- data/bin/rails +12 -0
- data/introspective_admin.gemspec +38 -0
- data/lib/introspective_admin/base.rb +170 -0
- data/lib/introspective_admin/version.rb +3 -0
- data/lib/introspective_admin.rb +4 -0
- data/lib/tasks/introspective_admin_tasks.rake +4 -0
- data/spec/admin/company_admin_spec.rb +72 -0
- data/spec/admin/job_admin_spec.rb +61 -0
- data/spec/admin/location_admin_spec.rb +65 -0
- data/spec/admin/project__admin_spec.rb +71 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/admin/company_admin.rb +4 -0
- data/spec/dummy/app/admin/job_admin.rb +4 -0
- data/spec/dummy/app/admin/location_admin.rb +4 -0
- data/spec/dummy/app/admin/project_admin.rb +6 -0
- data/spec/dummy/app/admin/role_admin.rb +5 -0
- data/spec/dummy/app/admin/user_admin.rb +9 -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 +12 -0
- data/spec/dummy/app/models/concerns/.keep +0 -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 +64 -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 +25 -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 +75 -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/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 +32 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +18 -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/active_admin.rb +7 -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 +260 -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/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +9 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -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/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/20150909225019_add_password_to_project.rb +5 -0
- data/spec/dummy/db/schema.rb +261 -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/rails_helper.rb +13 -0
- data/spec/support/blueprints.rb +119 -0
- data/spec/support/location_helper.rb +56 -0
- metadata +420 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Admin::CompaniesController, :type => :controller do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
user = double('user')
|
8
|
+
allow(request.env['warden']).to receive(:authenticate!) { user }
|
9
|
+
allow(controller).to receive(:current_user) { user }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "GET index" do
|
13
|
+
it "finds all companies" do
|
14
|
+
c = Company.make!
|
15
|
+
get :index
|
16
|
+
response.status.should == 200
|
17
|
+
assigns(:companies).include?(c).should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "SHOW record" do
|
22
|
+
it "finds the record" do
|
23
|
+
c = Company.make!
|
24
|
+
get :show, id: c.id
|
25
|
+
response.status.should == 200
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "NEW record" do
|
30
|
+
it "renders the form for a new record" do
|
31
|
+
get :new
|
32
|
+
response.status.should == 200
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "CREATE record" do
|
37
|
+
it "creates the record" do
|
38
|
+
c = Company.make
|
39
|
+
post :create, company: c.attributes
|
40
|
+
response.should redirect_to action: :show, id: Company.last.id
|
41
|
+
Company.last.name.should == c.name
|
42
|
+
end
|
43
|
+
|
44
|
+
it "creates a record with an admin" do
|
45
|
+
u = User.make!
|
46
|
+
c = Company.make
|
47
|
+
post :create, company: c.attributes.merge({
|
48
|
+
roles_attributes: {'0'=>{ user_id: u.id }}
|
49
|
+
})
|
50
|
+
response.should redirect_to action: :show, id: Company.last.id
|
51
|
+
Company.last.admins.include?(u).should be_truthy
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "EDIT record" do
|
57
|
+
it "renders the edit form for an existing record" do
|
58
|
+
r = Company.make!
|
59
|
+
get :edit, id: r.id
|
60
|
+
response.status.should == 200
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "UPDATE record" do
|
65
|
+
it "updates the record" do
|
66
|
+
c = Company.make!
|
67
|
+
put :update, id: c.id, company: { name: "New Name" }
|
68
|
+
response.should redirect_to action: :show, id: c.id
|
69
|
+
Company.find(c.id).name.should == "New Name"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Admin::JobsController, :type => :controller do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
before :each do # Why can't I do this shit in a helper like I do for requests?
|
7
|
+
user = double('user')
|
8
|
+
allow(request.env['warden']).to receive(:authenticate!) { user }
|
9
|
+
allow(controller).to receive(:current_user) { user }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "GET index" do
|
13
|
+
it "finds all Jobs" do
|
14
|
+
r = Job.make!
|
15
|
+
get :index
|
16
|
+
response.status.should == 200
|
17
|
+
assigns(:jobs).include?(r).should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "SHOW record" do
|
22
|
+
it "finds the record" do
|
23
|
+
r = Job.make!
|
24
|
+
get :show, id: r.id
|
25
|
+
response.status.should == 200
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "NEW record" do
|
30
|
+
it "renders the form for a new record" do
|
31
|
+
get :new
|
32
|
+
response.status.should == 200
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "CREATE record" do
|
37
|
+
it "creates the record" do
|
38
|
+
r = Job.make
|
39
|
+
post :create, job: r.attributes
|
40
|
+
response.should redirect_to action: :show, id: Job.last.id
|
41
|
+
Job.last.title.should == r.title
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "EDIT record" do
|
46
|
+
it "renders the edit form for an existing record" do
|
47
|
+
r = Job.make!
|
48
|
+
get :edit, id: r.id
|
49
|
+
response.status.should == 200
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "UPDATE record" do
|
54
|
+
it "updates the record" do
|
55
|
+
r = Job.make!
|
56
|
+
put :update, id: r.id, job: { title: "New Name" }
|
57
|
+
response.should redirect_to action: :show, id: r.id
|
58
|
+
Job.find(r.id).title.should == "New Name"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Admin::LocationsController, :type => :controller do
|
4
|
+
render_views
|
5
|
+
|
6
|
+
before :each do # Why can't I do this shit in a helper like I do for requests?
|
7
|
+
user = double('user')
|
8
|
+
allow(request.env['warden']).to receive(:authenticate!) { user }
|
9
|
+
allow(controller).to receive(:current_user) { user }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "GET index" do
|
13
|
+
it "finds all locations" do
|
14
|
+
c = Location.make!
|
15
|
+
get :index
|
16
|
+
response.status.should == 200
|
17
|
+
assigns(:locations).include?(c).should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "SHOW record" do
|
22
|
+
it "finds the record" do
|
23
|
+
c = Location.make!
|
24
|
+
get :show, id: c.id
|
25
|
+
response.status.should == 200
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "NEW record" do
|
30
|
+
# will fail until https://github.com/activeadmin/activeadmin/pull/4010 is merged
|
31
|
+
it "renders the form for a new record" do
|
32
|
+
get :new
|
33
|
+
response.status.should == 200
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "CREATE record" do
|
38
|
+
it "creates the record" do
|
39
|
+
c = Location.make
|
40
|
+
gps = LocationGps.make
|
41
|
+
post :create, location: c.attributes.merge(gps_attributes: gps.attributes)
|
42
|
+
response.should redirect_to action: :show, id: Location.last.id
|
43
|
+
Location.last.name.should == c.name
|
44
|
+
Location.last.gps.lat.round(10).should == gps.lat.round(10)
|
45
|
+
Location.last.gps.lng.round(10).should == gps.lng.round(10)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "EDIT record" do
|
50
|
+
it "renders the edit form for an existing record" do
|
51
|
+
r = Location.make!
|
52
|
+
get :edit, id: r.id
|
53
|
+
response.status.should == 200
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "UPDATE record" do
|
58
|
+
it "updates the record" do
|
59
|
+
r = Location.make!
|
60
|
+
put :update, id: r.id, location: { name: "New Name" }
|
61
|
+
response.should redirect_to action: :show, id: r.id
|
62
|
+
Location.find(r.id).name.should == "New Name"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
RSpec.describe Admin::ProjectsController, :type => :controller do
|
3
|
+
render_views
|
4
|
+
|
5
|
+
before :each do # Why can't I do this shit in a helper like I do for requests?
|
6
|
+
user = double('user')
|
7
|
+
allow(request.env['warden']).to receive(:authenticate!) { user }
|
8
|
+
allow(controller).to receive(:current_user) { user }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "GET index" do
|
12
|
+
it "finds all projects" do
|
13
|
+
r = Project.make!
|
14
|
+
get :index
|
15
|
+
response.status.should == 200
|
16
|
+
assigns(:projects).include?(r).should == true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "SHOW record" do
|
21
|
+
it "finds the record" do
|
22
|
+
r = Project.make!
|
23
|
+
get :show, id: r.id
|
24
|
+
response.status.should == 200
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "NEW record" do
|
29
|
+
it "renders the form for a new record" do
|
30
|
+
get :new
|
31
|
+
response.status.should == 200
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "CREATE record" do
|
36
|
+
it "creates the record" do
|
37
|
+
r = Project.make
|
38
|
+
post :create, project: r.attributes
|
39
|
+
response.should redirect_to action: :show, id: Project.last.id
|
40
|
+
Project.last.name.should == r.name
|
41
|
+
end
|
42
|
+
|
43
|
+
it "the inverse_of declaration allows a new project to be created with a project_job" do
|
44
|
+
j = Job.make!
|
45
|
+
r = Project.make
|
46
|
+
post :create, project: r.attributes.merge({project_jobs_attributes:{'0'=>{job_id: j.id}}})
|
47
|
+
p = Project.last
|
48
|
+
p.name.should == r.name
|
49
|
+
p.project_jobs.size.should == 1
|
50
|
+
p.project_jobs.first.job.should == j
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "EDIT record" do
|
56
|
+
it "renders the edit form for an existing record" do
|
57
|
+
r = Project.make!
|
58
|
+
get :edit, id: r.id
|
59
|
+
response.status.should == 200
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "UPDATE record" do
|
64
|
+
it "updates the record" do
|
65
|
+
r = Project.make!
|
66
|
+
put :update, id: r.id, project: { name: "New Name" }
|
67
|
+
response.should redirect_to action: :show, id: r.id
|
68
|
+
Project.find(r.id).name.should == "New Name"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
class UserAdmin < IntrospectiveAdmin::Base
|
2
|
+
def self.exclude_params
|
3
|
+
%w(reset_password_at current_sign_in_at current_sign_in_ip remember_created_at sign_in_count encrypted_password reset_password_sent_at reset_password_token password authentication_token unlock_token failed_attempts last_sign_in_at locked_at last_sign_in_ip)
|
4
|
+
end
|
5
|
+
|
6
|
+
register User do
|
7
|
+
end
|
8
|
+
|
9
|
+
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
|
+
*/
|
File without changes
|
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,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,12 @@
|
|
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
|
+
end
|
File without changes
|
@@ -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,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,64 @@
|
|
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
|
+
class << self
|
15
|
+
def bounding_box(lat,lng,alt)
|
16
|
+
# box constrain the nearest neighbor search to something reasonble
|
17
|
+
box = 0.1447 # 10 miles radius: 111132 meters/deg, 1609m a mile, 0.01447 deg to a mile
|
18
|
+
alt_box = 8 # we're trying to constrain the box to a floor of a building here...
|
19
|
+
["lat BETWEEN :lat-#{box} AND :lat+#{box} AND
|
20
|
+
lng BETWEEN :lng-#{box} AND :lng+#{box} AND
|
21
|
+
alt BETWEEN :alt-#{alt_box} AND :alt+#{alt_box}",
|
22
|
+
{ lat: lat, lng: lng, alt: alt } ]
|
23
|
+
end
|
24
|
+
|
25
|
+
def nearest_beacon(lat, lng, alt=0, companies)
|
26
|
+
company_ids = companies.kind_of?(Array) ? companies : [companies]
|
27
|
+
|
28
|
+
gps = nearest_approximation(lat,lng,alt).joins(:beacons).where("location_beacons.company_id"=>company_ids).first || ( raise ActiveRecord::RecordNotFound.new("Couldn't find a Beacon for that GPS Location.") )
|
29
|
+
# It's not clear, if they're doing this across multiple companies, how which beacon
|
30
|
+
# comes back is not arbitrary, so maybe we should require it be specific.
|
31
|
+
gps.beacons.where(company_id: company_ids).first
|
32
|
+
end
|
33
|
+
|
34
|
+
def nearest(lat,lng,alt=0)
|
35
|
+
nearest_approximation(lat,lng,alt).first || ( raise ActiveRecord::RecordNotFound.new("Couldn't find GPS Location.") )
|
36
|
+
end
|
37
|
+
|
38
|
+
def nearest_approximation(lat,lng,alt=0) # approximate as a flat-ish plane
|
39
|
+
dOrder= sanitize_sql_array(["(lat - ?)^2 + (lng - ?)^2 * COS(RADIANS(lat))", lat, lng])
|
40
|
+
|
41
|
+
self.select("location_gps.id, location_gps.location_id, #{dOrder} as dOrder").
|
42
|
+
where( bounding_box(lat,lng,alt) ).order("dOrder ASC")
|
43
|
+
end
|
44
|
+
|
45
|
+
def nearest_great_circle(lat,lng,alt=0)
|
46
|
+
dOrder = sanitize_sql_array(["acos( sin(radians(lat))*sin(radians(:lat)) + cos(radians(lat))*cos(radians(:lat))*cos(radians(lng - :lng)) )", { lat: lat, lng: lng } ] )
|
47
|
+
|
48
|
+
self.select("location_gps.id, location_gps.location_id, #{dOrder} as dOrder").
|
49
|
+
where( bounding_box(lat,lng,alt) ).order("dOrder ASC")
|
50
|
+
end
|
51
|
+
|
52
|
+
def nearest_haversine(lat,lng,alt=0)
|
53
|
+
dOrder = sanitize_sql_array(["asin( sqrt( (sin(radians(:lat - lat))/2)^2 + cos(radians(lat))*cos(radians(:lat))*(sin(radians(:lng - lng )/2))^2 ))", { lat: lat, lng: lng }])
|
54
|
+
|
55
|
+
self.select("location_gps.id, location_gps.location_id, #{dOrder} as dOrder").
|
56
|
+
where( bounding_box(lat,lng,alt) ).order("dOrder ASC")
|
57
|
+
end
|
58
|
+
|
59
|
+
#def nearest_wgs84(lat,lng,alt=0)
|
60
|
+
# # vincenty's ellipsoid calculation is an iterative estimate available in postGIS
|
61
|
+
#end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|