recordselect-custom 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ .record-select {
2
+ width: 300px;
3
+ border: 1px solid #afd0f5;
4
+ font-family: sans-serif;
5
+ background-color: #fff;
6
+ font-size: 11px;
7
+ }
8
+
9
+ .record-select img {
10
+ border-width: 0px;
11
+ }
12
+
13
+ .record-select form {
14
+ display: inline;
15
+ }
16
+
17
+ .record-select form .text-input {
18
+ width: 294px;
19
+ margin: 2px auto 1px auto;
20
+ display: block;
21
+ border: 1px solid #999;
22
+ }
23
+
24
+ .record-select form input.example {
25
+ color: #999;
26
+ text-align: center;
27
+ }
28
+
29
+ .record-select ol,
30
+ .record-select li {
31
+ margin: 0px;
32
+ padding: 0px;
33
+ list-style: none;
34
+ clear: both;
35
+ }
36
+
37
+ .record-select a {
38
+ color: #0066cc;
39
+ text-decoration: none;
40
+ }
41
+
42
+ .record-select ol a {
43
+ display: block;
44
+ zoom: 1;
45
+ background-color: #e6f2ff;
46
+ padding: 2px 4px;
47
+ }
48
+
49
+ .record-select ol .even a {
50
+ background-color: #ffffff;
51
+ }
52
+
53
+ .record-select ol .pagination a {
54
+ background-color: #eee;
55
+ }
56
+
57
+ .record-select ol .previous a {
58
+ border-bottom: 1px solid #afd0f5;
59
+ }
60
+
61
+ .record-select ol .next a {
62
+ border-top: 1px solid #afd0f5;
63
+ }
64
+
65
+ .record-select ol .pagination a img {
66
+ vertical-align: middle;
67
+ }
68
+
69
+ .record-select ol .found {
70
+ text-align: center;
71
+ font-style: italic;
72
+ color: #999;
73
+ padding: 1px 4px;
74
+ border-bottom: 1px solid #afd0f5;
75
+ }
76
+
77
+ .record-select ol .current a,
78
+ .record-select ol a:hover {
79
+ background-color: #ffff88;
80
+ }
81
+
82
+ .record-select ol a.selected {
83
+ background-color: #666;
84
+ color: #fff;
85
+ }
86
+
87
+ .record-select-container {
88
+ position: absolute;
89
+ z-index: 100;
90
+ }
91
+
92
+ iframe.record-select-mask {
93
+ /* to mask windowed elements in IE6 */
94
+ position: absolute;
95
+ z-index: 99;
96
+ filter: progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);
97
+ }
98
+
99
+ .record-select-autocomplete form .text-input {
100
+ display: none;
101
+ }
102
+
103
+ .record-select-list {
104
+ padding: 0px;
105
+ margin: 0px;
106
+ list-style: none;
107
+ }
108
+
109
+ .record-select-list li {
110
+ overflow: auto;
111
+ zoom: 1;
112
+ margin-left: 10px;
113
+ font-size: 80%;
114
+ }
115
+
116
+ .record-select-list label {
117
+ float: left;
118
+ }
119
+
120
+ .record-select-list a.remove {
121
+ display: block;
122
+ width: 0px;
123
+ height: 16px;
124
+ padding-left: 16px;
125
+ background: url('../../images/record_select/cross.gif') no-repeat 0 0;
126
+ overflow: hidden;
127
+ float: left;
128
+ margin-right: 5px;
129
+ }
@@ -0,0 +1,5 @@
1
+ en:
2
+ recordselect:
3
+ found: Found
4
+ next: Next
5
+ previous: Previous
@@ -0,0 +1,5 @@
1
+ zh:
2
+ recordselect:
3
+ found: 找到
4
+ next: 下页
5
+ previous: 上页
data/init.rb ADDED
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/lib/localization'
2
+ require File.dirname(__FILE__) + '/lib/extensions/active_record'
3
+
4
+ ActionController::Base.send(:include, RecordSelect)
5
+ ActionView::Base.send(:include, RecordSelect::Helpers)
6
+ ActionView::Helpers::FormBuilder.send(:include, RecordSelect::FormBuilder)
7
+
8
+ ['stylesheets', 'images', 'javascripts'].each do |asset_type|
9
+ public_dir = File.join(RAILS_ROOT, 'public', asset_type, 'record_select')
10
+ local_dir = File.join(File.dirname(__FILE__), 'assets', asset_type)
11
+ FileUtils.mkdir public_dir unless File.exists? public_dir
12
+ Dir.entries(local_dir).each do |file|
13
+ next if file =~ /^\./
14
+ FileUtils.cp File.join(local_dir, file), public_dir
15
+ end
16
+ end
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,9 @@
1
+ module ActiveRecord # :nodoc:
2
+ class Base # :nodoc:
3
+ unless method_defined? :to_label
4
+ def to_label
5
+ self.to_s
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # Provides a simple pass-through localizer for RecordSelect. If you want
2
+ # to localize RS, you need to override this method and route it to your
3
+ # own system.
4
+ class Object
5
+ def rs_(string_to_localize, *args)
6
+ args.empty? ? string_to_localize : (sprintf string_to_localize, *args)
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ module RecordSelect
2
+ def self.included(base)
3
+ base.send :extend, ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ # Enables and configures RecordSelect on your controller.
8
+ #
9
+ # *Options*
10
+ # +model+:: defaults based on the name of the controller
11
+ # +per_page+:: records to show per page when browsing
12
+ # +notify+:: a method name to invoke when a record has been selected.
13
+ # +order_by+:: a SQL string to order the search results
14
+ # +search_on+:: an array of searchable fields
15
+ # +full_text_search+:: a boolean for whether to use a %?% search pattern or not. default is false.
16
+ # +label+:: a proc that accepts a record as argument and returns an option label. default is to call record.to_label instead.
17
+ # +include+:: as for ActiveRecord::Base#find. can help with search conditions or just help optimize rendering the results.
18
+ #
19
+ # You may also pass a block, which will be used as options[:notify].
20
+ def record_select(options = {})
21
+ options[:model] ||= self.to_s.split('::').last.sub(/Controller$/, '').pluralize.singularize.underscore
22
+ @record_select_config = RecordSelect::Config.new(options.delete(:model), options)
23
+ self.send :include, RecordSelect::Actions
24
+ self.send :include, RecordSelect::Conditions
25
+ end
26
+
27
+ attr_reader :record_select_config
28
+
29
+ def uses_record_select?
30
+ !record_select_config.nil?
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ module RecordSelect
2
+ module Actions
3
+ # :method => :get
4
+ # params => [:page, :search]
5
+ def browse
6
+ conditions = record_select_conditions
7
+ klass = record_select_config.model
8
+ count = klass.count(:conditions => conditions, :include => record_select_includes)
9
+ pager = ::Paginator.new(count, (params[:per_page] || record_select_config.per_page).to_i) do |offset, per_page|
10
+ klass.find(:all,
11
+ :offset => offset,
12
+ :include => [record_select_includes, record_select_config.include].flatten.compact,
13
+ :limit => per_page,
14
+ :conditions => conditions,
15
+ :order => record_select_config.order_by
16
+ )
17
+ end
18
+ @page = pager.page(params[:page] || 1)
19
+
20
+ render_record_select((params[:update] ? 'browse.rjs' : '_browse.rhtml')) and return if params[:format] == "js"
21
+ render_record_select '_browse.rhtml', :layout => true
22
+ end
23
+
24
+ # :method => :post
25
+ # params => [:id]
26
+ def select
27
+ klass = record_select_config.model
28
+ record = klass.find(params[:id])
29
+ if record_select_config.notify.is_a? Proc
30
+ record_select_config.notify.call(record)
31
+ elsif record_select_config.notify
32
+ send(record_select_config.notify, record)
33
+ end
34
+ render :nothing => true
35
+ end
36
+
37
+ protected
38
+
39
+ def record_select_config #:nodoc:
40
+ self.class.record_select_config
41
+ end
42
+
43
+ def render_record_select(file, options = {}) #:nodoc:
44
+ options[:layout] ||= false
45
+ options[:file] = record_select_path_of(file)
46
+ options[:use_full_path] = false
47
+ render options
48
+ end
49
+
50
+ private
51
+
52
+ def record_select_views_path
53
+ @record_select_views_path ||= "vendor/plugins/#{File.expand_path(__FILE__).match(/vendor\/plugins\/(\w*)/)[1]}/lib/views"
54
+ end
55
+
56
+ def record_select_path_of(template)
57
+ File.join(RAILS_ROOT, record_select_views_path, template)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,88 @@
1
+ module RecordSelect
2
+ module Conditions
3
+ protected
4
+ # returns the combination of all conditions.
5
+ # conditions come from:
6
+ # * current search (params[:search])
7
+ # * intelligent url params (e.g. params[:first_name] if first_name is a model column)
8
+ # * specific conditions supplied by the developer
9
+ def record_select_conditions
10
+ conditions = []
11
+
12
+ merge_conditions(
13
+ record_select_conditions_from_search,
14
+ record_select_conditions_from_params,
15
+ record_select_conditions_from_controller
16
+ )
17
+ end
18
+
19
+ # an override method.
20
+ # here you can provide custom conditions to define the selectable records. useful for situational restrictions.
21
+ def record_select_conditions_from_controller; end
22
+
23
+ # another override method.
24
+ # define any association includes you want for the finder search.
25
+ def record_select_includes; end
26
+
27
+ # generate conditions from params[:search]
28
+ # override this if you want to customize the search routine
29
+ def record_select_conditions_from_search
30
+ search_pattern = record_select_config.full_text_search? ? '%?%' : '?%'
31
+
32
+ if params[:search] and !params[:search].strip.empty?
33
+ tokens = params[:search].strip.split(' ')
34
+
35
+ where_clauses = record_select_config.search_on.collect { |sql| "#{sql} LIKE ?" }
36
+ phrase = "(#{where_clauses.join(' OR ')})"
37
+
38
+ sql = ([phrase] * tokens.length).join(' AND ')
39
+ tokens = tokens.collect{ |value| [search_pattern.sub('?', value.downcase)] * record_select_config.search_on.length }.flatten
40
+
41
+ conditions = [sql, *tokens]
42
+ end
43
+ end
44
+
45
+ # instead of a shotgun approach, this assumes the user is
46
+ # searching vs some SQL field (possibly built with CONCAT())
47
+ # similar to the record labels.
48
+ # def record_select_simple_conditions_from_search
49
+ # return unless params[:search] and not params[:search].empty?
50
+ #
51
+ # search_pattern = record_select_config.full_text_search? ? '%?%' : '?%'
52
+ # search_string = search_pattern.sub('?', value.downcase)
53
+ #
54
+ # ["LOWER(#{record_select_config.search_on})", search_pattern.sub('?', value.downcase)]
55
+ # end
56
+
57
+ # generate conditions from the url parameters (e.g. users/browse?group_id=5)
58
+ def record_select_conditions_from_params
59
+ conditions = nil
60
+ params.each do |field, value|
61
+ next unless column = record_select_config.model.columns_hash[field]
62
+ conditions = merge_conditions(
63
+ conditions,
64
+ record_select_condition_for_column(column, value)
65
+ )
66
+ end
67
+ conditions
68
+ end
69
+
70
+ # generates an SQL condition for the given column/value
71
+ def record_select_condition_for_column(column, value)
72
+ if value.blank? and column.null
73
+ "#{column.name} IS NULL"
74
+ elsif value.is_a?(Array)
75
+ ["#{column.name} IN (?)", value.map{|v| column.type_cast(v)}]
76
+ elsif column.text?
77
+ ["LOWER(#{column.name}) LIKE ?", value]
78
+ else
79
+ ["#{column.name} = ?", column.type_cast(value)]
80
+ end
81
+ end
82
+
83
+ def merge_conditions(*conditions) #:nodoc:
84
+ c = conditions.find_all {|c| not c.nil? and not c.empty? }
85
+ c.empty? ? nil : c.collect{|c| record_select_config.model.send(:sanitize_sql, c)}.join(' AND ')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,84 @@
1
+ module RecordSelect
2
+ # a write-once configuration object
3
+ class Config
4
+ def initialize(klass, options = {})
5
+ @klass = klass
6
+
7
+ @notify = block_given? ? proc : options[:notify]
8
+
9
+ @per_page = options[:per_page]
10
+
11
+ @search_on = [options[:search_on]].flatten
12
+
13
+ @order_by = options[:order_by]
14
+
15
+ @full_text_search = options[:full_text_search]
16
+
17
+ @label = options[:label]
18
+
19
+ @include = options[:include]
20
+ end
21
+
22
+ # The model object we're browsing
23
+ def model
24
+ @model ||= klass.to_s.camelcase.constantize
25
+ end
26
+
27
+ # Records to show on a page
28
+ def per_page
29
+ @per_page ||= 10
30
+ end
31
+
32
+ # The method name or proc to notify of a selection event.
33
+ # May not matter if the selection event is intercepted client-side.
34
+ def notify
35
+ @notify
36
+ end
37
+
38
+ # A collection of fields to search. This is essentially raw SQL, so you could search on "CONCAT(first_name, ' ', last_name)" if you wanted to.
39
+ # NOTE: this does *NO* default transforms (such as LOWER()), that's left entirely up to you.
40
+ def search_on
41
+ @search_on ||= self.model.columns.collect{|c| c.name if [:text, :string].include? c.type}.compact
42
+ end
43
+
44
+ def order_by
45
+ @order_by ||= "#{model.primary_key} ASC"
46
+ end
47
+
48
+ def full_text_search?
49
+ @full_text_search ? true : false
50
+ end
51
+
52
+ def include
53
+ @include
54
+ end
55
+
56
+ # If a proc, must accept the record as an argument and return a descriptive string.
57
+ #
58
+ # If a symbol or string, must name a partial that renders a representation of the
59
+ # record. The partial should assume a local "record" variable, and should include a
60
+ # <label> tag, even if it's not visible. The contents of the <label> tag will be used
61
+ # to represent the record once it has been selected. For example:
62
+ #
63
+ # record_select_config.label = :user_description
64
+ #
65
+ # > app/views/users/_user_description.erb
66
+ #
67
+ # <div class="user_description">
68
+ # <%= image_tag url_for_file_column(record, 'avatar') %>
69
+ # <label><%= record.username %></label>
70
+ # <p><%= record.quote %></p>
71
+ # </div>
72
+ #
73
+ def label
74
+ @label ||= proc {|r| r.to_label}
75
+ end
76
+
77
+ protected
78
+
79
+ # A singularized underscored version of the model we're browsing
80
+ def klass
81
+ @klass
82
+ end
83
+ end
84
+ end