bullet_instructure 4.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +75 -0
- data/Gemfile +19 -0
- data/Gemfile.mongoid +14 -0
- data/Gemfile.mongoid-2.4 +19 -0
- data/Gemfile.mongoid-2.5 +19 -0
- data/Gemfile.mongoid-2.6 +19 -0
- data/Gemfile.mongoid-2.7 +19 -0
- data/Gemfile.mongoid-2.8 +19 -0
- data/Gemfile.mongoid-3.0 +19 -0
- data/Gemfile.mongoid-3.1 +19 -0
- data/Gemfile.mongoid-4.0 +19 -0
- data/Gemfile.rails-3.0 +19 -0
- data/Gemfile.rails-3.1 +19 -0
- data/Gemfile.rails-3.2 +19 -0
- data/Gemfile.rails-4.0 +19 -0
- data/Gemfile.rails-4.1 +19 -0
- data/Guardfile +8 -0
- data/Hacking.md +74 -0
- data/MIT-LICENSE +20 -0
- data/README.md +428 -0
- data/Rakefile +52 -0
- data/bullet_instructure.gemspec +27 -0
- data/lib/bullet.rb +196 -0
- data/lib/bullet/active_record3.rb +148 -0
- data/lib/bullet/active_record3x.rb +128 -0
- data/lib/bullet/active_record4.rb +128 -0
- data/lib/bullet/active_record41.rb +121 -0
- data/lib/bullet/dependency.rb +81 -0
- data/lib/bullet/detector.rb +9 -0
- data/lib/bullet/detector/association.rb +67 -0
- data/lib/bullet/detector/base.rb +6 -0
- data/lib/bullet/detector/counter_cache.rb +59 -0
- data/lib/bullet/detector/n_plus_one_query.rb +89 -0
- data/lib/bullet/detector/unused_eager_loading.rb +84 -0
- data/lib/bullet/ext/object.rb +9 -0
- data/lib/bullet/ext/string.rb +5 -0
- data/lib/bullet/mongoid2x.rb +56 -0
- data/lib/bullet/mongoid3x.rb +56 -0
- data/lib/bullet/mongoid4x.rb +56 -0
- data/lib/bullet/notification.rb +10 -0
- data/lib/bullet/notification/base.rb +97 -0
- data/lib/bullet/notification/counter_cache.rb +13 -0
- data/lib/bullet/notification/n_plus_one_query.rb +28 -0
- data/lib/bullet/notification/unused_eager_loading.rb +13 -0
- data/lib/bullet/notification_collector.rb +24 -0
- data/lib/bullet/rack.rb +81 -0
- data/lib/bullet/registry.rb +7 -0
- data/lib/bullet/registry/association.rb +13 -0
- data/lib/bullet/registry/base.rb +40 -0
- data/lib/bullet/registry/object.rb +13 -0
- data/lib/bullet/version.rb +4 -0
- data/perf/benchmark.rb +121 -0
- data/rails/init.rb +1 -0
- data/spec/bullet/detector/association_spec.rb +26 -0
- data/spec/bullet/detector/base_spec.rb +8 -0
- data/spec/bullet/detector/counter_cache_spec.rb +56 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +138 -0
- data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
- data/spec/bullet/ext/object_spec.rb +17 -0
- data/spec/bullet/ext/string_spec.rb +13 -0
- data/spec/bullet/notification/base_spec.rb +83 -0
- data/spec/bullet/notification/counter_cache_spec.rb +12 -0
- data/spec/bullet/notification/n_plus_one_query_spec.rb +14 -0
- data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
- data/spec/bullet/notification_collector_spec.rb +32 -0
- data/spec/bullet/rack_spec.rb +97 -0
- data/spec/bullet/registry/association_spec.rb +26 -0
- data/spec/bullet/registry/base_spec.rb +44 -0
- data/spec/bullet/registry/object_spec.rb +24 -0
- data/spec/bullet_spec.rb +41 -0
- data/spec/integration/active_record3/association_spec.rb +651 -0
- data/spec/integration/active_record4/association_spec.rb +649 -0
- data/spec/integration/counter_cache_spec.rb +63 -0
- data/spec/integration/mongoid/association_spec.rb +258 -0
- data/spec/models/address.rb +3 -0
- data/spec/models/author.rb +3 -0
- data/spec/models/base_user.rb +5 -0
- data/spec/models/category.rb +7 -0
- data/spec/models/city.rb +3 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/company.rb +3 -0
- data/spec/models/country.rb +3 -0
- data/spec/models/document.rb +5 -0
- data/spec/models/entry.rb +3 -0
- data/spec/models/firm.rb +4 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/mongoid/address.rb +7 -0
- data/spec/models/mongoid/category.rb +8 -0
- data/spec/models/mongoid/comment.rb +7 -0
- data/spec/models/mongoid/company.rb +7 -0
- data/spec/models/mongoid/entry.rb +7 -0
- data/spec/models/mongoid/post.rb +12 -0
- data/spec/models/mongoid/user.rb +5 -0
- data/spec/models/newspaper.rb +3 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +3 -0
- data/spec/models/pet.rb +3 -0
- data/spec/models/post.rb +10 -0
- data/spec/models/relationship.rb +4 -0
- data/spec/models/student.rb +3 -0
- data/spec/models/submission.rb +4 -0
- data/spec/models/teacher.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/support/bullet_ext.rb +55 -0
- data/spec/support/mongo_seed.rb +65 -0
- data/spec/support/rack_double.rb +55 -0
- data/spec/support/sqlite_seed.rb +229 -0
- data/tasks/bullet_tasks.rake +9 -0
- data/test.sh +15 -0
- metadata +246 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Detector
|
3
|
+
class NPlusOneQuery < Association
|
4
|
+
extend Dependency
|
5
|
+
|
6
|
+
class <<self
|
7
|
+
# executed when object.assocations is called.
|
8
|
+
# first, it keeps this method call for object.association.
|
9
|
+
# then, it checks if this associations call is unpreload.
|
10
|
+
# if it is, keeps this unpreload associations and caller.
|
11
|
+
def call_association(object, associations)
|
12
|
+
return unless Bullet.start?
|
13
|
+
return unless object.id
|
14
|
+
add_call_object_associations(object, associations)
|
15
|
+
|
16
|
+
Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_key}, associations: #{associations}")
|
17
|
+
if conditions_met?(object.bullet_key, associations)
|
18
|
+
Bullet.debug("detect n + 1 query", "object: #{object.bullet_key}, associations: #{associations}")
|
19
|
+
create_notification caller_in_project, object.class.to_s, associations
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_possible_objects(object_or_objects)
|
24
|
+
return unless Bullet.start?
|
25
|
+
return unless Bullet.n_plus_one_query_enable?
|
26
|
+
objects = Array(object_or_objects)
|
27
|
+
return if objects.map(&:id).compact.empty?
|
28
|
+
|
29
|
+
Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
|
30
|
+
objects.each { |object| possible_objects.add object.bullet_key }
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_impossible_object(object)
|
34
|
+
return unless Bullet.start?
|
35
|
+
return unless Bullet.n_plus_one_query_enable?
|
36
|
+
return unless object.id
|
37
|
+
|
38
|
+
Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_key}")
|
39
|
+
impossible_objects.add object.bullet_key
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def create_notification(callers, klazz, associations)
|
44
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
|
45
|
+
|
46
|
+
if notify_associations.present?
|
47
|
+
notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
|
48
|
+
Bullet.notification_collector.add(notice)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# decide whether the object.associations is unpreloaded or not.
|
53
|
+
def conditions_met?(bullet_key, associations)
|
54
|
+
possible?(bullet_key) && !impossible?(bullet_key) && !association?(bullet_key, associations)
|
55
|
+
end
|
56
|
+
|
57
|
+
def caller_in_project
|
58
|
+
app_root = rails? ? Rails.root.to_s : Dir.pwd
|
59
|
+
vendor_root = app_root + "/vendor"
|
60
|
+
caller.select do |c|
|
61
|
+
c.include?(app_root) && !c.include?(vendor_root) ||
|
62
|
+
Bullet.stacktrace_includes.any? { |include| c.include?(include) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def possible?(bullet_key)
|
67
|
+
possible_objects.include? bullet_key
|
68
|
+
end
|
69
|
+
|
70
|
+
def impossible?(bullet_key)
|
71
|
+
impossible_objects.include? bullet_key
|
72
|
+
end
|
73
|
+
|
74
|
+
# check if object => associations already exists in object_associations.
|
75
|
+
def association?(bullet_key, associations)
|
76
|
+
value = object_associations[bullet_key]
|
77
|
+
if value
|
78
|
+
value.each do |v|
|
79
|
+
result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations
|
80
|
+
return true if result
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Detector
|
3
|
+
class UnusedEagerLoading < Association
|
4
|
+
class <<self
|
5
|
+
# check if there are unused preload associations.
|
6
|
+
# get related_objects from eager_loadings associated with object and associations
|
7
|
+
# get call_object_association from associations of call_object_associations whose object is in related_objects
|
8
|
+
# if association not in call_object_association, then the object => association - call_object_association is ununsed preload assocations
|
9
|
+
def check_unused_preload_associations
|
10
|
+
return unless Bullet.start?
|
11
|
+
return unless Bullet.unused_eager_loading_enable?
|
12
|
+
|
13
|
+
object_associations.each do |bullet_key, associations|
|
14
|
+
object_association_diff = diff_object_associations bullet_key, associations
|
15
|
+
next if object_association_diff.empty?
|
16
|
+
|
17
|
+
Bullet.debug("detect unused preload", "object: #{bullet_key}, associations: #{object_association_diff}")
|
18
|
+
create_notification bullet_key.bullet_class_name, object_association_diff
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_eager_loadings(objects, associations)
|
23
|
+
return unless Bullet.start?
|
24
|
+
return unless Bullet.unused_eager_loading_enable?
|
25
|
+
return if objects.map(&:id).compact.empty?
|
26
|
+
|
27
|
+
Bullet.debug("Detector::UnusedEagerLoading#add_eager_loadings", "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}")
|
28
|
+
bullet_keys = objects.map(&:bullet_key)
|
29
|
+
|
30
|
+
to_add = nil
|
31
|
+
to_merge, to_delete = [], []
|
32
|
+
eager_loadings.each do |k, v|
|
33
|
+
key_objects_overlap = k & bullet_keys
|
34
|
+
|
35
|
+
next if key_objects_overlap.empty?
|
36
|
+
|
37
|
+
if key_objects_overlap == k
|
38
|
+
to_add = [k, associations]
|
39
|
+
break
|
40
|
+
else
|
41
|
+
to_merge << [key_objects_overlap, ( eager_loadings[k].dup << associations )]
|
42
|
+
|
43
|
+
keys_without_objects = k - bullet_keys
|
44
|
+
to_merge << [keys_without_objects, eager_loadings[k]]
|
45
|
+
to_delete << k
|
46
|
+
bullet_keys = bullet_keys - k
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
eager_loadings.add *to_add if to_add
|
51
|
+
to_merge.each { |k,val| eager_loadings.merge k, val }
|
52
|
+
to_delete.each { |k| eager_loadings.delete k }
|
53
|
+
|
54
|
+
eager_loadings.add bullet_keys, associations unless bullet_keys.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def create_notification(klazz, associations)
|
59
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
|
60
|
+
|
61
|
+
if notify_associations.present?
|
62
|
+
notice = Bullet::Notification::UnusedEagerLoading.new(klazz, notify_associations)
|
63
|
+
Bullet.notification_collector.add(notice)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def call_associations(bullet_key, associations)
|
68
|
+
all = Set.new
|
69
|
+
eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
|
70
|
+
coa = call_object_associations[related_bullet_key]
|
71
|
+
next if coa.nil?
|
72
|
+
all.merge coa
|
73
|
+
end
|
74
|
+
all.to_a
|
75
|
+
end
|
76
|
+
|
77
|
+
def diff_object_associations(bullet_key, associations)
|
78
|
+
potential_associations = associations - call_associations(bullet_key, associations)
|
79
|
+
potential_associations.reject { |a| a.is_a?(Hash) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Mongoid
|
3
|
+
def self.enable
|
4
|
+
require 'mongoid'
|
5
|
+
|
6
|
+
::Mongoid::Contexts::Mongo.class_eval do
|
7
|
+
alias_method :origin_first, :first
|
8
|
+
alias_method :origin_last, :last
|
9
|
+
alias_method :origin_iterate, :iterate
|
10
|
+
alias_method :origin_eager_load, :eager_load
|
11
|
+
|
12
|
+
def first
|
13
|
+
result = origin_first
|
14
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def last
|
19
|
+
result = origin_last
|
20
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def iterate(&block)
|
25
|
+
records = execute.to_a
|
26
|
+
if records.size > 1
|
27
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
28
|
+
elsif records.size == 1
|
29
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
30
|
+
end
|
31
|
+
origin_iterate(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def eager_load(docs)
|
35
|
+
associations = criteria.inclusions.map(&:name)
|
36
|
+
docs.each do |doc|
|
37
|
+
Bullet::Detector::Association.add_object_associations(doc, associations)
|
38
|
+
end
|
39
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
40
|
+
origin_eager_load(docs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
::Mongoid::Relations::Accessors.class_eval do
|
45
|
+
alias_method :origin_set_relation, :set_relation
|
46
|
+
|
47
|
+
def set_relation(name, relation)
|
48
|
+
if relation && relation.metadata.macro !~ /embed/
|
49
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
50
|
+
end
|
51
|
+
origin_set_relation(name, relation)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Mongoid
|
3
|
+
def self.enable
|
4
|
+
require 'mongoid'
|
5
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
6
|
+
alias_method :origin_first, :first
|
7
|
+
alias_method :origin_last, :last
|
8
|
+
alias_method :origin_each, :each
|
9
|
+
alias_method :origin_eager_load, :eager_load
|
10
|
+
|
11
|
+
def first
|
12
|
+
result = origin_first
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
def last
|
18
|
+
result = origin_last
|
19
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
records = query.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
25
|
+
if records.length > 1
|
26
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
27
|
+
elsif records.size == 1
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
29
|
+
end
|
30
|
+
origin_each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def eager_load(docs)
|
34
|
+
associations = criteria.inclusions.map(&:name)
|
35
|
+
docs.each do |doc|
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
37
|
+
end
|
38
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
39
|
+
origin_eager_load(docs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
::Mongoid::Relations::Accessors.class_eval do
|
44
|
+
alias_method :origin_get_relation, :get_relation
|
45
|
+
|
46
|
+
def get_relation(name, metadata, reload = false)
|
47
|
+
result = origin_get_relation(name, metadata, reload)
|
48
|
+
if metadata.macro !~ /embed/
|
49
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Mongoid
|
3
|
+
def self.enable
|
4
|
+
require 'mongoid'
|
5
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
6
|
+
alias_method :origin_first, :first
|
7
|
+
alias_method :origin_last, :last
|
8
|
+
alias_method :origin_each, :each
|
9
|
+
alias_method :origin_eager_load, :eager_load
|
10
|
+
|
11
|
+
def first
|
12
|
+
result = origin_first
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
def last
|
18
|
+
result = origin_last
|
19
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&block)
|
24
|
+
records = query.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
|
25
|
+
if records.length > 1
|
26
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
27
|
+
elsif records.size == 1
|
28
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
29
|
+
end
|
30
|
+
origin_each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def eager_load(docs)
|
34
|
+
associations = criteria.inclusions.map(&:name)
|
35
|
+
docs.each do |doc|
|
36
|
+
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
|
37
|
+
end
|
38
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
39
|
+
origin_eager_load(docs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
::Mongoid::Relations::Accessors.class_eval do
|
44
|
+
alias_method :origin_get_relation, :get_relation
|
45
|
+
|
46
|
+
def get_relation(name, metadata, object, reload = false)
|
47
|
+
result = origin_get_relation(name, metadata, object, reload)
|
48
|
+
if metadata.macro !~ /embed/
|
49
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name)
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Notification
|
3
|
+
autoload :Base, 'bullet/notification/base'
|
4
|
+
autoload :UnusedEagerLoading, 'bullet/notification/unused_eager_loading'
|
5
|
+
autoload :NPlusOneQuery, 'bullet/notification/n_plus_one_query'
|
6
|
+
autoload :CounterCache, 'bullet/notification/counter_cache'
|
7
|
+
|
8
|
+
class UnoptimizedQueryError < StandardError; end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Notification
|
3
|
+
class Base
|
4
|
+
attr_accessor :notifier, :url
|
5
|
+
attr_reader :base_class, :associations, :path
|
6
|
+
|
7
|
+
def initialize(base_class, association_or_associations, path = nil)
|
8
|
+
@base_class = base_class
|
9
|
+
@associations = association_or_associations.is_a?(Array) ? association_or_associations : [association_or_associations]
|
10
|
+
@path = path
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
raise NoMethodError.new("no method title defined")
|
15
|
+
end
|
16
|
+
|
17
|
+
def body
|
18
|
+
raise NoMethodError.new("no method body defined")
|
19
|
+
end
|
20
|
+
|
21
|
+
def whoami
|
22
|
+
user = `whoami`
|
23
|
+
if user
|
24
|
+
"user: #{user.chomp}"
|
25
|
+
else
|
26
|
+
""
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def body_with_caller
|
31
|
+
body
|
32
|
+
end
|
33
|
+
|
34
|
+
def standard_notice
|
35
|
+
@standard_notifice ||= title + "\n" + body
|
36
|
+
end
|
37
|
+
|
38
|
+
def full_notice
|
39
|
+
[url, title, body_with_caller].compact.join("\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
def notify_inline
|
43
|
+
self.notifier.inline_notify(notification_data)
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify_out_of_channel
|
47
|
+
self.notifier.out_of_channel_notify(notification_data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def short_notice
|
51
|
+
[url, title, body].compact.join("\n")
|
52
|
+
end
|
53
|
+
|
54
|
+
def notification_data
|
55
|
+
{
|
56
|
+
:url => url,
|
57
|
+
:title => title,
|
58
|
+
:body => body_with_caller,
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def eql?(other)
|
63
|
+
klazz_associations_str == other.klazz_associations_str
|
64
|
+
end
|
65
|
+
|
66
|
+
def hash
|
67
|
+
klazz_associations_str.hash
|
68
|
+
end
|
69
|
+
|
70
|
+
class HTMLwithPygments < Redcarpet::Render::HTML
|
71
|
+
def block_code(code, language)
|
72
|
+
Pygments.highlight(code, :lexer => language)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def markdown(text)
|
77
|
+
renderer = HTMLwithPygments.new(hard_wrap: true)
|
78
|
+
options = {
|
79
|
+
:no_intra_emphasis => true,
|
80
|
+
:fenced_code_blocks => true
|
81
|
+
}
|
82
|
+
# markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
|
83
|
+
# markdown.render(text).html_safe
|
84
|
+
Redcarpet::Markdown.new(renderer, options).render(text).html_safe
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
def klazz_associations_str
|
89
|
+
" #{@base_class} => [#{@associations.map(&:inspect).join(', ')}]"
|
90
|
+
end
|
91
|
+
|
92
|
+
def associations_str
|
93
|
+
":include => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|