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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85c25fceeb4fbc1b4c93491384ef5af2d867587d
4
- data.tar.gz: 23873eb57b191664b7b6afd60e6590841e888d2d
3
+ metadata.gz: 6e3a03c9829b29ae970341a6869350c6d81a63e6
4
+ data.tar.gz: 1075e5565218f39b55b224764911b19b419f30da
5
5
  SHA512:
6
- metadata.gz: 12894618783176fcb4053053aac63ebc69e3a6d81de7208d060cd6aa14253b5817378de929cc025f96f9255595ab6b6fae2188d846e99356507a898d4f4d2412
7
- data.tar.gz: ee74932ca1193c89a6cc4898696f390134975376b475241c459cc9b3c76b0d3f61793588a8493ab5cc20e20261bdcb92b65538699493c73977c8f107e2cce326
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
- name :user
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
- name :user, :argument, :no_closing_tag
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
- @@tags = []
4
+ class ParseError < StandardError
5
+ end
6
+
7
+ @tags = {}
5
8
  def self.tags
6
- @@tags
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!( '&', '&amp;' )
@@ -15,20 +28,140 @@ module BBCode
15
28
  str.gsub!( '"', '&quot;' )
16
29
  str.gsub!( "'", '&apos;' )
17
30
 
18
- # For eager loading
19
- if defined?(Rails) && Rails.env.development?
20
- str.scan(/\[(\w+)(?:=.+)?\]/).each do |tagname|
21
- begin
22
- "#{tagname[0]}_tag".camelize.constantize
23
- rescue NameError
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 = /&quot;/
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
- @@tags.each do |t|
29
- str.gsub!(t.regex) { t.instance.instance_exec($~, &t.block) }
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
@@ -17,33 +17,73 @@ class BBCode::Tag
17
17
  self.view_paths = "app/views"
18
18
  end
19
19
 
20
- def self.instance
21
- @_tag ||= new
20
+ attr_accessor :parent
21
+
22
+ def initialize parent
23
+ @parent = parent
24
+ @content = []
25
+ @argument = ""
22
26
  end
23
27
 
24
- def self.inherited subclass
25
- # In case we autoreload, remove earlier instances
26
- BBCode.tags.delete_if {|c| c.to_s == subclass.to_s }
27
- BBCode.tags << subclass
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 self.block_name n, *args
31
- if args.include? :argument
32
- arg = '=(?:&quot;)?(.+?)(?:&quot;)?'
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
- if args.include? :no_closing_tag
35
- @regex = /\[#{n.to_s}#{arg}\]/mi
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
- @regex = /\[#{n.to_s}#{arg}\](.+?)\[\/#{n.to_s}\]/mi
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.regex
46
- @regex
85
+ def self.options
86
+ @options ||= []
47
87
  end
48
88
 
49
89
  def self.block
@@ -1,3 +1,3 @@
1
1
  module BBCode
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
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.7.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-16 00:00:00.000000000 Z
11
+ date: 2015-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler