introspective_admin 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +34 -0
- data/README.md +49 -4
- data/introspective_admin.gemspec +8 -5
- data/lib/introspective_admin/base.rb +7 -0
- data/lib/introspective_admin/version.rb +1 -1
- data/spec/dummy/app/models/abstract_adapter.rb +0 -1
- data/spec/dummy/app/models/location_gps.rb +0 -53
- data/spec/dummy/app/models/user.rb +0 -7
- data/spec/dummy/config/database.yml +9 -5
- data/spec/rails_helper.rb +0 -2
- data/spec/support/blueprints.rb +0 -16
- metadata +12 -50
- data/spec/dummy/app/models/chat.rb +0 -18
- data/spec/dummy/app/models/chat_message.rb +0 -34
- data/spec/dummy/app/models/chat_message_user.rb +0 -17
- data/spec/dummy/app/models/chat_user.rb +0 -16
- data/spec/dummy/app/models/user/chatter.rb +0 -79
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31c04925094186ee8656917b8d3a0072df365a4d
|
4
|
+
data.tar.gz: f2e55a36107edededc44be81a4589d3f383bcbca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00d867fe289e8029f9364bcfcab98b7eca31154b06b4b2711c28e0e1b21dea49cd162acee0f82348b3320d5391bbe31e255bdb6b99cd29bacd3b74dcf13e2825
|
7
|
+
data.tar.gz: 321c9ba8b69d8abce6c719a8d73fcc8197491d46bb59974bf5c2c3f82338fc9b1f267a8c59fcff19906f03a00f57a61dabd8d54bd32828cf427231f7a2002414
|
data/.travis.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
language: ruby
|
2
|
+
install: bundle install --jobs=1 --retry=1
|
3
|
+
script:
|
4
|
+
- bundle install
|
5
|
+
- bundle exec rspec
|
6
|
+
rvm:
|
7
|
+
- 1.9
|
8
|
+
- 2.2.3
|
9
|
+
# - jruby-9.0.0.0
|
10
|
+
env:
|
11
|
+
matrix:
|
12
|
+
- RAILS=3.2.22
|
13
|
+
- RAILS=4.1.13
|
14
|
+
- RAILS=4.2.4
|
15
|
+
- RAILS=master
|
16
|
+
global:
|
17
|
+
- JRUBY_OPTS="-J-Xmx1024m --debug"
|
18
|
+
matrix:
|
19
|
+
fast_finish: true
|
20
|
+
exclude:
|
21
|
+
- rvm: 2.2.3
|
22
|
+
env: RAILS=3.2.22
|
23
|
+
- rvm: jruby-9.0.0.0
|
24
|
+
env: RAILS=3.2.22
|
25
|
+
- rvm: 1.9
|
26
|
+
env: RAILS=master
|
27
|
+
allow_failures:
|
28
|
+
- env: RAILS=master
|
29
|
+
notifications:
|
30
|
+
email:
|
31
|
+
recipients:
|
32
|
+
- buermann@gmail.com
|
33
|
+
on_success: change
|
34
|
+
on_failure: always
|
data/README.md
CHANGED
@@ -1,18 +1,34 @@
|
|
1
1
|
# IntrospectiveAdmin
|
2
2
|
|
3
|
+
[![Gem Version][GV img]][Gem Version]
|
4
|
+
[![Build Status][BS img]][Build Status]
|
5
|
+
[![Dependency Status][DS img]][Dependency Status]
|
6
|
+
[![Coverage Status][CS img]][Coverage Status]
|
7
|
+
|
8
|
+
[Gem Version]: https://rubygems.org/gems/introspective_admin
|
9
|
+
[Build Status]: https://travis-ci.org/buermann/introspective_admin
|
10
|
+
[travis pull requests]: https://travis-ci.org/buermann/introspective_admin/pull_requests
|
11
|
+
[Dependency Status]: https://gemnasium.com/buermann/introspective_admin
|
12
|
+
[Coverage Status]: https://coveralls.io/r/buermann/introspective_admin
|
13
|
+
|
14
|
+
[GV img]: https://badge.fury.io/rb/introspective_admin.png
|
15
|
+
[BS img]: https://travis-ci.org/buermann/introspective_admin.png
|
16
|
+
[DS img]: https://gemnasium.com/buermann/introspective_admin.png
|
17
|
+
[CS img]: https://coveralls.io/repos/buermann/introspective_admin/badge.png?branch=master
|
18
|
+
|
3
19
|
IntrospectiveAdmin is a Rails Plugin for DRYing up ActiveAdmin configurations by
|
4
20
|
laying out simple defaults and including nested relations according to the models'
|
5
|
-
accepts_nested_attributes_for :relation declarations.
|
21
|
+
accepts_nested_attributes_for :relation declarations.
|
6
22
|
|
7
23
|
## Documentation
|
8
24
|
|
9
25
|
In your Gemfile:
|
10
26
|
|
11
27
|
```
|
12
|
-
gem 'introspective_admin'
|
28
|
+
gem 'introspective_admin'
|
13
29
|
```
|
14
30
|
|
15
|
-
And bundle install.
|
31
|
+
And bundle install. In app/admin/my_admin.rb:
|
16
32
|
|
17
33
|
```
|
18
34
|
class MyAdmin < IntrospectiveAdmin::Base
|
@@ -21,11 +37,15 @@ class MyAdmin < IntrospectiveAdmin::Base
|
|
21
37
|
end
|
22
38
|
|
23
39
|
register MyModel do
|
24
|
-
#
|
40
|
+
# It yields the ActiveAdmin DSL context back, allowing further configuration to
|
41
|
+
# be added here, just as you would normally, to the Admin::MyModelController
|
42
|
+
# namespace.
|
25
43
|
end
|
26
44
|
end
|
27
45
|
```
|
28
46
|
|
47
|
+
Registering MyModel will set up the index, show, and form configurations for every attribute and nested association on the model excluding those in MyAdmin.exclude_params, with links to associated records (if they have ActiveAdmin screens) and permitting every attribute on the model.
|
48
|
+
|
29
49
|
Customizing select box options for associations is done by adding an
|
30
50
|
"options_for_X" class method on the administrated model:
|
31
51
|
|
@@ -41,6 +61,31 @@ class MyModel < ActiveRecord::Base
|
|
41
61
|
end
|
42
62
|
```
|
43
63
|
|
64
|
+
IntrospectiveAdmin will detect nested polymorphic relations and attempt to handle
|
65
|
+
them using virutal attributes that you must add to the model instance, plus a class
|
66
|
+
method for the select box options, using a shared delimiter string for the compound ID.
|
67
|
+
E.g. here we use a hyphen:
|
68
|
+
|
69
|
+
```
|
70
|
+
class MyModel < ActiveRecord::Base
|
71
|
+
belongs_to :poly_model, polymorphic: true
|
72
|
+
accepts_nested_attributes_for :poly_model, :allow_destroy => true
|
73
|
+
|
74
|
+
def self.options_for_poly_model
|
75
|
+
PolyModel.all.map { |i| [ "#{i.class}: #{i.name}", "#{i.class}-#{i.id}"] }
|
76
|
+
end
|
77
|
+
|
78
|
+
def poly_model_assign
|
79
|
+
poly_model.present? ? "#{poly_model_type}-#{poly_model_id}" : nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def poly_model_assign=(value)
|
83
|
+
self.poly_model_type,self.poly_model_id = value.split('-')
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
44
89
|
## Dependencies
|
45
90
|
|
46
91
|
Tool | Description
|
data/introspective_admin.gemspec
CHANGED
@@ -22,17 +22,20 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.add_dependency 'sass-rails'
|
24
24
|
|
25
|
-
|
25
|
+
if RUBY_PLATFORM == 'java'
|
26
|
+
s.add_development_dependency "jdbc-sqlite3"
|
27
|
+
else
|
28
|
+
s.add_development_dependency "sqlite3"
|
29
|
+
if RUBY_VERSION > '2.0.0'
|
30
|
+
s.add_development_dependency 'byebug'
|
31
|
+
end
|
32
|
+
end
|
26
33
|
s.add_development_dependency "rspec-rails", '>= 3.0'
|
27
34
|
s.add_development_dependency 'devise'
|
28
35
|
s.add_development_dependency 'devise-async'
|
29
36
|
s.add_development_dependency 'machinist'
|
30
37
|
s.add_development_dependency 'simplecov'
|
31
38
|
s.add_development_dependency 'rufus-mnemo'
|
32
|
-
# For compatibility of schema_validations with AR 4.2.1+
|
33
|
-
s.add_development_dependency "schema_plus", "2.0.0.pre12"
|
34
|
-
s.add_development_dependency "schema_validations"
|
35
|
-
s.add_development_dependency 'byebug'
|
36
39
|
|
37
40
|
end
|
38
41
|
|
@@ -71,7 +71,14 @@ module IntrospectiveAdmin
|
|
71
71
|
[assoc, options]
|
72
72
|
}]
|
73
73
|
|
74
|
+
|
74
75
|
ActiveAdmin.register model do
|
76
|
+
controller do
|
77
|
+
def scoped_collection
|
78
|
+
super.includes super.nested_attributes_options.keys
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
75
82
|
index do
|
76
83
|
cols = model.columns.map(&:name)-klass.exclude_params
|
77
84
|
cols.each_with_index do |c,i|
|
@@ -7,58 +7,5 @@ class LocationGps < AbstractAdapter
|
|
7
7
|
validates_numericality_of :lng, greater_than_or_equal_to: -180.0, less_than_or_equal_to: 180.0
|
8
8
|
validates_numericality_of :alt
|
9
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
10
|
|
64
11
|
end
|
@@ -19,13 +19,6 @@ class User < AbstractAdapter
|
|
19
19
|
has_many :team_users
|
20
20
|
has_many :teams, through: :team_users
|
21
21
|
|
22
|
-
has_many :own_chats, foreign_key: :creator_id, class_name: 'Chat'
|
23
|
-
has_many :chat_users
|
24
|
-
has_many :chats, through: :chat_users
|
25
|
-
has_many :chat_message_users
|
26
|
-
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
|
27
|
-
include User::Chatter
|
28
|
-
|
29
22
|
has_many :roles, dependent: :destroy, inverse_of: :user
|
30
23
|
accepts_nested_attributes_for :roles, allow_destroy: true
|
31
24
|
has_many :admin_companies, through: :roles, source: :ownable, source_type: Company
|
@@ -1,7 +1,13 @@
|
|
1
1
|
# SQLite version 3.x
|
2
2
|
# gem install sqlite3-ruby (not necessary on OS X Leopard)
|
3
|
-
|
3
|
+
|
4
|
+
default: &default
|
4
5
|
adapter: sqlite3
|
6
|
+
database: ":memory:"
|
7
|
+
|
8
|
+
|
9
|
+
development:
|
10
|
+
<<: *default
|
5
11
|
database: db/development.sqlite3
|
6
12
|
pool: 5
|
7
13
|
timeout: 5000
|
@@ -10,9 +16,7 @@ development:
|
|
10
16
|
# re-generated from your development database when you run "rake".
|
11
17
|
# Do not set this db to the same as development or production.
|
12
18
|
test:
|
13
|
-
|
14
|
-
database: ":memory:"
|
19
|
+
<<: *default
|
15
20
|
|
16
21
|
production:
|
17
|
-
|
18
|
-
database: ":memory:"
|
22
|
+
<<: *default
|
data/spec/rails_helper.rb
CHANGED
data/spec/support/blueprints.rb
CHANGED
@@ -42,22 +42,6 @@ Role.blueprint {
|
|
42
42
|
ownable_type { 'Company' }
|
43
43
|
}
|
44
44
|
|
45
|
-
Chat.blueprint {
|
46
|
-
creator { User.make }
|
47
|
-
}
|
48
|
-
ChatMessage.blueprint {
|
49
|
-
author = User.make
|
50
|
-
chat = Chat.make(users: [author, User.make])
|
51
|
-
chat.save
|
52
|
-
chat { chat }
|
53
|
-
author { author }
|
54
|
-
message { paragraph }
|
55
|
-
}
|
56
|
-
ChatUser.blueprint {
|
57
|
-
chat { Chat.make }
|
58
|
-
user { User.make }
|
59
|
-
}
|
60
|
-
|
61
45
|
Locatable.blueprint {
|
62
46
|
location { Location.make }
|
63
47
|
locatable { Company.make }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: introspective_admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Buermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sass-rails
|
@@ -39,21 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: devise
|
42
|
+
name: byebug
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - ">="
|
@@ -67,21 +53,21 @@ dependencies:
|
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
56
|
+
name: rspec-rails
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - ">="
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
61
|
+
version: '3.0'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - ">="
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
68
|
+
version: '3.0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
70
|
+
name: devise
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - ">="
|
@@ -95,7 +81,7 @@ dependencies:
|
|
95
81
|
- !ruby/object:Gem::Version
|
96
82
|
version: '0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
84
|
+
name: devise-async
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
87
|
- - ">="
|
@@ -109,7 +95,7 @@ dependencies:
|
|
109
95
|
- !ruby/object:Gem::Version
|
110
96
|
version: '0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
98
|
+
name: machinist
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
114
100
|
requirements:
|
115
101
|
- - ">="
|
@@ -123,21 +109,7 @@ dependencies:
|
|
123
109
|
- !ruby/object:Gem::Version
|
124
110
|
version: '0'
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - '='
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: 2.0.0.pre12
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - '='
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: 2.0.0.pre12
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: schema_validations
|
112
|
+
name: simplecov
|
141
113
|
requirement: !ruby/object:Gem::Requirement
|
142
114
|
requirements:
|
143
115
|
- - ">="
|
@@ -151,7 +123,7 @@ dependencies:
|
|
151
123
|
- !ruby/object:Gem::Version
|
152
124
|
version: '0'
|
153
125
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
126
|
+
name: rufus-mnemo
|
155
127
|
requirement: !ruby/object:Gem::Requirement
|
156
128
|
requirements:
|
157
129
|
- - ">="
|
@@ -172,6 +144,7 @@ extensions: []
|
|
172
144
|
extra_rdoc_files: []
|
173
145
|
files:
|
174
146
|
- ".gitignore"
|
147
|
+
- ".travis.yml"
|
175
148
|
- Gemfile
|
176
149
|
- Gemfile.lock
|
177
150
|
- LICENSE
|
@@ -206,10 +179,6 @@ files:
|
|
206
179
|
- spec/dummy/app/models/.keep
|
207
180
|
- spec/dummy/app/models/abstract_adapter.rb
|
208
181
|
- spec/dummy/app/models/admin_user.rb
|
209
|
-
- spec/dummy/app/models/chat.rb
|
210
|
-
- spec/dummy/app/models/chat_message.rb
|
211
|
-
- spec/dummy/app/models/chat_message_user.rb
|
212
|
-
- spec/dummy/app/models/chat_user.rb
|
213
182
|
- spec/dummy/app/models/company.rb
|
214
183
|
- spec/dummy/app/models/concerns/.keep
|
215
184
|
- spec/dummy/app/models/job.rb
|
@@ -223,7 +192,6 @@ files:
|
|
223
192
|
- spec/dummy/app/models/team.rb
|
224
193
|
- spec/dummy/app/models/team_user.rb
|
225
194
|
- spec/dummy/app/models/user.rb
|
226
|
-
- spec/dummy/app/models/user/chatter.rb
|
227
195
|
- spec/dummy/app/models/user_location.rb
|
228
196
|
- spec/dummy/app/models/user_project_job.rb
|
229
197
|
- spec/dummy/app/views/layouts/application.html.erb
|
@@ -335,10 +303,6 @@ test_files:
|
|
335
303
|
- spec/dummy/app/models/.keep
|
336
304
|
- spec/dummy/app/models/abstract_adapter.rb
|
337
305
|
- spec/dummy/app/models/admin_user.rb
|
338
|
-
- spec/dummy/app/models/chat.rb
|
339
|
-
- spec/dummy/app/models/chat_message.rb
|
340
|
-
- spec/dummy/app/models/chat_message_user.rb
|
341
|
-
- spec/dummy/app/models/chat_user.rb
|
342
306
|
- spec/dummy/app/models/company.rb
|
343
307
|
- spec/dummy/app/models/concerns/.keep
|
344
308
|
- spec/dummy/app/models/job.rb
|
@@ -352,7 +316,6 @@ test_files:
|
|
352
316
|
- spec/dummy/app/models/team.rb
|
353
317
|
- spec/dummy/app/models/team_user.rb
|
354
318
|
- spec/dummy/app/models/user.rb
|
355
|
-
- spec/dummy/app/models/user/chatter.rb
|
356
319
|
- spec/dummy/app/models/user_location.rb
|
357
320
|
- spec/dummy/app/models/user_project_job.rb
|
358
321
|
- spec/dummy/app/views/layouts/application.html.erb
|
@@ -417,4 +380,3 @@ test_files:
|
|
417
380
|
- spec/rails_helper.rb
|
418
381
|
- spec/support/blueprints.rb
|
419
382
|
- spec/support/location_helper.rb
|
420
|
-
has_rdoc:
|
@@ -1,18 +0,0 @@
|
|
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
|
@@ -1,34 +0,0 @@
|
|
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
|
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,16 +0,0 @@
|
|
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
|
@@ -1,79 +0,0 @@
|
|
1
|
-
module User::Chatter
|
2
|
-
|
3
|
-
def message_query(chat_id: 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: 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: chat_id, new: 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: users, message: 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: chat, message: 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: chat, users: 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:chat, message: "#{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
|