hashtags 0.1.0
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 +9 -0
- data/.travis.yml +25 -0
- data/Gemfile +33 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +253 -0
- data/Rakefile +19 -0
- data/app/controllers/hashtags/resources_controller.rb +28 -0
- data/app/helpers/hashtags/view_helper.rb +8 -0
- data/app/views/hashtags/_help.slim +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/locales/.gitkeep +0 -0
- data/config/locales/hashtags/builder.en.yml +5 -0
- data/config/routes.rb +5 -0
- data/hashtags.gemspec +35 -0
- data/lib/assets/javascripts/hashtags.js +3 -0
- data/lib/assets/javascripts/hashtags/hashtags.coffee +66 -0
- data/lib/assets/stylesheets/hashtags.css +3 -0
- data/lib/assets/stylesheets/hashtags/help.scss +11 -0
- data/lib/assets/stylesheets/hashtags/textcomplete.scss +21 -0
- data/lib/hashtags.rb +11 -0
- data/lib/hashtags/base.rb +161 -0
- data/lib/hashtags/builder.rb +81 -0
- data/lib/hashtags/engine.rb +7 -0
- data/lib/hashtags/mongoid_extension.rb +36 -0
- data/lib/hashtags/resource.rb +92 -0
- data/lib/hashtags/resource_type.rb +56 -0
- data/lib/hashtags/user.rb +92 -0
- data/lib/hashtags/variable.rb +59 -0
- data/lib/hashtags/version.rb +3 -0
- metadata +243 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
|
3
|
+
module Hashtags
|
4
|
+
class Resource < Base
|
5
|
+
# implement as resource class in your application
|
6
|
+
def self.resource_class
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.find_by_resource_type(resource_type)
|
11
|
+
descendants.detect{ |cls| cls.resource_name.to_s == resource_type.to_s }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.resource_name
|
15
|
+
resource_class.to_s.demodulize.underscore
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.cache_key
|
19
|
+
resource_class.cache_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.trigger
|
23
|
+
'#'
|
24
|
+
end
|
25
|
+
|
26
|
+
# name of attribute to be used in the tag
|
27
|
+
# #resource_name:<tag_attribute>(id)
|
28
|
+
# (for example :to_s)
|
29
|
+
def self.tag_attribute
|
30
|
+
raise NotImplemented
|
31
|
+
end
|
32
|
+
|
33
|
+
# the tags will be replaced by this attribute
|
34
|
+
# (for example :link_to)
|
35
|
+
def self.result_attribute
|
36
|
+
raise NotImplemented
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.regexp
|
40
|
+
/#{Regexp.escape(trigger)}#{resource_name}:(.+?)\((.+?)\b\)/i
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.help_values
|
44
|
+
[resource_name]
|
45
|
+
end
|
46
|
+
|
47
|
+
# ---------------------------------------------------------------------
|
48
|
+
# JS
|
49
|
+
|
50
|
+
def self.match_regexp
|
51
|
+
/(#{Regexp.escape(trigger)}#{resource_name}\:)(\w{1,})\z/
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.match_index
|
55
|
+
2
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.match_template
|
59
|
+
"{{ #{tag_attribute} }}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.replace
|
63
|
+
"#{trigger}#{resource_name}:{{ #{tag_attribute} }}({{ id }})"
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.template
|
67
|
+
"{{ #{tag_attribute} }}"
|
68
|
+
end
|
69
|
+
|
70
|
+
private # =============================================================
|
71
|
+
|
72
|
+
def hashtag(match)
|
73
|
+
return unless id = match[self.class.match_index]
|
74
|
+
return unless res = resource(id)
|
75
|
+
Handlebars::Context.new
|
76
|
+
.compile(self.class.replace)
|
77
|
+
.call(id: res.id.to_s, self.class.tag_attribute => res.send(self.class.tag_attribute))
|
78
|
+
end
|
79
|
+
|
80
|
+
def markup(match)
|
81
|
+
return unless id = match[self.class.match_index]
|
82
|
+
return unless res = resource(id)
|
83
|
+
res.send(self.class.result_attribute)
|
84
|
+
end
|
85
|
+
|
86
|
+
# finds resource based on tag_attribute_value
|
87
|
+
# (for example resource_class.find(value))
|
88
|
+
def resource(value)
|
89
|
+
raise NotImplemented
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'active_support/descendants_tracker'
|
2
|
+
|
3
|
+
module Hashtags
|
4
|
+
class ResourceType < Base
|
5
|
+
def self.trigger
|
6
|
+
'#'
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.regexp
|
10
|
+
/#{trigger}(.+?)\b/i
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.resource_classes
|
14
|
+
Resource.descendants
|
15
|
+
end
|
16
|
+
|
17
|
+
# ---------------------------------------------------------------------
|
18
|
+
# JS
|
19
|
+
|
20
|
+
def self.match_regexp
|
21
|
+
/(\A#{trigger}|\s#{trigger})(\w*)\z/
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.match_index
|
25
|
+
2
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.replace
|
29
|
+
'$1{{ this }}:'
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.template
|
33
|
+
'{{ this }}'
|
34
|
+
end
|
35
|
+
|
36
|
+
# ---------------------------------------------------------------------
|
37
|
+
|
38
|
+
def self.values(hashtag_classes = Resource.descendants)
|
39
|
+
(resource_classes & hashtag_classes).map(&:resource_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.cache_key
|
43
|
+
resource_classes.map(&:resource_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
private # =============================================================
|
47
|
+
|
48
|
+
def hashtag(match)
|
49
|
+
# do nothing
|
50
|
+
end
|
51
|
+
|
52
|
+
def markup(match)
|
53
|
+
# do nothing
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'handlebars'
|
2
|
+
|
3
|
+
module Hashtags
|
4
|
+
class User < Base
|
5
|
+
# implement as user class in your application
|
6
|
+
def self.resource_class
|
7
|
+
raise NotImplemented
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.resource_name
|
11
|
+
resource_class.to_s.demodulize.underscore
|
12
|
+
end
|
13
|
+
|
14
|
+
# override for custom cache_key
|
15
|
+
def self.cache_key
|
16
|
+
resource_class.cache_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.trigger
|
20
|
+
'@'
|
21
|
+
end
|
22
|
+
|
23
|
+
# name of attribute to be used in the tag
|
24
|
+
# @<tag_attribute> (for example :username)
|
25
|
+
def self.tag_attribute
|
26
|
+
raise NotImplemented
|
27
|
+
end
|
28
|
+
|
29
|
+
# the tags will be replaced by this attribute
|
30
|
+
# (for example :full_name)
|
31
|
+
def self.result_attribute
|
32
|
+
raise NotImplemented
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.regexp
|
36
|
+
/#{Regexp.escape(trigger)}(\w+)/i
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.help_values
|
40
|
+
[resource_name]
|
41
|
+
end
|
42
|
+
|
43
|
+
# ---------------------------------------------------------------------
|
44
|
+
# JS
|
45
|
+
|
46
|
+
def self.match_regexp
|
47
|
+
/(\A#{Regexp.escape(trigger)}|\s#{Regexp.escape(trigger)})(\w{1,})\z/
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.match_index
|
51
|
+
2
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.match_template
|
55
|
+
"{{ #{tag_attribute} }}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.replace
|
59
|
+
"#{trigger}{{ #{tag_attribute} }}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.template
|
63
|
+
"{{ #{tag_attribute} }}"
|
64
|
+
end
|
65
|
+
|
66
|
+
private # =============================================================
|
67
|
+
|
68
|
+
# updates found tags with tag value from resource
|
69
|
+
# @jtschichold => @JTschichold
|
70
|
+
def hashtag(match)
|
71
|
+
return unless id = match[self.class.match_index-1]
|
72
|
+
return unless user = resource(id)
|
73
|
+
Handlebars::Context.new
|
74
|
+
.compile(self.class.replace)
|
75
|
+
.call(self.class.tag_attribute => user.send(self.class.tag_attribute))
|
76
|
+
end
|
77
|
+
|
78
|
+
# replaces tags with result from resource
|
79
|
+
# @JTschichold => Jan Tschichold
|
80
|
+
def markup(match)
|
81
|
+
return unless id = match[self.class.match_index-1]
|
82
|
+
return unless user = resource(id)
|
83
|
+
user.send(self.class.result_attribute)
|
84
|
+
end
|
85
|
+
|
86
|
+
# finds resource based on tag_attribute_value
|
87
|
+
# for example: resource_class.where(username: tag_attribute_value).first
|
88
|
+
def resource(value)
|
89
|
+
raise NotImplemented
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Hashtags
|
2
|
+
class Variable < Base
|
3
|
+
def self.trigger
|
4
|
+
'$'
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.regexp
|
8
|
+
/#{Regexp.escape(trigger)}(.+?)\b/i
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.values(hashtag_classes = Variable.descendants)
|
12
|
+
raise NotImplemented
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.help_values
|
16
|
+
values
|
17
|
+
end
|
18
|
+
|
19
|
+
# ---------------------------------------------------------------------
|
20
|
+
# JS
|
21
|
+
|
22
|
+
def self.match_regexp
|
23
|
+
/(\A#{Regexp.escape(trigger)}|\s#{Regexp.escape(trigger)})(\w{1,})\z/
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.match_index
|
27
|
+
2
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.replace
|
31
|
+
"#{trigger}{{ this }}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.template
|
35
|
+
'{{ this }}'
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.strategy(hashtag_classes)
|
39
|
+
super.tap { |obj| obj[:values] = compound_values(hashtag_classes) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.compound_values(hashtag_classes)
|
43
|
+
cls = Builder.new.filter_classes(variable_classes & hashtag_classes)
|
44
|
+
cls.map { |i| i.values(hashtag_classes) }.flatten.compact
|
45
|
+
end
|
46
|
+
|
47
|
+
private # =============================================================
|
48
|
+
|
49
|
+
def hashtag(match)
|
50
|
+
return unless name(match)
|
51
|
+
[self.class.trigger, name(match)].join
|
52
|
+
end
|
53
|
+
|
54
|
+
def name(match)
|
55
|
+
return unless self.class.values.include?(match[1])
|
56
|
+
match[1]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hashtags
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomas Celizna
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: handlebars
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rails-assets-jquery-textcomplete
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails-assets-handlebars
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.12'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.12'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard-minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: minitest
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '5.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '5.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: database_cleaner
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: mongoid
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '5.0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '5.0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: slim
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description:
|
182
|
+
email:
|
183
|
+
- tomas.celizna@gmail.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- ".gitignore"
|
189
|
+
- ".travis.yml"
|
190
|
+
- Gemfile
|
191
|
+
- Guardfile
|
192
|
+
- LICENSE.txt
|
193
|
+
- README.md
|
194
|
+
- Rakefile
|
195
|
+
- app/controllers/hashtags/resources_controller.rb
|
196
|
+
- app/helpers/hashtags/view_helper.rb
|
197
|
+
- app/views/hashtags/_help.slim
|
198
|
+
- bin/console
|
199
|
+
- bin/setup
|
200
|
+
- config/locales/.gitkeep
|
201
|
+
- config/locales/hashtags/builder.en.yml
|
202
|
+
- config/routes.rb
|
203
|
+
- hashtags.gemspec
|
204
|
+
- lib/assets/javascripts/hashtags.js
|
205
|
+
- lib/assets/javascripts/hashtags/hashtags.coffee
|
206
|
+
- lib/assets/stylesheets/hashtags.css
|
207
|
+
- lib/assets/stylesheets/hashtags/help.scss
|
208
|
+
- lib/assets/stylesheets/hashtags/textcomplete.scss
|
209
|
+
- lib/hashtags.rb
|
210
|
+
- lib/hashtags/base.rb
|
211
|
+
- lib/hashtags/builder.rb
|
212
|
+
- lib/hashtags/engine.rb
|
213
|
+
- lib/hashtags/mongoid_extension.rb
|
214
|
+
- lib/hashtags/resource.rb
|
215
|
+
- lib/hashtags/resource_type.rb
|
216
|
+
- lib/hashtags/user.rb
|
217
|
+
- lib/hashtags/variable.rb
|
218
|
+
- lib/hashtags/version.rb
|
219
|
+
homepage: https://github.com/tomasc/hashtags
|
220
|
+
licenses:
|
221
|
+
- MIT
|
222
|
+
metadata: {}
|
223
|
+
post_install_message:
|
224
|
+
rdoc_options: []
|
225
|
+
require_paths:
|
226
|
+
- lib
|
227
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
228
|
+
requirements:
|
229
|
+
- - ">="
|
230
|
+
- !ruby/object:Gem::Version
|
231
|
+
version: '0'
|
232
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0'
|
237
|
+
requirements: []
|
238
|
+
rubyforge_project:
|
239
|
+
rubygems_version: 2.4.5.1
|
240
|
+
signing_key:
|
241
|
+
specification_version: 4
|
242
|
+
summary: Rails engine to facilitate inline text hashtags.
|
243
|
+
test_files: []
|