html_slicer 0.0.7 → 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.
- data/CHANGELOG +6 -0
- data/README.rdoc +48 -1
- data/lib/html_slicer/cached_stuff.rb +70 -0
- data/lib/html_slicer/config.rb +2 -1
- data/lib/html_slicer/installer.rb +7 -1
- data/lib/html_slicer/interface.rb +129 -9
- data/lib/html_slicer/{makers → mappers}/resizing.rb +44 -27
- data/lib/html_slicer/{makers → mappers}/slicing.rb +31 -22
- data/lib/html_slicer/options.rb +19 -3
- data/lib/html_slicer/utilities.rb +9 -1
- data/lib/html_slicer/version.rb +1 -1
- data/lib/html_slicer.rb +0 -3
- metadata +5 -5
- data/lib/html_slicer/document.rb +0 -107
data/CHANGELOG
CHANGED
data/README.rdoc
CHANGED
@@ -65,6 +65,7 @@ where:
|
|
65
65
|
* <tt>:as</tt> is a name of basic accessor for result.
|
66
66
|
* <tt>:slice</tt> is a hash of <tt>slicing options</tt> as a part of configuration.
|
67
67
|
* <tt>:resize</tt> is a hash of <tt>resizing options</tt> as a part of configuration.
|
68
|
+
* <tt>:cache_to</tt> is an accessor name used to store the cache. +True+ value make it by default.
|
68
69
|
|
69
70
|
You can define any configuration key you want. Otherwise, default configuration options (if available) will be picked up automatically.
|
70
71
|
|
@@ -198,7 +199,7 @@ you can override/complete any configuration.
|
|
198
199
|
By applying +:config+ key you can use one of stylized configurations:
|
199
200
|
|
200
201
|
class Post < ActiveRecord::Base
|
201
|
-
slice :content, :config => :paged, :slice
|
202
|
+
slice :content, :config => :paged, :slice => {:complete => /\s+/, :maximum => 3000}
|
202
203
|
end
|
203
204
|
|
204
205
|
Through passing the block you get a more flexible approach to configure slicer:
|
@@ -304,6 +305,52 @@ Examples:
|
|
304
305
|
:param_name => [:article, :page]
|
305
306
|
# means you define params[:article][:page] as a slice key.
|
306
307
|
|
308
|
+
== Caching
|
309
|
+
|
310
|
+
Caching implies that resizing/slicing procedures is both time-consuming processes will be started only at once, and once again will be launched only if the target content has been changed or resizing/slicing options has been modified.
|
311
|
+
|
312
|
+
For caching, pass the option: <tt>:cache_to => true</tt> or <tt>:cache_to => :any_other_accessor</tt> within your config definition.
|
313
|
+
|
314
|
+
=== ActiveRecord model
|
315
|
+
|
316
|
+
Example:
|
317
|
+
|
318
|
+
class Article < ActiveRecord::Base
|
319
|
+
slice :content, :as => :paged, :slice => {:maximum => 1000, :complete => /\s+|\z/}, :cache_to => :content_page_cache
|
320
|
+
end
|
321
|
+
|
322
|
+
Accessor method <tt>.content_page_cache=</tt> used for caching here.
|
323
|
+
The first time when resizing and slicing procedures is called, generated maps will be cached and assigned to <tt>.content_page_cache</tt> accessor. Before the article saves itself, assigned dump stuff is recorded like any other attribute of article (callback <tt>before_save</tt> is set up).
|
324
|
+
|
325
|
+
Of course, attribute is recorded only as a column of the database :), So, before, add the column to a model:
|
326
|
+
|
327
|
+
<tt>% rails generate migration AddContentPageCacheToArticle content_page_cache:text</tt>
|
328
|
+
|
329
|
+
<tt>% rake db:migrate </tt>
|
330
|
+
|
331
|
+
Slicing/resizing procedures repeat again only if target content has been changed or options has been modified.
|
332
|
+
|
333
|
+
=== ActiveModel
|
334
|
+
|
335
|
+
Example:
|
336
|
+
|
337
|
+
class TextModel
|
338
|
+
attr_accessor :text, :paged_cache
|
339
|
+
|
340
|
+
extend HtmlSlicer::Installer
|
341
|
+
|
342
|
+
slice :text, :as => :paged, :slice => {:complete => /\s+/, :maximum => 300}, :cache_to => true
|
343
|
+
|
344
|
+
def initialize(text)
|
345
|
+
@text = text
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
+True+ value of <tt>:cache_to</tt> option set default cache accessor name consisted of basic accessor name + <tt>_page</tt>.
|
351
|
+
|
352
|
+
In fact, caching ActiveModel not so significant in most cases, but still works. The next time you call slicing method, the cached resizing/slicing map(s) will be used.
|
353
|
+
|
307
354
|
== More
|
308
355
|
|
309
356
|
=== Slicing a general String or ActiveModel (or any other) object
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module HtmlSlicer
|
4
|
+
# Object to be serialized and dumped as cache storage.
|
5
|
+
# Include +resizing+ and +slicing+ objects, and original text's hexdigest hash value to provide authenticity.
|
6
|
+
# During the runtime object is used as an +maps+ accessor too.
|
7
|
+
|
8
|
+
class CachedStuff
|
9
|
+
attr_reader :hexdigest, :resizing, :slicing
|
10
|
+
attr_accessor :changed, :time
|
11
|
+
|
12
|
+
def initialize(text = nil)
|
13
|
+
@changed = false
|
14
|
+
self.hexdigest_for = text if text
|
15
|
+
end
|
16
|
+
|
17
|
+
def hexdigest_for=(text)
|
18
|
+
hex = Digest::SHA1.hexdigest(text)
|
19
|
+
unless hex == @hexdigest
|
20
|
+
@changed = true
|
21
|
+
@hexdigest = hex
|
22
|
+
end
|
23
|
+
hex
|
24
|
+
end
|
25
|
+
|
26
|
+
def slicing=(object)
|
27
|
+
case object
|
28
|
+
when HtmlSlicer::Mappers::Slicing, nil then
|
29
|
+
@changed = true unless object.try(:options).try(:hexdigest) == @slicing.try(:options).try(:hexdigest)
|
30
|
+
@slicing = object
|
31
|
+
else
|
32
|
+
raise(TypeError, "HtmlSlicer::Mappers::Slicing or nil expected, '#{object.class}' passed")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def resizing=(object)
|
37
|
+
case object
|
38
|
+
when HtmlSlicer::Mappers::Resizing, nil then
|
39
|
+
@changed = true unless object.try(:options).try(:hexdigest) == @slicing.try(:options).try(:hexdigest)
|
40
|
+
@resizing = object
|
41
|
+
else
|
42
|
+
raise(TypeError, "HtmlSlicer::Mappers::Resizing or nil expected, '#{object.class}' passed")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid_text?(text)
|
47
|
+
Digest::SHA1.hexdigest(text) == @hexdigest
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_resizing_options?(options)
|
51
|
+
options.try(:hexdigest) == @resizing.try(:options).try(:hexdigest)
|
52
|
+
end
|
53
|
+
|
54
|
+
def valid_slicing_options?(options)
|
55
|
+
options.try(:hexdigest) == @slicing.try(:options).try(:hexdigest)
|
56
|
+
end
|
57
|
+
|
58
|
+
def changed?
|
59
|
+
@changed
|
60
|
+
end
|
61
|
+
|
62
|
+
# Serialize self, using Marshal and Base64 encoding
|
63
|
+
def to_dump
|
64
|
+
@time = Time.now
|
65
|
+
Base64.encode64(Marshal.dump(self))
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/lib/html_slicer/config.rb
CHANGED
@@ -39,7 +39,7 @@ module HtmlSlicer
|
|
39
39
|
|
40
40
|
# Config accessor for HtmlSlicer. Accepts argument as a +style+.
|
41
41
|
def self.config(style = nil)
|
42
|
-
eval("@config#{"_#{style}" if style}")
|
42
|
+
eval("@config#{"_#{style}" if style}") || raise("Config style '#{style}' is invalid.")
|
43
43
|
end
|
44
44
|
|
45
45
|
# need a Class for 3.0
|
@@ -57,6 +57,7 @@ module HtmlSlicer
|
|
57
57
|
config_accessor :left
|
58
58
|
config_accessor :right
|
59
59
|
config_accessor :param_name
|
60
|
+
config_accessor :cache_to
|
60
61
|
|
61
62
|
def slice # Ugly coding. Override Hash::slice method
|
62
63
|
config[:slice]
|
@@ -105,12 +105,18 @@ module HtmlSlicer
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
method_name = config.as||"#{attr_name}_slice"
|
108
|
-
|
108
|
+
config.cache_to = "#{method_name}_cache" if config.cache_to == true
|
109
|
+
class_eval do
|
109
110
|
define_method method_name do
|
110
111
|
var_name = "@_#{method_name}"
|
111
112
|
instance_variable_get(var_name)||instance_variable_set(var_name, HtmlSlicer::Interface.new(self, attr_name, config.config))
|
112
113
|
end
|
113
114
|
end
|
115
|
+
if config.cache_to && self.superclass == ActiveRecord::Base
|
116
|
+
before_save do
|
117
|
+
send(method_name).load!
|
118
|
+
end
|
119
|
+
end
|
114
120
|
end
|
115
121
|
|
116
122
|
end
|
@@ -1,3 +1,9 @@
|
|
1
|
+
require 'html_slicer/options'
|
2
|
+
require 'html_slicer/processor'
|
3
|
+
require 'html_slicer/cached_stuff'
|
4
|
+
require 'html_slicer/mappers/slicing'
|
5
|
+
require 'html_slicer/mappers/resizing'
|
6
|
+
|
1
7
|
module HtmlSlicer
|
2
8
|
|
3
9
|
# Interface code.
|
@@ -10,17 +16,75 @@ module HtmlSlicer
|
|
10
16
|
#
|
11
17
|
|
12
18
|
class Interface # General accessor instance
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
include HtmlSlicer::Mappers
|
20
|
+
|
21
|
+
attr_reader :options, :document, :current_slice, :cached_stuff
|
22
|
+
|
23
|
+
delegate :slicing, :resizing, :to => :cached_stuff
|
24
|
+
|
17
25
|
def initialize(env, method_name, options = {})
|
26
|
+
@env, @method_name = env, method_name
|
18
27
|
@options = options
|
19
|
-
@
|
28
|
+
@resizing_options = ResizingOptions.new(options[:resize]) if options[:resize]
|
29
|
+
@slicing_options = SlicingOptions.new(options[:slice]) if options[:slice]
|
20
30
|
@current_slice = 1
|
31
|
+
load!
|
32
|
+
end
|
33
|
+
|
34
|
+
# Getting source content
|
35
|
+
def source
|
36
|
+
case options[:processors].present?
|
37
|
+
when true then HtmlSlicer::Process.iterate(@env.send(@method_name), options[:processors])
|
38
|
+
else
|
39
|
+
@env.send(@method_name)
|
40
|
+
end
|
21
41
|
end
|
22
42
|
|
23
|
-
#
|
43
|
+
# Process initializer
|
44
|
+
def load!
|
45
|
+
text = source
|
46
|
+
@cached_stuff ||=
|
47
|
+
begin
|
48
|
+
if options[:cache_to] # Getting recorded hash dump
|
49
|
+
Marshal.load(Base64.decode64(@env.send(options[:cache_to])))
|
50
|
+
else
|
51
|
+
raise Exception
|
52
|
+
end
|
53
|
+
rescue Exception, TypeError, NoMethodError # New cache object otherwise
|
54
|
+
CachedStuff.new
|
55
|
+
end
|
56
|
+
if @document.blank? || !@cached_stuff.valid_text?(text) # Initialize new @document if not exist or content has been changed
|
57
|
+
@document = HTML::Document.new(text)
|
58
|
+
@cached_stuff.hexdigest_for = text
|
59
|
+
end
|
60
|
+
if @cached_stuff.changed? || !@cached_stuff.valid_resizing_options?(@resizing_options) # Initialize new resizing process if the content or options has been changed
|
61
|
+
if @resizing_options
|
62
|
+
@cached_stuff.resizing = Resizing.new(@document, @resizing_options)
|
63
|
+
else
|
64
|
+
@cached_stuff.resizing = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
if @cached_stuff.changed? || !@cached_stuff.valid_slicing_options?(@slicing_options) # Initialize new slicing process if the content or options has been changed
|
68
|
+
if @slicing_options
|
69
|
+
@cached_stuff.slicing = Slicing.new(@document, @slicing_options)
|
70
|
+
else
|
71
|
+
@cached_stuff.slicing = nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
if @cached_stuff.changed? # Serialize and dump the cache if any changes provided
|
75
|
+
@cached_stuff.changed = false
|
76
|
+
if options[:cache_to]
|
77
|
+
@env.send("#{options[:cache_to]}=", @cached_stuff.to_dump)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return number of slices.
|
83
|
+
def slice_number
|
84
|
+
sliced? ? slicing.slice_number : 1
|
85
|
+
end
|
86
|
+
|
87
|
+
# General slicing method. Passing the argument changes the +current_slice+.
|
24
88
|
def slice!(slice = nil)
|
25
89
|
raise(Exception, "Slicing unavailable!") unless sliced?
|
26
90
|
if slice.present?
|
@@ -32,10 +96,10 @@ module HtmlSlicer
|
|
32
96
|
end
|
33
97
|
self
|
34
98
|
end
|
35
|
-
|
36
|
-
# Textual representation according to a current slice.
|
99
|
+
|
37
100
|
def to_s
|
38
|
-
|
101
|
+
load!
|
102
|
+
view(document.root, @current_slice)
|
39
103
|
end
|
40
104
|
|
41
105
|
def inspect
|
@@ -45,10 +109,66 @@ module HtmlSlicer
|
|
45
109
|
def method_missing(*args, &block)
|
46
110
|
to_s.send(*args, &block)
|
47
111
|
end
|
112
|
+
|
113
|
+
# True if any HTML tag has been resized
|
114
|
+
def resized?
|
115
|
+
resizing ? resizing.map.any? : false
|
116
|
+
end
|
48
117
|
|
118
|
+
# True if any part of document has been sliced
|
119
|
+
def sliced?
|
120
|
+
slicing ? slicing.map.any? : false
|
121
|
+
end
|
122
|
+
|
49
123
|
# Return the current slice is a last or not?
|
50
124
|
def last_slice?
|
51
125
|
current_slice == slice_number
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Return a textual representation of the node including all children.
|
131
|
+
def view(node, slice)
|
132
|
+
slice = slice.to_i
|
133
|
+
case node
|
134
|
+
when HTML::Tag then
|
135
|
+
children_view = node.children.collect {|child| view(child, slice)}.compact.join
|
136
|
+
if resized?
|
137
|
+
resizing.resize_node(node)
|
138
|
+
end
|
139
|
+
if sliced?
|
140
|
+
if slicing.map.get(node, slice) || children_view.present?
|
141
|
+
if node.closing == :close
|
142
|
+
"</#{node.name}>"
|
143
|
+
else
|
144
|
+
s = "<#{node.name}"
|
145
|
+
node.attributes.each do |k,v|
|
146
|
+
s << " #{k}"
|
147
|
+
s << "=\"#{v}\"" if String === v
|
148
|
+
end
|
149
|
+
s << " /" if node.closing == :self
|
150
|
+
s << ">"
|
151
|
+
s += children_view
|
152
|
+
s << "</#{node.name}>" if node.closing != :self && !node.children.empty?
|
153
|
+
s
|
154
|
+
end
|
155
|
+
end
|
156
|
+
else
|
157
|
+
node.to_s
|
158
|
+
end
|
159
|
+
when HTML::Text then
|
160
|
+
if sliced?
|
161
|
+
if range = slicing.map.get(node, slice)
|
162
|
+
"#{node.content[Range.new(*range)]}#{slicing.options.text_break unless range.last == -1}"
|
163
|
+
end
|
164
|
+
else
|
165
|
+
node.to_s
|
166
|
+
end
|
167
|
+
when HTML::CDATA then
|
168
|
+
node.to_s
|
169
|
+
when HTML::Node then
|
170
|
+
node.children.collect {|child| view(child, slice)}.compact.join
|
171
|
+
end
|
52
172
|
end
|
53
173
|
|
54
174
|
end
|
@@ -1,17 +1,38 @@
|
|
1
1
|
module HtmlSlicer
|
2
|
-
module
|
2
|
+
module Mappers
|
3
3
|
|
4
|
-
class Resizing # Resizing engine
|
5
|
-
attr_reader :options
|
6
|
-
|
7
|
-
|
4
|
+
class Resizing # Resizing engine, generate and store slicing Map (hash)
|
5
|
+
attr_reader :options, :map
|
6
|
+
|
7
|
+
class Map < Hash #:nodoc:
|
8
|
+
include HtmlSlicer::Utilities::NodeIdent
|
9
|
+
|
10
|
+
def commit(node, width, height)
|
11
|
+
self[node_identify(node)] = [width, height]
|
12
|
+
end
|
13
|
+
def width(node)
|
14
|
+
self[node_identify(node)].try(:first)
|
15
|
+
end
|
16
|
+
def height(node)
|
17
|
+
self[node_identify(node)].try(:last)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(document, options)
|
8
22
|
raise(TypeError, "HTML::Document expected, '#{document.class}' passed") unless document.is_a?(HTML::Document)
|
9
|
-
|
10
|
-
|
23
|
+
raise(TypeError, "HtmlSlicer::Options expected, '#{options.class}' passed") unless options.is_a?(HtmlSlicer::Options)
|
24
|
+
@options = options
|
25
|
+
@map = Map.new
|
26
|
+
process!(document.root)
|
11
27
|
end
|
12
|
-
|
13
|
-
def
|
14
|
-
@
|
28
|
+
|
29
|
+
def resize_node(node)
|
30
|
+
if w = @map.width(node)
|
31
|
+
width(node) { w.to_s }
|
32
|
+
end
|
33
|
+
if h = @map.height(node)
|
34
|
+
height(node) { h.to_s }
|
35
|
+
end
|
15
36
|
end
|
16
37
|
|
17
38
|
private
|
@@ -19,9 +40,18 @@ module HtmlSlicer
|
|
19
40
|
include HtmlSlicer::Utilities::ParseNode
|
20
41
|
include HtmlSlicer::Utilities::NodeMatchExtension
|
21
42
|
|
22
|
-
def
|
43
|
+
def process!(root)
|
23
44
|
parse(root) do |node|
|
24
|
-
|
45
|
+
if node.is_a?(HTML::Tag) && resizeable?(node)
|
46
|
+
target_width = node.parent.is_a?(HTML::Tag) ? @map.width(node.parent)||@options.width : @options.width
|
47
|
+
if target_width.present? && node_width = width(node)
|
48
|
+
node_height = height(node)
|
49
|
+
if node_width > target_width
|
50
|
+
ratio = node_width.to_f/target_width
|
51
|
+
@map.commit(node, target_width, node_height ? (node_height/ratio).round : nil)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
25
55
|
end
|
26
56
|
end
|
27
57
|
|
@@ -31,7 +61,7 @@ module HtmlSlicer
|
|
31
61
|
|
32
62
|
def width(node)
|
33
63
|
values = []
|
34
|
-
if block_given?
|
64
|
+
if block_given? && node.attributes.has_key?("width")
|
35
65
|
node.attributes["width"] = yield
|
36
66
|
else
|
37
67
|
values << absolute_resolution(node.attributes["width"])
|
@@ -53,7 +83,7 @@ module HtmlSlicer
|
|
53
83
|
|
54
84
|
def height(node)
|
55
85
|
values = []
|
56
|
-
if block_given?
|
86
|
+
if block_given? && node.attributes.has_key?("height")
|
57
87
|
node.attributes["height"] = yield
|
58
88
|
else
|
59
89
|
values << absolute_resolution(node.attributes["height"])
|
@@ -73,19 +103,6 @@ module HtmlSlicer
|
|
73
103
|
values.compact.min
|
74
104
|
end
|
75
105
|
|
76
|
-
def resize!(node)
|
77
|
-
target_width = node.parent.is_a?(HTML::Tag) ? width(node.parent)||@options.width : nil
|
78
|
-
if target_width.present? && node_width = width(node)
|
79
|
-
node_height = height(node)
|
80
|
-
if node_width > target_width
|
81
|
-
ratio = node_width.to_f/target_width
|
82
|
-
width(node) { target_width.to_s }
|
83
|
-
height(node) { (node_height/ratio).round.to_s } if node_height
|
84
|
-
@did = true
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
106
|
def absolute_resolution(value)
|
90
107
|
(value.present? && value.last != "%") ? value.to_i : nil
|
91
108
|
end
|
@@ -1,19 +1,28 @@
|
|
1
1
|
module HtmlSlicer
|
2
|
-
module
|
2
|
+
module Mappers
|
3
3
|
|
4
|
-
class Slicing # Slicing engine
|
5
|
-
attr_reader :options, :map
|
4
|
+
class Slicing # Slicing engine, generate and store resizing Map (hash)
|
5
|
+
attr_reader :options, :map, :slice_number
|
6
|
+
|
7
|
+
class Map < Hash #:nodoc:
|
8
|
+
include HtmlSlicer::Utilities::NodeIdent
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def commit(node, number, value)
|
11
|
+
self[node_identify(node)] ||= {}
|
12
|
+
self[node_identify(node)].merge!(number => value)
|
13
|
+
end
|
14
|
+
def get(node, number)
|
15
|
+
self[node_identify(node)] ? self[node_identify(node)][number] : nil
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
def initialize(document, options)
|
20
|
+
raise(TypeError, "HTML::Document expected, '#{document.class}' passed") unless document.is_a?(HTML::Document)
|
21
|
+
raise(TypeError, "HtmlSlicer::Options expected, '#{options.class}' passed") unless options.is_a?(HtmlSlicer::Options)
|
22
|
+
@options = options
|
23
|
+
@map = Map.new
|
24
|
+
@slice_number = 1
|
25
|
+
@options.unit.is_a?(Hash) ? process_by_node!(document.root) : process_by_text!(document.root)
|
17
26
|
end
|
18
27
|
|
19
28
|
private
|
@@ -21,7 +30,7 @@ module HtmlSlicer
|
|
21
30
|
include HtmlSlicer::Utilities::ParseNode
|
22
31
|
include HtmlSlicer::Utilities::NodeMatchExtension
|
23
32
|
|
24
|
-
def
|
33
|
+
def process_by_text!(root)
|
25
34
|
units_count = 0
|
26
35
|
parse(root) do |node|
|
27
36
|
if node.is_a?(HTML::Text)
|
@@ -36,48 +45,48 @@ module HtmlSlicer
|
|
36
45
|
if units_count == @options.maximum
|
37
46
|
units_count = 0
|
38
47
|
index = complete!(content, match.end(0))
|
39
|
-
@map.
|
48
|
+
@map.commit(node, @slice_number, [last_index, index-1])
|
40
49
|
last_index = index
|
41
|
-
limited? ? raise(Exception) : @
|
50
|
+
limited? ? raise(Exception) : @slice_number += 1
|
42
51
|
else
|
43
52
|
index = match.end(0)
|
44
53
|
end
|
45
54
|
if units_count > 0
|
46
|
-
@map.
|
55
|
+
@map.commit(node, @slice_number, [last_index, -1])
|
47
56
|
end
|
48
57
|
end
|
49
58
|
rescue Exception
|
50
59
|
break
|
51
60
|
end
|
52
61
|
else
|
53
|
-
@map.
|
62
|
+
@map.commit(node, @slice_number, [0, -1])
|
54
63
|
end
|
55
64
|
else
|
56
|
-
@map.
|
65
|
+
@map.commit(node, @slice_number, true)
|
57
66
|
end
|
58
67
|
end
|
59
68
|
end
|
60
69
|
|
61
|
-
def
|
70
|
+
def process_by_node!(root)
|
62
71
|
units_count = 0
|
63
72
|
parse(root) do |node|
|
64
73
|
if node.is_a?(HTML::Text)
|
65
|
-
@map.
|
74
|
+
@map.commit(node, @slice_number, [0, -1])
|
66
75
|
else
|
67
|
-
@map.
|
76
|
+
@map.commit(node, @slice_number, true)
|
68
77
|
end
|
69
78
|
if node.match(@options.unit) && sliceable?(node)
|
70
79
|
units_count += 1
|
71
80
|
if units_count == @options.maximum
|
72
81
|
units_count = 0
|
73
|
-
limited? ? break : @
|
82
|
+
limited? ? break : @slice_number += 1
|
74
83
|
end
|
75
84
|
end
|
76
85
|
end
|
77
86
|
end
|
78
87
|
|
79
88
|
def limited?
|
80
|
-
@options.limit && slice_number >= @options.limit
|
89
|
+
@options.limit && @slice_number >= @options.limit
|
81
90
|
end
|
82
91
|
|
83
92
|
def sanitize_content!(node)
|
data/lib/html_slicer/options.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module HtmlSlicer
|
2
4
|
|
3
5
|
# Make options adjective and convinient for processing needs.
|
@@ -5,15 +7,26 @@ module HtmlSlicer
|
|
5
7
|
class Options #:nodoc:
|
6
8
|
# Superclass
|
7
9
|
attr_reader :only, :except
|
10
|
+
|
8
11
|
def initialize(options)
|
9
12
|
options ||= {}
|
10
13
|
@only = options[:only]
|
11
14
|
@except = options[:except]
|
12
15
|
end
|
16
|
+
|
17
|
+
def hexdigest
|
18
|
+
Digest::SHA1.hexdigest(string_value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def string_value
|
22
|
+
instance_variables.collect {|name| name.to_s + ":" + instance_variable_get(name).to_s}.join
|
23
|
+
end
|
24
|
+
|
13
25
|
end
|
14
26
|
|
15
|
-
class
|
27
|
+
class SlicingOptions < Options #:nodoc:
|
16
28
|
attr_reader :unit, :maximum, :complete, :text_break, :limit
|
29
|
+
|
17
30
|
def initialize(options)
|
18
31
|
super(options)
|
19
32
|
@unit = case options[:unit]
|
@@ -47,14 +60,17 @@ module HtmlSlicer
|
|
47
60
|
end
|
48
61
|
@text_break = options[:text_break]
|
49
62
|
end
|
63
|
+
|
50
64
|
end
|
51
65
|
|
52
|
-
class
|
66
|
+
class ResizingOptions < Options
|
53
67
|
attr_reader :width
|
68
|
+
|
54
69
|
def initialize(options)
|
55
70
|
super(options)
|
56
|
-
@width = options[:width]
|
71
|
+
@width = options[:width].to_i
|
57
72
|
end
|
73
|
+
|
58
74
|
end
|
59
75
|
|
60
76
|
end
|
@@ -5,11 +5,19 @@ module HtmlSlicer
|
|
5
5
|
def parse(node, &block)
|
6
6
|
node.children.each do |node|
|
7
7
|
yield node if block_given?
|
8
|
-
|
8
|
+
if node.is_a?(HTML::Tag)
|
9
|
+
parse(node, &block)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
15
|
+
module NodeIdent
|
16
|
+
def node_identify(node)
|
17
|
+
[node.line, node.position]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
module Deepcopy
|
14
22
|
# Return the 'deep' brand new copy of Hash or Array. All nested hashes/arrays rebuilded at the same way.
|
15
23
|
def deepcopy(object)
|
data/lib/html_slicer/version.rb
CHANGED
data/lib/html_slicer.rb
CHANGED
@@ -7,9 +7,6 @@ module HtmlSlicer
|
|
7
7
|
def self.load!
|
8
8
|
require 'html_slicer/utilities'
|
9
9
|
require 'html_slicer/config'
|
10
|
-
require 'html_slicer/options'
|
11
|
-
require 'html_slicer/processor'
|
12
|
-
require 'html_slicer/document'
|
13
10
|
require 'html_slicer/interface'
|
14
11
|
require 'html_slicer/engine'
|
15
12
|
require 'html_slicer/helpers/action_view_extension'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: html_slicer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -82,8 +82,8 @@ files:
|
|
82
82
|
- config/locales/html_slicer.yml
|
83
83
|
- html_slicer.gemspec
|
84
84
|
- lib/html_slicer.rb
|
85
|
+
- lib/html_slicer/cached_stuff.rb
|
85
86
|
- lib/html_slicer/config.rb
|
86
|
-
- lib/html_slicer/document.rb
|
87
87
|
- lib/html_slicer/engine.rb
|
88
88
|
- lib/html_slicer/helpers/action_view_extension.rb
|
89
89
|
- lib/html_slicer/helpers/slicer.rb
|
@@ -91,8 +91,8 @@ files:
|
|
91
91
|
- lib/html_slicer/helpers/tags.rb
|
92
92
|
- lib/html_slicer/installer.rb
|
93
93
|
- lib/html_slicer/interface.rb
|
94
|
-
- lib/html_slicer/
|
95
|
-
- lib/html_slicer/
|
94
|
+
- lib/html_slicer/mappers/resizing.rb
|
95
|
+
- lib/html_slicer/mappers/slicing.rb
|
96
96
|
- lib/html_slicer/models/active_record_extension.rb
|
97
97
|
- lib/html_slicer/options.rb
|
98
98
|
- lib/html_slicer/processor.rb
|
data/lib/html_slicer/document.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
require 'html_slicer/makers/slicing'
|
2
|
-
require 'html_slicer/makers/resizing'
|
3
|
-
|
4
|
-
module HtmlSlicer
|
5
|
-
|
6
|
-
class Document # Framework
|
7
|
-
include HtmlSlicer::Makers
|
8
|
-
|
9
|
-
attr_reader :options, :document, :slicing, :resizing
|
10
|
-
|
11
|
-
delegate :root, :to => :document
|
12
|
-
|
13
|
-
def initialize(env, method_name, options = {})
|
14
|
-
@options = options
|
15
|
-
@source = Proc.new do
|
16
|
-
if options[:processors].present?
|
17
|
-
HtmlSlicer::Process.iterate(env.send(method_name), options[:processors])
|
18
|
-
else
|
19
|
-
env.send(method_name)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
prepare!
|
23
|
-
end
|
24
|
-
|
25
|
-
# Resturn number of slices.
|
26
|
-
def slice_number
|
27
|
-
sliced? ? @slicing.slice_number : 1
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s(slice = 1)
|
31
|
-
prepare! if changed?
|
32
|
-
if sliced? && map = slicing.map[slice-1]
|
33
|
-
view(root, map, slicing.options.text_break)
|
34
|
-
else
|
35
|
-
root
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def inspect
|
40
|
-
to_s
|
41
|
-
end
|
42
|
-
|
43
|
-
def method_missing(*args, &block)
|
44
|
-
to_s.send(*args, &block)
|
45
|
-
end
|
46
|
-
|
47
|
-
def resized?
|
48
|
-
resizing.try(:did?)||false
|
49
|
-
end
|
50
|
-
|
51
|
-
def sliced?
|
52
|
-
slicing.present?
|
53
|
-
end
|
54
|
-
|
55
|
-
def changed?
|
56
|
-
@source_hash != @source.call.hash
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
# Return a textual representation of the node including all children assigned to a +map+.
|
62
|
-
def view(node, map = {}, text_break = nil)
|
63
|
-
case node
|
64
|
-
when HTML::Tag then
|
65
|
-
children_view = node.children.collect {|child| view(child, map, text_break)}.compact.join
|
66
|
-
if map[node.object_id] || children_view.present?
|
67
|
-
if node.closing == :close
|
68
|
-
"</#{node.name}>"
|
69
|
-
else
|
70
|
-
s = "<#{node.name}"
|
71
|
-
node.attributes.each do |k,v|
|
72
|
-
s << " #{k}"
|
73
|
-
s << "=\"#{v}\"" if String === v
|
74
|
-
end
|
75
|
-
s << " /" if node.closing == :self
|
76
|
-
s << ">"
|
77
|
-
s += children_view
|
78
|
-
s << "</#{node.name}>" if node.closing != :self && !node.children.empty?
|
79
|
-
s
|
80
|
-
end
|
81
|
-
end
|
82
|
-
when HTML::Text then
|
83
|
-
if range = map[node.object_id]
|
84
|
-
"#{node.content[range]}#{text_break unless range.last == -1}"
|
85
|
-
end
|
86
|
-
when HTML::CDATA then
|
87
|
-
node.to_s
|
88
|
-
when HTML::Node then
|
89
|
-
node.children.collect {|child| view(child, map, text_break)}.compact.join
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Mapping the original source.
|
94
|
-
def prepare!
|
95
|
-
@source_hash = @source.call.hash
|
96
|
-
@document = HTML::Document.new(@source.call)
|
97
|
-
if options[:resize]
|
98
|
-
@resizing = Resizing.new(document, options[:resize])
|
99
|
-
end
|
100
|
-
if options[:slice]
|
101
|
-
@slicing = Slicing.new(document, options[:slice])
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|