index_view 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/index_view.rb +54 -0
- data/lib/index_view/column.rb +64 -0
- data/lib/index_view/customization_defaults.rb +28 -0
- data/lib/index_view/implementation.rb +174 -0
- data/lib/index_view/sql_conditions.rb +19 -0
- data/lib/index_view/sql_generator.rb +20 -0
- metadata +50 -0
data/lib/index_view.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/index_view/column"
|
2
|
+
require File.dirname(__FILE__) + "/index_view/sql_generator"
|
3
|
+
require File.dirname(__FILE__) + "/index_view/sql_conditions"
|
4
|
+
require File.dirname(__FILE__) + "/index_view/implementation"
|
5
|
+
require File.dirname(__FILE__) + "/index_view/customization_defaults"
|
6
|
+
|
7
|
+
module IndexView
|
8
|
+
module Version
|
9
|
+
MAJOR = 0
|
10
|
+
MINOR = 1
|
11
|
+
TINY = 0
|
12
|
+
|
13
|
+
STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
|
14
|
+
end
|
15
|
+
|
16
|
+
class InvalidSort < StandardError; end
|
17
|
+
|
18
|
+
class Base
|
19
|
+
include Implementation
|
20
|
+
include CustomizationDefaults
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def find(*args)
|
24
|
+
new({}).find(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def all(*args)
|
28
|
+
new({}).all(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def first(*args)
|
32
|
+
new({}).first(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
# used to define columns you want to render in the view,
|
36
|
+
# and gives you a way to customize _how_ they render
|
37
|
+
# See IndexView::Column to get an idea of the options you can pass in.
|
38
|
+
def column(*args, &block)
|
39
|
+
columns << Column.new(*args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns a collection of the IndexView::Column objects that were
|
43
|
+
# added through the +column+ method
|
44
|
+
def columns
|
45
|
+
@columns ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def fields_for_search
|
49
|
+
searchable_columns = columns.select { |c| c.searchable? }
|
50
|
+
searchable_columns.map { |col| col.column_name }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module IndexView
|
2
|
+
class Column
|
3
|
+
class InvalidKeyError < StandardError; end
|
4
|
+
|
5
|
+
OPTION_KEYS = [:link, :sortable, :title, :searchable]
|
6
|
+
|
7
|
+
def initialize(column_name, options={ }, &link)
|
8
|
+
@column_name = column_name.to_sym
|
9
|
+
|
10
|
+
check_and_assign_values_from_keys(options, link)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :column_name, :link
|
14
|
+
|
15
|
+
def column_value(context, object)
|
16
|
+
context.instance_exec(object, &link)
|
17
|
+
end
|
18
|
+
|
19
|
+
def column_value?
|
20
|
+
link ? true : false
|
21
|
+
end
|
22
|
+
|
23
|
+
def sortable?
|
24
|
+
@sortable ? true : false
|
25
|
+
end
|
26
|
+
|
27
|
+
def human_name
|
28
|
+
column_name.to_s.humanize
|
29
|
+
end
|
30
|
+
|
31
|
+
def title
|
32
|
+
@title ? @title : human_name
|
33
|
+
end
|
34
|
+
|
35
|
+
def searchable?
|
36
|
+
@searchable ? true : false
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_and_assign_values_from_keys(hash, link)
|
42
|
+
hash.each do |key, value|
|
43
|
+
key = key.to_sym
|
44
|
+
|
45
|
+
if key == :link
|
46
|
+
Kernel.warn ":link is no longer a valid key. Pass a block directly to the column method (from #{caller[5]})"
|
47
|
+
end
|
48
|
+
|
49
|
+
if OPTION_KEYS.include?(key)
|
50
|
+
assign_if_present(key, value)
|
51
|
+
else
|
52
|
+
key_names = "[#{OPTION_KEYS.map { |k| ":#{k}" }.join(", ")}]"
|
53
|
+
raise InvalidKeyError, "#{key} is not a valid key. Valid keys are #{key_names}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
assign_if_present(:link, link)
|
58
|
+
end
|
59
|
+
|
60
|
+
def assign_if_present(key, value)
|
61
|
+
instance_variable_set("@#{key}", value) if value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module IndexView
|
2
|
+
# The following methods are safe to override in descendent classes
|
3
|
+
module CustomizationDefaults
|
4
|
+
DEFAULT_PAGINATION_NUMBER = 30
|
5
|
+
|
6
|
+
def target_class
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def per_page
|
11
|
+
target_class.respond_to?(:per_page) ?
|
12
|
+
target_class.per_page :
|
13
|
+
DEFAULT_PAGINATION_NUMBER
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_sort_term
|
17
|
+
raise NotImplementedError, "default_sort_term must be defined"
|
18
|
+
end
|
19
|
+
|
20
|
+
def secondary_sort_term
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_sort_direction
|
25
|
+
Implementation::DESC
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
module IndexView
|
2
|
+
module Implementation
|
3
|
+
include SQLConditions
|
4
|
+
|
5
|
+
ASC = :ASC
|
6
|
+
DESC = :DESC
|
7
|
+
SORT_DIRECTIONS = [ASC, DESC]
|
8
|
+
|
9
|
+
attr_reader :params
|
10
|
+
|
11
|
+
# IndexView objects are initialized with the params of a request.
|
12
|
+
# See README.rdoc
|
13
|
+
def initialize(params = { })
|
14
|
+
@params = params
|
15
|
+
end
|
16
|
+
|
17
|
+
# returns a paginated set of the IndexView's +target_class+
|
18
|
+
# To customize how the objects are paginated -
|
19
|
+
# redefine +pagination_options+ in your class
|
20
|
+
def paginate
|
21
|
+
target_class.paginate(pagination_options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(selector, options={})
|
25
|
+
case selector
|
26
|
+
when :first, :last, :all
|
27
|
+
target_class.find(selector, find_options.merge(options))
|
28
|
+
else
|
29
|
+
target_class.find(selector)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def all(*args)
|
34
|
+
find(:all, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
def first(*args)
|
38
|
+
find(:first, *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_options
|
42
|
+
{
|
43
|
+
:from => table_name.to_s,
|
44
|
+
:order => sort,
|
45
|
+
:conditions => conditions_sql
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a hash of options used to paginate your IndexView's +target_class+.
|
50
|
+
# You can overwrite this in your class to customize pagination.
|
51
|
+
def pagination_options
|
52
|
+
{
|
53
|
+
:from => table_name.to_s,
|
54
|
+
:conditions => conditions_sql,
|
55
|
+
:order => sort,
|
56
|
+
:page => @params[:page],
|
57
|
+
:per_page => per_page
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def search_term
|
62
|
+
@params[:search]
|
63
|
+
end
|
64
|
+
|
65
|
+
def search_term?
|
66
|
+
search_term && !search_term.blank? ? true : false
|
67
|
+
end
|
68
|
+
|
69
|
+
def sort
|
70
|
+
"#{sort_term} #{sort_direction}" + (!secondary_sort_term.blank? ? ", #{secondary_sort_term} #{sort_direction}" : '')
|
71
|
+
end
|
72
|
+
|
73
|
+
def sort_term
|
74
|
+
if sort = @params[:sort]
|
75
|
+
sort
|
76
|
+
else
|
77
|
+
default_sort_term.is_a?(Array) ? default_sort_term.join(", ") : default_sort_term
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def sort_direction
|
82
|
+
if SORT_DIRECTIONS.include?(given_sort_direction)
|
83
|
+
given_sort_direction
|
84
|
+
else
|
85
|
+
raise IndexView::InvalidSort, "#{given_sort_direction} is not a valid sort direction"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def opposite_sort_direction
|
90
|
+
ascending? ? DESC : ASC
|
91
|
+
end
|
92
|
+
|
93
|
+
# returns a collection of the IndexView::Column objects that were
|
94
|
+
# added through the +column+ method
|
95
|
+
def columns
|
96
|
+
self.class.columns
|
97
|
+
end
|
98
|
+
|
99
|
+
# Takes a column name and returns whether or not your index is currently sorted on that column.
|
100
|
+
def sorting?(column_name)
|
101
|
+
sort_term.to_s == column_name.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns whether or not your index is currently sorted ascended.
|
105
|
+
def ascending?
|
106
|
+
sort_direction == ASC
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns whether or not your index is currently sorted descended.
|
110
|
+
def descending?
|
111
|
+
!ascending?
|
112
|
+
end
|
113
|
+
|
114
|
+
def state?
|
115
|
+
state && !state.blank? ? true : false
|
116
|
+
end
|
117
|
+
|
118
|
+
def state
|
119
|
+
@params[:state]
|
120
|
+
end
|
121
|
+
|
122
|
+
def fields_for_search
|
123
|
+
self.class.fields_for_search
|
124
|
+
end
|
125
|
+
|
126
|
+
def table_name
|
127
|
+
target_class.table_name.to_sym
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def conditions_sql
|
133
|
+
present_conditions.join(" AND ")
|
134
|
+
end
|
135
|
+
|
136
|
+
def present_conditions
|
137
|
+
parenthesize(remove_empties(sanitize(conditions)))
|
138
|
+
end
|
139
|
+
|
140
|
+
def conditions
|
141
|
+
[search_conditions, state_conditions, *index_parameter_conditions]
|
142
|
+
end
|
143
|
+
|
144
|
+
def index_parameter_conditions
|
145
|
+
if index_params = params[:index]
|
146
|
+
index_params.reject { |k,v| v.blank? }.map { |k, v| ["#{k} = ?", v] }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def parenthesize(collection)
|
151
|
+
collection.map { |element| "(#{element})" }
|
152
|
+
end
|
153
|
+
|
154
|
+
def remove_empties(collection)
|
155
|
+
collection.reject { |element| element.blank? }
|
156
|
+
end
|
157
|
+
|
158
|
+
def sanitize(collection)
|
159
|
+
collection.map { |element| sanitize_sql(element) }
|
160
|
+
end
|
161
|
+
|
162
|
+
def sanitize_sql(sql)
|
163
|
+
target_class.send(:sanitize_sql, sql)
|
164
|
+
end
|
165
|
+
|
166
|
+
def given_sort_direction
|
167
|
+
if direction = @params[:direction]
|
168
|
+
direction.upcase.to_sym
|
169
|
+
else
|
170
|
+
default_sort_direction
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module IndexView
|
2
|
+
module SQLConditions
|
3
|
+
include SQLGenerator
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def state_conditions
|
8
|
+
if state?
|
9
|
+
["state = ?", state]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def search_conditions
|
14
|
+
if search_term?
|
15
|
+
like_for_many_columns(search_term, *fields_for_search)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module IndexView
|
2
|
+
module SQLGenerator
|
3
|
+
def like_for_many_columns(value, *fields)
|
4
|
+
[
|
5
|
+
condition_fields_for_like_with_many_columns(value, fields),
|
6
|
+
*value_fields_for_like_with_many_columns(value, fields)
|
7
|
+
]
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def condition_fields_for_like_with_many_columns(value, fields)
|
13
|
+
fields.map { |field| "#{field} LIKE ?" }.join(" OR ")
|
14
|
+
end
|
15
|
+
|
16
|
+
def value_fields_for_like_with_many_columns(value, fields)
|
17
|
+
fields.map { "%#{value.gsub(" ", "%")}%" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: index_view
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Scott Taylor
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-06 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A simple way to use mysql indexes in a rails app
|
15
|
+
email: scott@railsnewbie.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/index_view/column.rb
|
21
|
+
- lib/index_view/customization_defaults.rb
|
22
|
+
- lib/index_view/implementation.rb
|
23
|
+
- lib/index_view/sql_conditions.rb
|
24
|
+
- lib/index_view/sql_generator.rb
|
25
|
+
- lib/index_view.rb
|
26
|
+
homepage: https://github.com/smtlaissezfaire/index_view
|
27
|
+
licenses: []
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 1.8.24
|
47
|
+
signing_key:
|
48
|
+
specification_version: 3
|
49
|
+
summary: index_view rails plugin
|
50
|
+
test_files: []
|