html_slicer 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|