index_view 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.
@@ -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: []