actionset 0.2.1 → 0.3.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: 5026afa544f9def59209497c089522877b6ab900
4
- data.tar.gz: 3bfbd8ff3930d022ac45e8680390ad9a42c4406c
3
+ metadata.gz: 27ec348f94bbc668c2b2b0488ae39f551aa4eb07
4
+ data.tar.gz: 926fda73f52119dd20c4e355ff0a501d25238f54
5
5
  SHA512:
6
- metadata.gz: f78f95e0cf017f4a22dd8339cfb2338d4ca8c01c97a3d68d7f7200895e74572073be94bc1756fc115cee336660738f8988354275e794d5280b6c70028ecddf0a
7
- data.tar.gz: e165d731e37993c0a92a3d407642d7823dbaaf5414ae137a5d11fbdf1bb64bdfdb7c859c6c1cfae11acfcc596dc852b5957d336137dfa8330fec22b06d3b9bfd
6
+ metadata.gz: bdcadbe61ad8a0b6209015df5e9c0302ee6923d2aa33782b9baa75873650c457ac8e092513d5f2dd9dbc33a07f8ab6bc60a3f79121532bd8e429c4aa8098dc0e
7
+ data.tar.gz: f503e4ae5a77fc6a435b09519e1f26d09400e64e4b1d5d0354b7b7b6394400cbf8ee9c7d2419970919dbc6e3cb3496f982c3c0eef1f6f29e7da3cec26997688a
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ v 0.3.0
2
+ - Add a `paginate_set` method that is a part of the `process_set` chain now
3
+ - Add `paginate` view helper for rendering a pagination navigation
1
4
  v 0.2.1
2
5
  - Extract the `set_filters_ivar` method so that app controllers can use it in a before action for filter forms that are present on multiple actions
3
6
  v 0.2.0
@@ -7,13 +7,13 @@ require 'ostruct'
7
7
 
8
8
  require 'action_set/version'
9
9
  require_relative './action_set/instructions/entry_value'
10
- require_relative './action_set/view_helpers'
10
+ require_relative './action_set/helpers/helper_methods'
11
11
 
12
12
  module ActionSet
13
13
  class Railtie < ::Rails::Railtie
14
14
  initializer 'action_set.view_helpers' do
15
15
  ActiveSupport.on_load :action_view do
16
- include ViewHelpers
16
+ include Helpers::HelperMethods
17
17
  end
18
18
  end
19
19
  end
@@ -30,25 +30,31 @@ module ActionSet
30
30
  module InstanceMethods
31
31
  def process_set(set)
32
32
  @set = set
33
- sort_set(filter_set(ActiveSet.new(set)))
33
+ paginate_set(sort_set(filter_set(ActiveSet.new(set))))
34
34
  end
35
35
 
36
36
  def filter_set(set)
37
37
  set_filters_ivar
38
- active_set = set.is_a?(ActiveSet) ? set : ActiveSet.new(set)
38
+ active_set = ensure_active_set(set)
39
39
  active_set = active_set.filter(filter_structure) if filter_params.any?
40
40
  active_set
41
41
  end
42
42
 
43
43
  def sort_set(set)
44
- active_set = set.is_a?(ActiveSet) ? set : ActiveSet.new(set)
44
+ active_set = ensure_active_set(set)
45
45
  active_set = active_set.sort(sort_params) if sort_params.any?
46
46
  active_set
47
47
  end
48
48
 
49
+ def paginate_set(set)
50
+ active_set = ensure_active_set(set)
51
+ active_set = active_set.paginate(paginate_structure) if paginate_params.any?
52
+ active_set
53
+ end
54
+
49
55
  def export_set(set)
50
56
  return send_file(set, export_set_options(request.format)) if set.is_a?(String) && File.file?(set)
51
- active_set = set.is_a?(ActiveSet) ? set : ActiveSet.new(set)
57
+ active_set = ensure_active_set(set)
52
58
  transformed_data = active_set.transform(transform_structure)
53
59
  send_data(transformed_data, export_set_options(request.format))
54
60
  end
@@ -60,10 +66,6 @@ module ActionSet
60
66
 
61
67
  private
62
68
 
63
- def filter_params
64
- params.fetch(:filter, {}).to_unsafe_hash
65
- end
66
-
67
69
  def filter_structure
68
70
  filter_params.flatten_keys.reject { |_, v| v.blank? }.each_with_object({}) do |(keypath, value), memo|
69
71
  instruction = ActiveSet::Instructions::Entry.new(keypath, value)
@@ -75,6 +77,10 @@ module ActionSet
75
77
  end
76
78
  end
77
79
 
80
+ def paginate_structure
81
+ paginate_params.transform_values(&:to_i)
82
+ end
83
+
78
84
  def transform_structure
79
85
  {}.tap do |struct|
80
86
  struct[:format] = transform_params[:format] || request.format.symbol
@@ -90,10 +96,18 @@ module ActionSet
90
96
  end
91
97
  end
92
98
 
99
+ def filter_params
100
+ params.fetch(:filter, {}).to_unsafe_hash
101
+ end
102
+
93
103
  def sort_params
94
104
  params.fetch(:sort, {}).to_unsafe_hash
95
105
  end
96
106
 
107
+ def paginate_params
108
+ params.fetch(:paginate, {}).to_unsafe_hash
109
+ end
110
+
97
111
  def transform_params
98
112
  params.fetch(:transform, {}).to_unsafe_hash
99
113
  end
@@ -105,6 +119,12 @@ module ActionSet
105
119
  opts[:disposition] = :inline if %w[development test].include?(Rails.env.to_s)
106
120
  end
107
121
  end
122
+
123
+ def ensure_active_set(set)
124
+ return set if set.is_a?(ActiveSet)
125
+
126
+ ActiveSet.new(set)
127
+ end
108
128
  end
109
129
 
110
130
  def self.included(receiver)
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './sort_linker'
4
+ require_relative './paginator'
5
+
6
+ module ActionSet
7
+ module Helpers
8
+ module HelperMethods
9
+ def sort_link(column, title)
10
+ Helpers::SortLinker.new(self).render(column, title)
11
+ end
12
+
13
+ def paginate(set)
14
+ Helpers::Paginator.new(self).render(set)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionSet
4
+ module Helpers
5
+ class Paginator
6
+ def initialize(template)
7
+ @template = template
8
+ @info = {}
9
+ end
10
+
11
+ def render(set)
12
+ @info = info_for_set(set)
13
+
14
+ content_tag(:ul, class: 'pagination') do
15
+ safe_join([
16
+ link_to_first_page,
17
+ link_to_prev_page,
18
+ link_to_prev_gap,
19
+ safe_join(relevant_page_numbers.map do |num|
20
+ page = PageInterface.new(num, @info)
21
+ link = link_to(page, page_link_url(page), rel: page.rel)
22
+ content_tag(:li, link, class: ['page', (page.current? ? 'active' : nil)].compact)
23
+ end),
24
+ link_to_next_gap,
25
+ link_to_next_page,
26
+ link_to_last_page
27
+ ].compact)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def info_for_set(set)
34
+ {
35
+ current_page: set.instructions.dig(:paginate, :page),
36
+ page_size: set.instructions.dig(:paginate, :size),
37
+ total_pages: (set.total_count.to_f / set.instructions.dig(:paginate, :size)).ceil,
38
+ left_window_size: 2,
39
+ right_window_size: 2
40
+ }
41
+ end
42
+
43
+ def relevant_page_numbers
44
+ current_page = [@info[:current_page]]
45
+ left_window = [*(@info[:current_page] - @info[:left_window_size])..@info[:current_page]]
46
+ right_window = [*@info[:current_page]..(@info[:current_page] + @info[:right_window_size])]
47
+
48
+ (left_window | current_page | right_window).sort.reject { |x| (x < 1) || (x > @info[:total_pages]) }
49
+ end
50
+
51
+ def page_link_url(page_number)
52
+ request.query_parameters
53
+ .merge(controller: controller.controller_path,
54
+ action: controller.action_name,
55
+ paginate: {
56
+ page: page_number
57
+ })
58
+ end
59
+
60
+ def link_to_prev_gap
61
+ return unless @info[:current_page] > 1
62
+ return unless @info[:current_page] <= @info[:total_pages]
63
+ return unless (@info[:current_page] - 1) > (@info[:left_window_size] + 1)
64
+
65
+ link = link_to('…', '#', onclick: 'return false;')
66
+ content_tag(:li, link, class: 'page prev_gap disabled')
67
+ end
68
+
69
+ def link_to_next_gap
70
+ return unless @info[:current_page] >= 1
71
+ return unless @info[:current_page] <= @info[:total_pages]
72
+ return unless (@info[:total_pages] - @info[:current_page]) > (@info[:right_window_size] + 1)
73
+
74
+ link = link_to('…', '#', onclick: 'return false;')
75
+ content_tag(:li, link, class: 'page next_gap disabled')
76
+ end
77
+
78
+ def link_to_first_page
79
+ return unless @info[:current_page] > 1
80
+
81
+ link = link_to('« First', page_link_url(1))
82
+ content_tag(:li, link, class: 'page first')
83
+ end
84
+
85
+ def link_to_prev_page
86
+ return unless @info[:current_page] > 1
87
+ return unless @info[:current_page] <= @info[:total_pages]
88
+
89
+ link = link_to('‹ Prev', page_link_url(@info[:current_page] - 1), rel: 'prev')
90
+ content_tag(:li, link, class: 'page prev')
91
+ end
92
+
93
+ def link_to_last_page
94
+ return unless @info[:current_page] != @info[:total_pages]
95
+ return unless @info[:current_page] <= @info[:total_pages]
96
+
97
+ link = link_to('Last »', page_link_url(@info[:total_pages]))
98
+ content_tag(:li, link, class: 'page last')
99
+ end
100
+
101
+ def link_to_next_page
102
+ return unless @info[:current_page] != @info[:total_pages]
103
+ return unless @info[:current_page] <= @info[:total_pages]
104
+
105
+ link = link_to('Next ›', page_link_url(@info[:current_page] + 1), rel: 'next')
106
+ content_tag(:li, link, class: 'page next')
107
+ end
108
+
109
+ # delegates view helper methods to @template
110
+ def method_missing(name, *args, &block)
111
+ @template.respond_to?(name) ? @template.send(name, *args, &block) : super
112
+ end
113
+
114
+ # Wraps a "page number" and provides some utility methods
115
+ class PageInterface
116
+ def initialize(page, info)
117
+ @page = page
118
+ @info = info
119
+ end
120
+
121
+ def number
122
+ @page
123
+ end
124
+
125
+ def current?
126
+ @page == @info[:current_page]
127
+ end
128
+
129
+ def first?
130
+ @page == 1
131
+ end
132
+
133
+ def last?
134
+ @page == @info[:total_pages]
135
+ end
136
+
137
+ def prev?
138
+ @page == @info[:current_page] - 1
139
+ end
140
+
141
+ def next?
142
+ @page == @info[:current_page] + 1
143
+ end
144
+
145
+ def rel
146
+ return 'next' if next?
147
+ return 'prev' if prev?
148
+ nil
149
+ end
150
+
151
+ def out_of_range?
152
+ @page > @info[:total_pages]
153
+ end
154
+
155
+ def to_i
156
+ number
157
+ end
158
+
159
+ def to_s
160
+ number.to_s
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionSet
4
+ module Helpers
5
+ class SortLinker
6
+ def initialize(template)
7
+ @template = template
8
+ end
9
+
10
+ def render(column, title = nil)
11
+ column = column.to_s
12
+ title ||= sort_link_title(column)
13
+
14
+ @template.link_to title, sort_link_url(column)
15
+ end
16
+
17
+ private
18
+
19
+ def sort_link_title(column)
20
+ I18n.t(column,
21
+ scope: :attributes,
22
+ default: default_sort_link_title(column))
23
+ end
24
+
25
+ def sort_link_url(column)
26
+ @template.request
27
+ .query_parameters
28
+ .merge(controller: @template.controller.controller_path,
29
+ action: @template.controller.action_name,
30
+ sort: {
31
+ column => sort_link_direction(column)
32
+ })
33
+ end
34
+
35
+ def sort_link_direction(column)
36
+ return 'asc' unless sort_params_include_column?(column)
37
+ return 'asc' unless sort_params_only_include_valid_directions?
38
+
39
+ sort_direction_inverses = { 'asc' => 'desc', 'desc' => 'asc' }
40
+ sort_direction_inverses[@template.params.dig(:sort, column).to_s]
41
+ end
42
+
43
+ def default_sort_link_title(column)
44
+ ActiveSet::Instructions::Entry::KeyPath.new(column).titleized
45
+ end
46
+
47
+ def sort_params_include_column?(column)
48
+ @template.params[:sort]&.keys&.include?(column.to_s)
49
+ end
50
+
51
+ def sort_params_only_include_valid_directions?
52
+ (sort_params_directions - %w[asc desc]).empty?
53
+ end
54
+
55
+ def sort_params_directions
56
+ @template.params[:sort]&.values&.map(&:to_s)&.uniq
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionSet
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionset
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-25 00:00:00.000000000 Z
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activeset
@@ -227,9 +227,11 @@ files:
227
227
  - bin/setup
228
228
  - config.ru
229
229
  - lib/action_set.rb
230
+ - lib/action_set/helpers/helper_methods.rb
231
+ - lib/action_set/helpers/paginator.rb
232
+ - lib/action_set/helpers/sort_linker.rb
230
233
  - lib/action_set/instructions/entry_value.rb
231
234
  - lib/action_set/version.rb
232
- - lib/action_set/view_helpers.rb
233
235
  homepage: https://github.com/fractaledmind/actionset
234
236
  licenses:
235
237
  - MIT
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionSet
4
- module ViewHelpers
5
- def sort_link(column, title = nil)
6
- column = column.to_s
7
- title ||= sort_link_title(column)
8
-
9
- link_to title, sort_link_url(column)
10
- end
11
-
12
- private
13
-
14
- def sort_link_url(column)
15
- request.query_parameters.merge(controller: controller.controller_path,
16
- action: controller.action_name,
17
- sort: {
18
- column => sort_link_direction(column)
19
- })
20
- end
21
-
22
- def sort_link_title(column)
23
- I18n.t(column,
24
- scope: :attributes,
25
- default: default_sort_link_title(column))
26
- end
27
-
28
- def sort_link_direction(column)
29
- return 'asc' unless params[:sort]&.keys&.include?(column.to_s)
30
-
31
- sort_direction_inverses = { 'asc' => 'desc', 'desc' => 'asc' }
32
- sort_direction_inverses[params.dig(:sort, column)]
33
- end
34
-
35
- def default_sort_link_title(column)
36
- ActiveSet::Instructions::Entry::KeyPath.new(column).titleized
37
- end
38
- end
39
- end