bbcode-rails 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -2
- data/lib/bbcode-rails.rb +146 -13
- data/lib/bbcode-rails/tag.rb +54 -14
- data/lib/bbcode-rails/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e3a03c9829b29ae970341a6869350c6d81a63e6
|
4
|
+
data.tar.gz: 1075e5565218f39b55b224764911b19b419f30da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a8313ca29126829b91a2338647b00adf318223ef4de44f7649dba819198070b5045ec211cf977094aa8024da3bf2b1bbfe66c24ae4dbcf1e28ad8d9a4cdfcde
|
7
|
+
data.tar.gz: f5cc8b2ae3c5dcc509c8275599b8d4c1a366c7b6a476773f300492fd1137a7860801b43e0d43ebb172ac5cf2d4138d9b48335ec660bece20045cef174f6ccdc7
|
data/README.md
CHANGED
@@ -55,7 +55,7 @@ This will create `app/bbcode/user_tag.rb`.
|
|
55
55
|
#app/bbcode/user.rb
|
56
56
|
|
57
57
|
class UserTag < BBCode::Tag
|
58
|
-
|
58
|
+
block_name :user
|
59
59
|
on_layout do |args|
|
60
60
|
"TODO: Implement user tag"
|
61
61
|
end
|
@@ -68,7 +68,7 @@ You could now add something like:
|
|
68
68
|
#app/bbcode/user.rb
|
69
69
|
|
70
70
|
class UserTag < BBCode::Tag
|
71
|
-
|
71
|
+
block_name :user, :argument, :no_closing_tag
|
72
72
|
on_layout do |args|
|
73
73
|
user = User.find_by_id(args[1])
|
74
74
|
render partial: 'shared/userquote', locals: { user: user }
|
@@ -78,6 +78,16 @@ end
|
|
78
78
|
|
79
79
|
Of course, the limitations are your knowledge in ruby and rails :)
|
80
80
|
|
81
|
+
### Transforming a BBCode string into HTML
|
82
|
+
|
83
|
+
Just call `bbcode_to_html` on any string.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
#> User.all.first.bio
|
87
|
+
"[i]Hello [b]Everyone[/b][/i]"
|
88
|
+
#> User.all.first.bio.bbcode_to_html
|
89
|
+
=> "<em>Hello <strong>Everyone</strong></em>"
|
90
|
+
```
|
81
91
|
|
82
92
|
## Contributing
|
83
93
|
|
data/lib/bbcode-rails.rb
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
require "bbcode-rails/version"
|
2
2
|
|
3
3
|
module BBCode
|
4
|
-
|
4
|
+
class ParseError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
@tags = {}
|
5
8
|
def self.tags
|
6
|
-
|
9
|
+
@tags
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get_tag_by_name name
|
13
|
+
if defined?(Rails) && Rails.env.development?
|
14
|
+
begin
|
15
|
+
"#{p}_tag".camelize.constantize
|
16
|
+
rescue NameError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@tags["#{name.to_s.downcase}tag"]
|
7
20
|
end
|
8
21
|
|
9
|
-
def self.parse str
|
22
|
+
def self.parse str, raise_error=false
|
10
23
|
str = str.dup
|
11
24
|
|
12
25
|
str.gsub!( '&', '&' )
|
@@ -15,20 +28,140 @@ module BBCode
|
|
15
28
|
str.gsub!( '"', '"' )
|
16
29
|
str.gsub!( "'", ''' )
|
17
30
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
# Let's iterate over the pieces to build a tree
|
32
|
+
# It works like this:
|
33
|
+
# For each object you have two things:
|
34
|
+
# 1. It is a tag name a la [img]
|
35
|
+
# 2. It is a simple string, something like 'Hello'
|
36
|
+
#
|
37
|
+
# Now, we want a tree that looks like this
|
38
|
+
#
|
39
|
+
# -> |
|
40
|
+
# | ImgTag src: http://adada
|
41
|
+
# | String This cat
|
42
|
+
# | BTag is -> |
|
43
|
+
# | ITag funny!
|
44
|
+
# In the end we just recursively append
|
45
|
+
|
46
|
+
result = []
|
47
|
+
|
48
|
+
tag_open = /\[/
|
49
|
+
tag_close = /\]/
|
50
|
+
tag_close_prefix = /\//
|
51
|
+
tag_arg = /=/
|
52
|
+
tag_arg_delim = /"/
|
53
|
+
tag_name = /[-_a-z0-9]/
|
54
|
+
|
55
|
+
current_state = :text
|
56
|
+
current_tag = result
|
57
|
+
|
58
|
+
begin
|
59
|
+
pos = 0
|
60
|
+
while pos < str.length
|
61
|
+
case current_state
|
62
|
+
when :text
|
63
|
+
tmp = ""
|
64
|
+
# We iterate through the string either until the end or if we find a [
|
65
|
+
while not str[pos] =~ tag_open and pos < str.length
|
66
|
+
tmp << str[pos]
|
67
|
+
pos = pos.next
|
68
|
+
end
|
69
|
+
current_tag << tmp
|
70
|
+
current_state = :tag_name if pos < str.length # Okay, we have found the beginning of a possible tag!
|
71
|
+
pos = pos.next
|
72
|
+
when :tag_name
|
73
|
+
name = ""
|
74
|
+
if str[pos-1] =~ tag_open and str[pos] =~ tag_close_prefix
|
75
|
+
# It's a closing tag!
|
76
|
+
# Let's check if it applies to our current tag..
|
77
|
+
|
78
|
+
broke_out = false
|
79
|
+
tag = current_tag
|
80
|
+
until tag.is_a? Array
|
81
|
+
len = tag.name.length
|
82
|
+
if str[pos+1,len] == tag.name and str[pos+len+1] =~ tag_close
|
83
|
+
# If it's the current one?
|
84
|
+
if current_tag == tag
|
85
|
+
current_tag = tag.parent
|
86
|
+
pos += len+1
|
87
|
+
broke_out = true
|
88
|
+
break
|
89
|
+
else
|
90
|
+
# It's not the current one! Invalid construct
|
91
|
+
raise BBCode::ParseError, "Invalid nested tags"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
tag = tag.parent
|
95
|
+
end
|
96
|
+
|
97
|
+
# It's not a closing tag we recognize, so it's text really!
|
98
|
+
# Let's restore this!
|
99
|
+
if not broke_out
|
100
|
+
current_tag << "[/"
|
101
|
+
end
|
102
|
+
current_state = :text
|
103
|
+
pos = pos.next
|
104
|
+
next
|
105
|
+
end
|
106
|
+
while not (str[pos] =~ tag_close and str[pos] =~ tag_arg and pos < str.length) and str[pos] =~ tag_name
|
107
|
+
name << str[pos]
|
108
|
+
pos = pos.next
|
109
|
+
end
|
110
|
+
|
111
|
+
if str[pos] =~ tag_arg or str[pos] =~ tag_close
|
112
|
+
if self.get_tag_by_name(name)
|
113
|
+
new_tag = self.get_tag_by_name(name).new(current_tag)
|
114
|
+
current_tag << new_tag
|
115
|
+
if new_tag.has_option(:content) or new_tag.has_option(:argument)
|
116
|
+
current_tag = new_tag
|
117
|
+
end
|
118
|
+
if str[pos] =~ tag_arg
|
119
|
+
if new_tag.has_option :argument
|
120
|
+
current_state = :tag_arg
|
121
|
+
else
|
122
|
+
raise BBCode::ParseError
|
123
|
+
end
|
124
|
+
else
|
125
|
+
current_state = :text
|
126
|
+
end
|
127
|
+
pos = pos.next
|
128
|
+
else
|
129
|
+
current_tag << "[#{name}"
|
130
|
+
current_state = :text
|
131
|
+
end
|
132
|
+
next
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
# Did we hit the end? Let's get out
|
137
|
+
current_tag << name
|
138
|
+
current_state = :text
|
139
|
+
when :tag_arg
|
140
|
+
arg = ""
|
141
|
+
while not str[pos] =~ tag_close and pos < str.length
|
142
|
+
arg << str[pos]
|
143
|
+
pos = pos.next
|
144
|
+
end
|
145
|
+
|
146
|
+
current_tag.argument = arg.gsub(/^#{tag_arg_delim}(.*)#{tag_arg_delim}$/, '\1')
|
147
|
+
if not current_tag.has_option(:content)
|
148
|
+
current_tag = current_tag.parent
|
149
|
+
end
|
150
|
+
current_state = :text
|
151
|
+
pos = pos.next
|
152
|
+
else
|
153
|
+
pos = pos.next
|
24
154
|
end
|
25
155
|
end
|
26
|
-
end
|
27
156
|
|
28
|
-
|
29
|
-
|
157
|
+
result.map(&:to_s).join('').strip
|
158
|
+
rescue BBCode::ParseError => e
|
159
|
+
if raise_error
|
160
|
+
raise e
|
161
|
+
else
|
162
|
+
str
|
163
|
+
end
|
30
164
|
end
|
31
|
-
str
|
32
165
|
end
|
33
166
|
|
34
167
|
end
|
data/lib/bbcode-rails/tag.rb
CHANGED
@@ -17,33 +17,73 @@ class BBCode::Tag
|
|
17
17
|
self.view_paths = "app/views"
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
attr_accessor :parent
|
21
|
+
|
22
|
+
def initialize parent
|
23
|
+
@parent = parent
|
24
|
+
@content = []
|
25
|
+
@argument = ""
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
def has_option opt
|
29
|
+
self.class.options.include?(opt)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_block
|
33
|
+
self.class.block
|
28
34
|
end
|
29
35
|
|
30
|
-
def
|
31
|
-
if
|
32
|
-
|
36
|
+
def argument= arg
|
37
|
+
if !has_option :argument
|
38
|
+
raise BBCode::ParseError, "Tried to assign an argument to a tag which takes none, #{name}"
|
39
|
+
else
|
40
|
+
@argument = arg
|
33
41
|
end
|
34
|
-
|
35
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
def << c
|
45
|
+
if !has_option :content
|
46
|
+
raise BBCode::ParseError, "Tried to assign content to a tag which takes none, #{name}"
|
36
47
|
else
|
37
|
-
@
|
48
|
+
@content << c
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
if has_option :content
|
54
|
+
result = @content.map(&:to_s).join('')
|
38
55
|
end
|
56
|
+
if has_option(:content) and has_option(:argument)
|
57
|
+
get_block.call(@argument, result)
|
58
|
+
elsif has_option :content
|
59
|
+
get_block.call(result)
|
60
|
+
elsif has_option :argument
|
61
|
+
get_block.call(@argument)
|
62
|
+
else
|
63
|
+
get_block.call
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def name
|
68
|
+
self.class.to_s.downcase.gsub(/tag$/i,'')
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.inherited subclass
|
72
|
+
# In case we autoreload, remove earlier instances
|
73
|
+
BBCode.tags.delete_if {|c| c.to_s == subclass.to_s }
|
74
|
+
BBCode.tags[subclass.to_s.downcase] = subclass
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.block_options *args
|
78
|
+
@options = args
|
39
79
|
end
|
40
80
|
|
41
81
|
def self.on_layout &b
|
42
82
|
@block = b
|
43
83
|
end
|
44
84
|
|
45
|
-
def self.
|
46
|
-
@
|
85
|
+
def self.options
|
86
|
+
@options ||= []
|
47
87
|
end
|
48
88
|
|
49
89
|
def self.block
|
data/lib/bbcode-rails/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bbcode-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcel Müller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|