html_slicer 0.0.4
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/.gitignore +5 -0
- data/CHANGELOG +16 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +336 -0
- data/Rakefile +1 -0
- data/app/views/html_slicer/_first_slice.html.erb +10 -0
- data/app/views/html_slicer/_first_slice.html.haml +8 -0
- data/app/views/html_slicer/_first_slice.html.slim +9 -0
- data/app/views/html_slicer/_gap.html.erb +7 -0
- data/app/views/html_slicer/_gap.html.haml +7 -0
- data/app/views/html_slicer/_gap.html.slim +8 -0
- data/app/views/html_slicer/_last_slice.html.erb +10 -0
- data/app/views/html_slicer/_last_slice.html.haml +8 -0
- data/app/views/html_slicer/_last_slice.html.slim +9 -0
- data/app/views/html_slicer/_next_slice.html.erb +10 -0
- data/app/views/html_slicer/_next_slice.html.haml +8 -0
- data/app/views/html_slicer/_next_slice.html.slim +9 -0
- data/app/views/html_slicer/_prev_slice.html.erb +10 -0
- data/app/views/html_slicer/_prev_slice.html.haml +8 -0
- data/app/views/html_slicer/_prev_slice.html.slim +9 -0
- data/app/views/html_slicer/_slice.html.erb +11 -0
- data/app/views/html_slicer/_slice.html.haml +9 -0
- data/app/views/html_slicer/_slice.html.slim +10 -0
- data/app/views/html_slicer/_slicer.html.erb +22 -0
- data/app/views/html_slicer/_slicer.html.haml +17 -0
- data/app/views/html_slicer/_slicer.html.slim +18 -0
- data/config/locales/html_slicer.yml +8 -0
- data/html_slicer.gemspec +27 -0
- data/lib/html_slicer/config.rb +85 -0
- data/lib/html_slicer/engine.rb +4 -0
- data/lib/html_slicer/helpers/action_view_extension.rb +92 -0
- data/lib/html_slicer/helpers/slicer.rb +184 -0
- data/lib/html_slicer/helpers/smart_params.rb +35 -0
- data/lib/html_slicer/helpers/tags.rb +100 -0
- data/lib/html_slicer/installer.rb +118 -0
- data/lib/html_slicer/interface.rb +105 -0
- data/lib/html_slicer/models/active_record_extension.rb +22 -0
- data/lib/html_slicer/options.rb +60 -0
- data/lib/html_slicer/processor.rb +46 -0
- data/lib/html_slicer/railtie.rb +15 -0
- data/lib/html_slicer/resizing.rb +46 -0
- data/lib/html_slicer/slicing.rb +101 -0
- data/lib/html_slicer/utilities.rb +132 -0
- data/lib/html_slicer/version.rb +3 -0
- data/lib/html_slicer.rb +23 -0
- metadata +105 -0
data/html_slicer.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "html_slicer/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "html_slicer"
|
8
|
+
s.version = HtmlSlicer::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Valery Kvon"]
|
11
|
+
s.email = ["addagger@gmail.com"]
|
12
|
+
s.homepage = %q{http://github.com/addagger/HtmlSlicer}
|
13
|
+
s.summary = %q{HTML text slicer}
|
14
|
+
s.description = %q{A "smart" way to slice HTMLsed text to pages, also it can optionally resize included "width/height" attributes of HTML tags like <iframe>, <object>, <img> etc.}
|
15
|
+
|
16
|
+
s.add_development_dependency "actionpack", ['>= 3.0.0']
|
17
|
+
|
18
|
+
s.rubyforge_project = "html_slicer"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
s.licenses = ['MIT']
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'active_support/configurable'
|
2
|
+
|
3
|
+
module HtmlSlicer
|
4
|
+
# Configures global settings for HtmlSlicer
|
5
|
+
#
|
6
|
+
# === Default global configuration options
|
7
|
+
#
|
8
|
+
# window = 4
|
9
|
+
# outer_window = 0
|
10
|
+
# left = 0
|
11
|
+
# right = 0
|
12
|
+
# param_name = :slice
|
13
|
+
#
|
14
|
+
# === Override/complete global configuration
|
15
|
+
#
|
16
|
+
# HtmlSlicer.configure do |config|
|
17
|
+
# config.slice = {:complete => /\s+|\z/, :maximum => 2000}
|
18
|
+
# config.resize = {:width => 300, :only => {:tag => 'iframe'}}
|
19
|
+
# config.window = 5
|
20
|
+
# config.param_name = :any_other_param_name
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# === Passing an argument (:symbol) creates stylized configuration, which can be used like that:
|
24
|
+
#
|
25
|
+
# HtmlSlicer.configure(:paged) do |config|
|
26
|
+
# config.as = :page
|
27
|
+
# config.param_name = :page
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# class Post < ActiveRecord::Base
|
31
|
+
# slice :content, :config => :paged
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# * Missing required parameters pick up automatically from global configuration set before.
|
35
|
+
#
|
36
|
+
def self.configure(style = nil, &block)
|
37
|
+
yield eval("@config#{"_#{style}" if style} ||= #{style ? "@config.duplicate" : "Configuration.new"}")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Config accessor for HtmlSlicer. Accepts argument as a +style+.
|
41
|
+
def self.config(style = nil)
|
42
|
+
eval("@config#{"_#{style}" if style}")
|
43
|
+
end
|
44
|
+
|
45
|
+
# need a Class for 3.0
|
46
|
+
class Configuration #:nodoc:
|
47
|
+
|
48
|
+
include ActiveSupport::Configurable
|
49
|
+
include HtmlSlicer::Utilities::Deepcopy
|
50
|
+
|
51
|
+
config_accessor :as
|
52
|
+
config_accessor :slice
|
53
|
+
config_accessor :resize
|
54
|
+
config_accessor :processors
|
55
|
+
config_accessor :window
|
56
|
+
config_accessor :outer_window
|
57
|
+
config_accessor :left
|
58
|
+
config_accessor :right
|
59
|
+
config_accessor :param_name
|
60
|
+
|
61
|
+
def slice # Ugly coding. Override Hash::slice method
|
62
|
+
config[:slice]
|
63
|
+
end
|
64
|
+
|
65
|
+
def param_name
|
66
|
+
config.param_name.respond_to?(:call) ? config.param_name.call : config.param_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def duplicate
|
70
|
+
dup = Configuration.new
|
71
|
+
dup.config.replace(deepcopy(config))
|
72
|
+
dup
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
configure do |config| # Setup default global configuration
|
78
|
+
config.window = 4
|
79
|
+
config.outer_window = 0
|
80
|
+
config.left = 0
|
81
|
+
config.right = 0
|
82
|
+
config.param_name = :slice
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# This part of code is almost completely ported from +Kaminari+ gem by Akira Matsuda.
|
2
|
+
# Look at http://github.com/amatsuda/kaminari/tree/master/lib/kaminari/helpers
|
3
|
+
module HtmlSlicer
|
4
|
+
|
5
|
+
# The part of code, processing the +:param_name+ was rewritten by me.
|
6
|
+
# Now you can define +:param_name+ as a +symbol+ or +string+, or as an +array of any object that responses +.to_s+ method and returns +string+.
|
7
|
+
# Passing +array+ is the way to define nested :param_name.
|
8
|
+
#
|
9
|
+
# === Examples:
|
10
|
+
#
|
11
|
+
# :param_name => :page
|
12
|
+
# # means you define params[:page] as a slice key.
|
13
|
+
#
|
14
|
+
# :param_name => [:article, :page]
|
15
|
+
# # means you define params[:article][:page] as a slice key.
|
16
|
+
#
|
17
|
+
|
18
|
+
module ActionViewExtension
|
19
|
+
# A helper that renders the pagination links.
|
20
|
+
#
|
21
|
+
# <%= slicer @article.paged %>
|
22
|
+
#
|
23
|
+
# ==== Options
|
24
|
+
# * <tt>:window</tt> - The "inner window" size (4 by default).
|
25
|
+
# * <tt>:outer_window</tt> - The "outer window" size (0 by default).
|
26
|
+
# * <tt>:left</tt> - The "left outer window" size (0 by default).
|
27
|
+
# * <tt>:right</tt> - The "right outer window" size (0 by default).
|
28
|
+
# * <tt>:params</tt> - url_for parameters for the links (:controller, :action, etc.)
|
29
|
+
# * <tt>:param_name</tt> - parameter name for slice number in the links. Accepts +symbol+, +string+, +array+.
|
30
|
+
# * <tt>:remote</tt> - Ajax? (false by default)
|
31
|
+
# * <tt>:ANY_OTHER_VALUES</tt> - Any other hash key & values would be directly passed into each tag as :locals value.
|
32
|
+
def slice(object, options = {}, &block)
|
33
|
+
slicer = HtmlSlicer::Helpers::Slicer.new self, object.options.reverse_merge(options).reverse_merge(:current_slice => object.current_slice, :slice_number => object.slice_number, :remote => false)
|
34
|
+
slicer.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
# A simple "Twitter like" pagination link that creates a link to the next slice.
|
38
|
+
#
|
39
|
+
# ==== Examples
|
40
|
+
# Basic usage:
|
41
|
+
#
|
42
|
+
# <%= link_to_next_slice @article.paged, 'Next page' %>
|
43
|
+
#
|
44
|
+
# Ajax:
|
45
|
+
#
|
46
|
+
# <%= link_to_next_slice @article.paged, 'Next page', :remote => true %>
|
47
|
+
#
|
48
|
+
# By default, it renders nothing if there are no more results on the next slice.
|
49
|
+
# You can customize this output by passing a block.
|
50
|
+
#
|
51
|
+
# <%= link_to_next_slice @article.paged, 'Next page' do %>
|
52
|
+
# <span>No More slices</span>
|
53
|
+
# <% end %>
|
54
|
+
def link_to_next_slice(object, name, options = {}, &block)
|
55
|
+
params = options[:params] ? self.params.merge(options.delete :params) : self.params
|
56
|
+
param_name = options.delete(:param_name) || object.options.param_name
|
57
|
+
link_to_unless object.last_slice?, name, HtmlSlicer::SmartParams.new(params, param_name, (object.current_slice + 1)), options.reverse_merge(:rel => 'next') do
|
58
|
+
block.call if block
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Renders a helpful message with numbers of displayed vs. total entries.
|
63
|
+
# Ported from mislav/will_paginate
|
64
|
+
#
|
65
|
+
# ==== Examples
|
66
|
+
# Basic usage:
|
67
|
+
#
|
68
|
+
# <%= slice_entries_info @article.paged %>
|
69
|
+
# #-> Displaying paged 6 of 26
|
70
|
+
#
|
71
|
+
# By default, the message will use the stringified +method_name (+:as+ option)+ implemented as slicer method.
|
72
|
+
# Override this with the <tt>:entry_name</tt> parameter:
|
73
|
+
#
|
74
|
+
# <%= slice_entries_info @article.paged, :entry_name => 'page' %>
|
75
|
+
# #-> Displaying page 6 of 26
|
76
|
+
def slice_entries_info(object, options = {})
|
77
|
+
entry_name = options[:entry_name] || object.options.as
|
78
|
+
output = ""
|
79
|
+
if object.slice_number < 2
|
80
|
+
output = case object.slice_number
|
81
|
+
when 0 then "No #{entry_name} found"
|
82
|
+
when 1 then "Displaying <b>1</b> #{entry_name}"
|
83
|
+
else; "Displaying <b>all #{object.slice_number}</b> #{entry_name.to_s.pluralize}"
|
84
|
+
end
|
85
|
+
else
|
86
|
+
output = %{Displaying #{entry_name} <b>#{object.current_slice}</b> of <b>#{object.slice_number}</b>}
|
87
|
+
end
|
88
|
+
output.html_safe
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# This part of code is almost completely ported from +Kaminari+ gem by Akira Matsuda.
|
2
|
+
# Look at http://github.com/amatsuda/kaminari/tree/master/lib/kaminari/helpers
|
3
|
+
|
4
|
+
require 'active_support/inflector'
|
5
|
+
require 'action_view'
|
6
|
+
require 'action_view/log_subscriber'
|
7
|
+
require 'action_view/context'
|
8
|
+
require 'html_slicer/helpers/smart_params'
|
9
|
+
require 'html_slicer/helpers/tags'
|
10
|
+
|
11
|
+
module HtmlSlicer
|
12
|
+
|
13
|
+
module Helpers
|
14
|
+
# The main container tag
|
15
|
+
|
16
|
+
# Configure ActiveSupport inflections to pluralize 'slice' in a correct way = 'slices'. # By default would be 'slouse'.
|
17
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
18
|
+
inflect.plural 'slice', 'slices'
|
19
|
+
inflect.singular 'slice', 'slice'
|
20
|
+
end
|
21
|
+
|
22
|
+
class Slicer < Tag
|
23
|
+
# so that this instance can actually "render"
|
24
|
+
include ::ActionView::Context
|
25
|
+
|
26
|
+
def initialize(template, options) #:nodoc:
|
27
|
+
@window_options = {}.tap do |h|
|
28
|
+
h[:window] = options.delete(:window) || options.delete(:inner_window)
|
29
|
+
outer_window = options.delete(:outer_window)
|
30
|
+
h[:left] = options.delete(:left)
|
31
|
+
h[:left] = outer_window if h[:left] == 0
|
32
|
+
h[:right] = options.delete(:right)
|
33
|
+
h[:right] = outer_window if h[:right] == 0
|
34
|
+
end
|
35
|
+
@template, @options = template, options
|
36
|
+
@theme = @options[:theme] ? "#{@options[:theme]}/" : ''
|
37
|
+
@options[:current_slice] = SliceProxy.new @window_options.merge(@options), @options[:current_slice], nil
|
38
|
+
# initialize the output_buffer for Context
|
39
|
+
@output_buffer = ActionView::OutputBuffer.new
|
40
|
+
end
|
41
|
+
|
42
|
+
# render given block as a view template
|
43
|
+
def render(&block)
|
44
|
+
instance_eval &block if @options[:slice_number] > 1
|
45
|
+
@output_buffer
|
46
|
+
end
|
47
|
+
|
48
|
+
# enumerate each slice providing sliceProxy object as the block parameter
|
49
|
+
# Because of performance reason, this doesn't actually enumerate all slices but slices that are seemingly relevant to the paginator.
|
50
|
+
# "Relevant" slices are:
|
51
|
+
# * slices inside the left outer window plus one for showing the gap tag
|
52
|
+
# * slices inside the inner window plus one on the left plus one on the right for showing the gap tags
|
53
|
+
# * slices inside the right outer window plus one for showing the gap tag
|
54
|
+
def each_relevant_slice
|
55
|
+
return to_enum(:each_relevant_slice) unless block_given?
|
56
|
+
|
57
|
+
relevant_slices(@window_options.merge(@options)).each do |i|
|
58
|
+
yield SliceProxy.new(@window_options.merge(@options), i, @last)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias each_slice each_relevant_slice
|
62
|
+
|
63
|
+
def relevant_slices(options)
|
64
|
+
left_window_plus_one = 1.upto(options[:left] + 1).to_a
|
65
|
+
right_window_plus_one = (options[:slice_number] - options[:right]).upto(options[:slice_number]).to_a
|
66
|
+
inside_window_plus_each_sides = (options[:current_slice] - options[:window] - 1).upto(options[:current_slice] + options[:window] + 1).to_a
|
67
|
+
|
68
|
+
(left_window_plus_one + inside_window_plus_each_sides + right_window_plus_one).uniq.sort.reject {|x| (x < 1) || (x > options[:slice_number])}
|
69
|
+
end
|
70
|
+
private :relevant_slices
|
71
|
+
|
72
|
+
def slice_tag(slice)
|
73
|
+
@last = Slice.new @template, @options.merge(:slice => slice)
|
74
|
+
end
|
75
|
+
|
76
|
+
%w[first_slice prev_slice next_slice last_slice gap].each do |tag|
|
77
|
+
eval <<-DEF
|
78
|
+
def #{tag}_tag
|
79
|
+
@last = #{tag.classify}.new @template, @options
|
80
|
+
end
|
81
|
+
DEF
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s #:nodoc:
|
85
|
+
subscriber = ActionView::LogSubscriber.log_subscribers.detect {|ls| ls.is_a? ActionView::LogSubscriber}
|
86
|
+
return super @window_options.merge(@options).merge :slicer => self unless subscriber
|
87
|
+
|
88
|
+
# dirty hack to suppress logging render_partial
|
89
|
+
class << subscriber
|
90
|
+
alias_method :render_partial_with_logging, :render_partial
|
91
|
+
# do nothing
|
92
|
+
def render_partial(event); end
|
93
|
+
end
|
94
|
+
|
95
|
+
ret = super @window_options.merge(@options).merge :slicer => self
|
96
|
+
|
97
|
+
class << subscriber
|
98
|
+
alias_method :render_partial, :render_partial_with_logging
|
99
|
+
undef :render_partial_with_logging
|
100
|
+
end
|
101
|
+
ret
|
102
|
+
end
|
103
|
+
|
104
|
+
# Wraps a "slice number" and provides some utility methods
|
105
|
+
class SliceProxy
|
106
|
+
include Comparable
|
107
|
+
|
108
|
+
def initialize(options, slice, last) #:nodoc:
|
109
|
+
@options, @slice, @last = options, slice, last
|
110
|
+
end
|
111
|
+
|
112
|
+
# the slice number
|
113
|
+
def number
|
114
|
+
@slice
|
115
|
+
end
|
116
|
+
|
117
|
+
# current slice or not
|
118
|
+
def current?
|
119
|
+
@slice == @options[:current_slice]
|
120
|
+
end
|
121
|
+
|
122
|
+
# the first slice or not
|
123
|
+
def first?
|
124
|
+
@slice == 1
|
125
|
+
end
|
126
|
+
|
127
|
+
# the last slice or not
|
128
|
+
def last?
|
129
|
+
@slice == @options[:slice_number]
|
130
|
+
end
|
131
|
+
|
132
|
+
# the previous slice or not
|
133
|
+
def prev?
|
134
|
+
@slice == @options[:current_slice] - 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# the next slice or not
|
138
|
+
def next?
|
139
|
+
@slice == @options[:current_slice] + 1
|
140
|
+
end
|
141
|
+
|
142
|
+
# within the left outer window or not
|
143
|
+
def left_outer?
|
144
|
+
@slice <= @options[:left]
|
145
|
+
end
|
146
|
+
|
147
|
+
# within the right outer window or not
|
148
|
+
def right_outer?
|
149
|
+
@options[:slice_number] - @slice < @options[:right]
|
150
|
+
end
|
151
|
+
|
152
|
+
# inside the inner window or not
|
153
|
+
def inside_window?
|
154
|
+
(@options[:current_slice] - @slice).abs <= @options[:window]
|
155
|
+
end
|
156
|
+
|
157
|
+
# The last rendered tag was "truncated" or not
|
158
|
+
def was_truncated?
|
159
|
+
@last.is_a? Gap
|
160
|
+
end
|
161
|
+
|
162
|
+
def to_i
|
163
|
+
number
|
164
|
+
end
|
165
|
+
|
166
|
+
def to_s
|
167
|
+
number.to_s
|
168
|
+
end
|
169
|
+
|
170
|
+
def +(other)
|
171
|
+
to_i + other.to_i
|
172
|
+
end
|
173
|
+
|
174
|
+
def -(other)
|
175
|
+
to_i - other.to_i
|
176
|
+
end
|
177
|
+
|
178
|
+
def <=>(other)
|
179
|
+
to_i <=> other.to_i
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module HtmlSlicer
|
2
|
+
|
3
|
+
class SmartParams < Hash
|
4
|
+
# Implements smart and flexible +params+ merging.
|
5
|
+
# Method accepts passed +params+ hash and merge it with a new +param_name+ and it's value.
|
6
|
+
# In the case when you passed +param_name+ option as an Array, method returns merged new
|
7
|
+
# instance of hashed params where all subhashes merged into the same way.
|
8
|
+
#
|
9
|
+
# === Example:
|
10
|
+
#
|
11
|
+
# params = {:controller => "comments", :action => "show", :id => 34, :article_id => 3, :page => {:article => 2}}
|
12
|
+
#
|
13
|
+
# :slice_params => [:page, :comment]
|
14
|
+
#
|
15
|
+
# HtmlSlicer::SmartParams.new(params, slice_params, 34)
|
16
|
+
# # => {:controller => "comments", :action => "show", :id => 34, :article_id => 3, :page => {:article => 2, :comment => 34}}
|
17
|
+
#
|
18
|
+
def initialize(params = {}, param_name = nil, value = nil)
|
19
|
+
super()
|
20
|
+
param_subhash = case param_name
|
21
|
+
when Array then hashup(param_name.collect {|e| e.to_s} << value)
|
22
|
+
when String, Symbol then {param_name.to_s => value}
|
23
|
+
else {}
|
24
|
+
end
|
25
|
+
update(nested_merge(params, param_subhash))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
include HtmlSlicer::Utilities::HashupArray
|
31
|
+
include HtmlSlicer::Utilities::NestedMergeHash
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# This part of code is almost completely ported from +Kaminari+ gem by Akira Matsuda.
|
2
|
+
# Look at http://github.com/amatsuda/kaminari/tree/master/lib/kaminari/helpers
|
3
|
+
module HtmlSlicer
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
# A tag stands for an HTML tag inside the paginator.
|
7
|
+
# Basically, a tag has its own partial template file, so every tag can be
|
8
|
+
# rendered into String using its partial template.
|
9
|
+
#
|
10
|
+
# The template file should be placed in your app/views/html_slicer/ directory
|
11
|
+
# with underscored class name (besides the "Tag" class. Tag is an abstract
|
12
|
+
# class, so _tag parital is not needed).
|
13
|
+
# e.g.) PrevLink -> app/views/html_slicer/_prev_link.html.erb
|
14
|
+
#
|
15
|
+
# When no matching template were found in your app, the engine's pre
|
16
|
+
# installed template will be used.
|
17
|
+
# e.g.) Paginator -> $GEM_HOME/html_slicer-x.x.x/app/views/html_slicer/_paginator.html.erb
|
18
|
+
|
19
|
+
class Tag
|
20
|
+
def initialize(template, options = {}) #:nodoc:
|
21
|
+
@template, @options = template, options.dup
|
22
|
+
@param_name = @options.delete(:param_name)
|
23
|
+
@theme = @options[:theme] ? "#{@options.delete(:theme)}/" : ''
|
24
|
+
@params = @options[:params] ? template.params.merge(@options.delete :params) : template.params
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s(locals = {}) #:nodoc:
|
28
|
+
@template.render :partial => "html_slicer/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals)
|
29
|
+
end
|
30
|
+
|
31
|
+
def slice_url_for(slice)
|
32
|
+
# +HtmlSlicer::SmartParams+: return deep merged params with a new slice number value.
|
33
|
+
@template.url_for HtmlSlicer::SmartParams.new(@params, @param_name, (slice <= 1 ? nil : slice))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Tag that contains a link
|
38
|
+
module Link
|
39
|
+
# target slice number
|
40
|
+
def slice
|
41
|
+
raise 'Override slice with the actual slice value to be a slice.'
|
42
|
+
end
|
43
|
+
# the link's href
|
44
|
+
def url
|
45
|
+
slice_url_for slice
|
46
|
+
end
|
47
|
+
def to_s(locals = {}) #:nodoc:
|
48
|
+
super locals.merge(:url => url)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# A slice
|
53
|
+
class Slice < Tag
|
54
|
+
include Link
|
55
|
+
# target slice number
|
56
|
+
def slice
|
57
|
+
@options[:slice]
|
58
|
+
end
|
59
|
+
def to_s(locals = {}) #:nodoc:
|
60
|
+
super locals.merge(:slice => slice)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Link with slice number that appears at the leftmost
|
65
|
+
class FirstSlice < Tag
|
66
|
+
include Link
|
67
|
+
def slice #:nodoc:
|
68
|
+
1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Link with slice number that appears at the rightmost
|
73
|
+
class LastSlice < Tag
|
74
|
+
include Link
|
75
|
+
def slice #:nodoc:
|
76
|
+
@options[:slice_number]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# The "previous" slice of the current slice
|
81
|
+
class PrevSlice < Tag
|
82
|
+
include Link
|
83
|
+
def slice #:nodoc:
|
84
|
+
@options[:current_slice] - 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# The "next" slice of the current slice
|
89
|
+
class NextSlice < Tag
|
90
|
+
include Link
|
91
|
+
def slice #:nodoc:
|
92
|
+
@options[:current_slice] + 1
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Non-link tag that stands for skipped slices...
|
97
|
+
class Gap < Tag
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module HtmlSlicer
|
2
|
+
|
3
|
+
module Installer
|
4
|
+
|
5
|
+
# The basic implementation method.
|
6
|
+
#
|
7
|
+
# slice <method_name>, <configuration>, [:config => <:style>]*
|
8
|
+
#
|
9
|
+
# where:
|
10
|
+
# * <method_name> - any method or local variable which returns source String (can be called with .send()).
|
11
|
+
# * <configuration> - Hash of configuration options and/or +:config+ parameter.
|
12
|
+
#
|
13
|
+
# === Example:
|
14
|
+
#
|
15
|
+
# class Article < ActiveRecord::Base
|
16
|
+
# slice :content, :as => :paged, :slice => {:maximum => 2000}, :resize => {:width => 600}
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# === Where:
|
20
|
+
# * <tt>:content</tt> is a method name or local variable, that return a target String object.
|
21
|
+
# * <tt>:as</tt> is a name of basic accessor for result.
|
22
|
+
# * <tt>:slice</tt> is a hash of +slicing options+.
|
23
|
+
# * <tt>:resize</tt> is a hash of +resizing options+.
|
24
|
+
#
|
25
|
+
# You can define any key of configuration you want. Otherwise, default configuration
|
26
|
+
# will be picked up automatically.
|
27
|
+
#
|
28
|
+
# === All configuration keys:
|
29
|
+
# * <tt>:as</tt> is a name of basic accessor for sliced +object+.
|
30
|
+
# * <tt>:slice</tt> is a hash of slicing options*.
|
31
|
+
# * <tt>:resize</tt> is a hash of resizing options*.
|
32
|
+
# * <tt>:processors</tt> - processors names*.
|
33
|
+
# * <tt>:window</tt> - parameter for ActionView: The "inner window" size (4 by default).
|
34
|
+
# * <tt>:outer_window</tt> - parameter for ActionView: The "outer window" size (0 by default).
|
35
|
+
# * <tt>:left</tt> - parameter for ActionView: The "left outer window" size (0 by default).
|
36
|
+
# * <tt>:right</tt> - parameter for ActionView: The "right outer window" size (0 by default).
|
37
|
+
# * <tt>:params</tt> - parameter for ActionView: url_for parameters for the links (:controller, :action, etc.)
|
38
|
+
# * <tt>:param_name</tt> - parameter for ActionView: parameter name for slice number in the links. Accepts +symbol+, +string+, +array+.
|
39
|
+
# * <tt>:remote</tt> - parameter for ActionView: Ajax? (false by default)
|
40
|
+
#
|
41
|
+
# === Block-style configuration example:
|
42
|
+
#
|
43
|
+
# slice *args do |config|
|
44
|
+
# config.as = :paged
|
45
|
+
# config.slice.merge! {:maximum => 1500}
|
46
|
+
# config.resize = nil
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # *args = method name or local variable, and/or +:config+ parameter.
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# === Premature configuration (+:config+ parameter):
|
53
|
+
# Stylizied general configuration can be used for many implementations, such as:
|
54
|
+
#
|
55
|
+
# # For example, we set the global stylized config:
|
56
|
+
#
|
57
|
+
# HtmlSlicer.configure(:paged_config) do |config|
|
58
|
+
# config.as = :page
|
59
|
+
# config.slice = {:maximum => 300}
|
60
|
+
# config.window = 4
|
61
|
+
# config.outer_window = 0
|
62
|
+
# config.left = 0
|
63
|
+
# config.right = 0
|
64
|
+
# config.param_name = :slice
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # Now we can use it as next:
|
68
|
+
#
|
69
|
+
# slice *args, :config => :paged_config
|
70
|
+
#
|
71
|
+
# You can also pass another configuration options directrly as arguments
|
72
|
+
# and/or the block to clarify the config along with used global:
|
73
|
+
#
|
74
|
+
# slice *args, :as => :chapter, :config => :paged_config do |config|
|
75
|
+
# config.slice.merge! {:unit => {:tag => 'h1', :class => 'chapter'}, :maximum => 1}
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# === Skipping slicing:
|
79
|
+
#
|
80
|
+
# To skip slicing (for example, if you want to use only resizing feature)
|
81
|
+
# you can nilify +slice+ option at all:
|
82
|
+
#
|
83
|
+
# slice :content, :slice => nil, :resize => {:width => 300}
|
84
|
+
#
|
85
|
+
# Notice: without +:slice+ neither +:resize+ options, using HtmlSlicer becomes meaningless. :)
|
86
|
+
#
|
87
|
+
# === See README.rdoc for details about +:slice+ and +:resize+ options etc.
|
88
|
+
#
|
89
|
+
def slice(*args, &block)
|
90
|
+
attr_name = args.first
|
91
|
+
raise(NameError, "Attribute name expected!") unless attr_name
|
92
|
+
|
93
|
+
options = args.extract_options!
|
94
|
+
config = HtmlSlicer.config(options.delete(:config)).duplicate # Configuration instance for each single one implementation
|
95
|
+
if options.present? # Accepts options from args
|
96
|
+
options.each do |key, value|
|
97
|
+
config.send("#{key}=", value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
if block_given? # Accepts options from block
|
101
|
+
yield config
|
102
|
+
end
|
103
|
+
if config.processors
|
104
|
+
Array.wrap(config.processors).each do |name|
|
105
|
+
HtmlSlicer.load_processor!(name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
method_name = config.as||"#{attr_name}_slice"
|
109
|
+
class_exec do
|
110
|
+
define_method method_name do
|
111
|
+
var_name = "@_#{method_name}"
|
112
|
+
instance_variable_get(var_name)||instance_variable_set(var_name, HtmlSlicer::Interface.new(send(attr_name), config.config))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|