livelist-rails 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/livelist.coffee +57 -52
- data/app/assets/javascripts/livelist.js +61 -61
- data/app/assets/javascripts/livelist.min.js +1 -1
- data/lib/livelist/rails.rb +4 -3
- data/lib/livelist/rails/active_record.rb +12 -144
- data/lib/livelist/rails/filter.rb +109 -0
- data/lib/livelist/rails/filter_collection.rb +34 -0
- data/lib/livelist/rails/filter_criteria.rb +50 -0
- data/lib/livelist/rails/filter_criterion.rb +81 -0
- data/lib/livelist/rails/version.rb +2 -2
- data/livelist-rails.gemspec +3 -5
- data/spec/livelist/rails/active_record_spec.rb +1 -189
- data/spec/livelist/rails/filter_collection_spec.rb +26 -0
- data/spec/livelist/rails/filter_criteria_spec.rb +42 -0
- data/spec/livelist/rails/filter_criterion_spec.rb +34 -0
- data/spec/livelist/rails/filter_spec.rb +54 -0
- metadata +22 -10
@@ -1 +1 @@
|
|
1
|
-
((function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b=Object.prototype.hasOwnProperty,c=function(a,c){function e(){this.constructor=a}for(var d in c)b.call(c,d)&&(a[d]=c[d]);return e.prototype=c.prototype,a.prototype=new e,a.__super__=c.prototype,a};window.Utilities=function(){function b(){this.setOptions=a(this.setOptions,this)}return b.prototype.setOptions=function(a,b){var c=this;return b==null&&(b=this),_.each(a,function(a,c){return b[c]=a})},b}(),window.LiveList=function(a){function b(a){this.
|
1
|
+
((function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b=Object.prototype.hasOwnProperty,c=function(a,c){function e(){this.constructor=a}for(var d in c)b.call(c,d)&&(a[d]=c[d]);return e.prototype=c.prototype,a.prototype=new e,a.__super__=c.prototype,a};window.Utilities=function(){function b(){this.setOptions=a(this.setOptions,this)}return b.prototype.setOptions=function(a,b){var c=this;return b==null&&(b=this),_.each(a,function(a,c){return b[c]=a})},b}(),window.LiveList=function(a){function b(a){this.listSelector=a.list.renderTo,this.resourceName=a.global.resourceName,this.resourceNameSingular=a.global.resourceNameSingular,this.eventName="livelist:"+this.resourceName,this.urlPrefix="/"+this.resourceName,this.search=new Search(a.search,this),this.filters=new Filters(a.filters,this),this.pagination=new Pagination(a.pagination,this),this.list=new List(a.list,this)}return c(b,a),b}(Utilities),window.List=function(b){function d(b,c){this.renderIndex=a(this.renderIndex,this),this.removeFetchingIndication=a(this.removeFetchingIndication,this),this.displayFetchingIndication=a(this.displayFetchingIndication,this);var d=this;this.fetchRequest=null,this.livelist=c,this.listTemplate="{{#"+this.livelist.resourceName+"}}{{>"+this.livelist.resourceNameSingular+"}}{{/"+this.livelist.resourceName+"}}",this.listItemTemplate="<li>{{id}}</li>",this.fetchingIndicationClass="updating",this.renderTo="ul#"+this.livelist.resourceName,this.setOptions(b),$(this.renderTo).bind(this.livelist.eventName,function(a,b){return d.fetch({presets:null,page:b!=null?b.page:void 0})}),this.fetch({presets:this.livelist.filters.getPresets()})}return c(d,b),d.prototype.displayFetchingIndication=function(){return $(this.renderTo).addClass(this.fetchingIndicationClass)},d.prototype.removeFetchingIndication=function(){return $(this.renderTo).removeClass(this.fetchingIndicationClass)},d.prototype.renderIndex=function(a,b,c){return this.livelist.data=a,this.render(),this.livelist.pagination.render(this.livelist.data),this.livelist.filters.render(this.livelist.data)},d.prototype.fetch=function(a){var b,c;return this.fetchRequest&&this.fetchRequest.abort(),c=this.livelist.search.searchTerm(),b={},b.filters=this.livelist.filters.setPresets(a.presets),c&&(b.q=c),a.page&&(b.page=a.page),this.fetchRequest=$.ajax({url:this.livelist.urlPrefix,type:this.livelist.httpMethod,dataType:"json",data:b,beforeSend:this.displayFetchingIndication,success:this.renderIndex})},d.prototype.render=function(){var a,b;return b={},b[this.livelist.resourceNameSingular]=this.listItemTemplate,a=Mustache.to_html(this.listTemplate,this.livelist.data,b),$(this.renderTo).html(a),this.removeFetchingIndication()},d}(Utilities),window.LiveList.version="0.0.5",window.Filters=function(b){function d(b,c){this.handleAdvancedOptionsClick=a(this.handleAdvancedOptionsClick,this);var d=this;this.livelist=c,this.filters=b.presets?_.keys(b.presets):[],this.initializeCookies(),this.setOptions(b),$("input.filter_option",this.renderTo).live("change",function(){return $(d.livelist.listSelector).trigger(d.livelist.eventName)}),$(this.advancedOptionsToggleSelector).click(this.handleAdvancedOptionsClick)}return c(d,b),d.prototype.initializeCookies=function(){if(jQuery.cookie&&this.useCookies&&this.cookieName)return this.cookieName="livelist_filter_presets"},d.prototype.getPresets=function(){var a;return jQuery.cookie&&this.useCookies&&(a=jQuery.cookie(this.cookieName)),this.useCookies&&a?JSON.parse(a):this.presets},d.prototype.setPresets=function(a){var b;return b={},jQuery.isEmptyObject(a)?(b=this.selections(),jQuery.cookie&&this.setCookie(b)):b=a,b},d.prototype.setCookie=function(a){if(!jQuery.isEmptyObject(a))return jQuery.cookie(this.cookieName,JSON.stringify(a))},d.prototype.template="{{#filters}}\n<div class='filter'>\n <h3>\n {{name}}\n </h3>\n <ul id='{{filter_slug}}_filter_options'>\n {{#options}}\n <label>\n <li>\n <input {{#selected}}checked='checked'{{/selected}}\n class='left filter_option'\n id='filter_{{slug}}'\n name='filters[]'\n type='checkbox'\n value='{{value}}' />\n <div class='left filter_name'>{{name}}</div>\n <div class='right filter_count'>{{count}}</div>\n <div class='clear'></div>\n </li>\n </label>\n {{/options}}\n </ul>\n</div>\n{{/filters}}",d.prototype.selections=function(){var a,b=this;return a={},_.each(this.filters,function(b){return a[b]=_.pluck($("#"+b+"_filter_options input.filter_option:checked"),"value")}),a},d.prototype.noFiltersSelected=function(a){return _.all(a.filters,function(a){return _.all(a.options,function(a){return!a.selected})})},d.prototype.sortOptions=function(a){return _.map(a,function(a){return a.options=_.sortBy(a.options,function(a){return a.name}),a})},d.prototype.sort=function(a){return _.sortBy(a,function(a){return a.name})},d.prototype.render=function(a){var b;this.filters=_.pluck(a.filters,"filter_slug"),this.sort(a.filters),this.sortOptions(a.filters),b=Mustache.to_html(this.template,a),$(this.renderTo).html(b);if(this.noFiltersSelected(a)&&a[this.livelist.resourceName].length>0)return $('input[type="checkbox"]',this.renderTo).attr("checked","checked")},d.prototype.handleAdvancedOptionsClick=function(a){return a.preventDefault(),$(this.renderTo).slideToggle()},d}(Utilities),window.Pagination=function(b){function d(b,c){this.handlePaginationLinkClick=a(this.handlePaginationLinkClick,this),this.livelist=c,this.pagination=null,this.maxPages=30,this.emptyListMessage="<p>No "+this.livelist.resourceName+" matched your filter criteria</p>",this.setOptions(b),$(""+this.renderTo+" a").live("click",function(a){return a.preventDefault()}),$(""+this.renderTo+" li:not(.disabled) a").live("click",this.handlePaginationLinkClick)}return c(d,b),d.prototype.template="{{#isEmpty}}\n {{{emptyListMessage}}}\n{{/isEmpty}}\n{{^isEmpty}}\n<div class=\"pagination\">\n <ul>\n <li class=\"{{^previousPage}}disabled{{/previousPage}}\">\n <a href='{{urlPrefix}}?page={{previousPage}}' data-page='{{previousPage}}'>← Previous</a>\n </li>\n\n {{#pages}}\n <li class=\"{{#currentPage}}active disabled{{/currentPage}}\">\n <a href='{{urlPrefix}}?page={{page}}' data-page='{{page}}'>{{page}}</a>\n </li>\n {{/pages}}\n\n <li class=\"{{^nextPage}}disabled{{/nextPage}}\">\n <a href='{{urlPrefix}}?page={{nextPage}}' data-page='{{nextPage}}'>Next →</a>\n </li>\n </ul>\n</div>\n{{/isEmpty}}",d.prototype.pagesJSON=function(a,b){var c,d,e,f,g,h;return d=Math.floor(this.maxPages/2),c=a<=d?1:a-d,f=c+d*2-1,e=f>=b?b:f,_.map(function(){h=[];for(var a=c;c<=e?a<=e:a>=e;c<=e?a++:a--)h.push(a);return h}.apply(this),function(b){return{page:b,currentPage:a===b}})},d.prototype.paginationJSON=function(a){return{isEmpty:a.total_pages===0,emptyListMessage:this.emptyListMessage,currentPage:a.current_page,nextPage:a.next_page,previousPage:a.previous_page,urlPrefix:this.livelist.urlPrefix,pages:this.pagesJSON(a.current_page,a.total_pages)}},d.prototype.render=function(a){var b;return this.pagination=this.paginationJSON(a.pagination),b=Mustache.to_html(this.template,this.pagination),$(this.renderTo).html(b)},d.prototype.handlePaginationLinkClick=function(a){return a.preventDefault(),$(this.livelist.listSelector).trigger(this.livelist.eventName,{page:$(a.target).data("page")})},d}(Utilities),window.Search=function(b){function d(b,c){this.handleSearchFormSubmit=a(this.handleSearchFormSubmit,this);var d=this;this.livelist=c,this.setOptions(b),$(this.formSelector).submit(function(a){return d.handleSearchFormSubmit(a)})}return c(d,b),d.prototype.searchTerm=function(){var a;return a=$(this.searchTextInputSelector).val(),!a||a===""?null:a},d.prototype.handleSearchFormSubmit=function(a){return a.preventDefault(),$(this.livelist.listSelector).trigger(this.livelist.eventName)},d}(Utilities)})).call(this);
|
data/lib/livelist/rails.rb
CHANGED
@@ -1,159 +1,27 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require 'livelist/rails/filter_collection'
|
2
3
|
|
3
4
|
module Livelist
|
4
5
|
module Rails
|
5
6
|
module ActiveRecord
|
6
|
-
@@filter_slugs = []
|
7
|
-
@@filter_collections = {}
|
8
7
|
|
9
|
-
def filter_for(
|
10
|
-
|
11
|
-
|
8
|
+
def filter_for(slug, options = {})
|
9
|
+
@filters ||= FilterCollection.new
|
10
|
+
@filters.create_filter(
|
11
|
+
:reference_criteria => options[:reference_criteria],
|
12
|
+
:model_name => model_name,
|
13
|
+
:slug => slug
|
14
|
+
)
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
def filters_as_json(filter_params)
|
16
|
-
@counts = {}
|
17
|
-
@filter_params = filter_params || {}
|
18
|
-
filters = @@filter_slugs.map do |filter|
|
19
|
-
filter_options = send("#{filter}_filters", filter)
|
20
|
-
send("#{filter}_filter", filter_options)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def filter_relation(filter_params)
|
25
|
-
query = scoped
|
26
|
-
@@filter_slugs.each do |filter_slug|
|
27
|
-
values = filter_params[filter_slug.to_s]
|
28
|
-
query = query.send("#{filter_slug}_where", filter_collection(filter_slug))
|
29
|
-
query = query.send("#{filter_slug}_relation", values) unless filter_params.empty?
|
30
|
-
end
|
31
|
-
query
|
32
|
-
end
|
33
|
-
|
34
|
-
def filter(filter_params)
|
35
|
-
filter_params ||= {}
|
36
|
-
@filter_relation = filter_relation(filter_params)
|
37
|
-
end
|
38
|
-
|
39
|
-
def filter_option_count(filter_slug, option)
|
40
|
-
@counts[filter_slug] ||= send("#{filter_slug}_filter_counts")
|
41
|
-
@counts[filter_slug][option.to_s] || 0
|
42
|
-
end
|
43
|
-
|
44
|
-
def filter_collection(filter_slug)
|
45
|
-
collection = @@filter_collections[filter_slug.to_sym]
|
46
|
-
if collection.respond_to?(:call)
|
47
|
-
collection.call.map(&filter_slug.to_sym)
|
48
|
-
else
|
49
|
-
collection
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def exclude_filter_relation?(filter_slug, slug)
|
54
|
-
@filter_params[filter_slug].nil? || (slug.to_s == filter_slug)
|
55
|
-
end
|
56
|
-
|
57
|
-
def counts_relation(filter_slug, slug)
|
58
|
-
query = scoped
|
59
|
-
query = query.send("#{slug}_join")
|
60
|
-
query = query.send("#{slug}_where", filter_collection(slug))
|
61
|
-
query = query.send("#{slug}_where", @filter_params[slug]) unless exclude_filter_relation?(filter_slug, slug)
|
62
|
-
query
|
16
|
+
def filters_as_json(params)
|
17
|
+
@filters.as_json(scoped, params)
|
63
18
|
end
|
64
19
|
|
65
|
-
def
|
66
|
-
|
67
|
-
@@filter_slugs.each do |slug|
|
68
|
-
query = query.counts_relation(filter_slug, slug)
|
69
|
-
end
|
70
|
-
group_by = send("#{filter_slug}_counts_group_by")
|
71
|
-
query.group(group_by).count
|
20
|
+
def filter(params)
|
21
|
+
@filters.relation(scoped, params)
|
72
22
|
end
|
73
23
|
end
|
74
24
|
|
75
|
-
def define_class_methods(filter_slug)
|
76
|
-
filter_slug = filter_slug.to_s
|
77
|
-
metaclass = class << self; self; end
|
78
|
-
|
79
|
-
metaclass.instance_eval do
|
80
|
-
define_method("#{filter_slug}_filter_name") { filter_slug.capitalize }
|
81
|
-
define_method("#{filter_slug}_filter_slug") { filter_slug }
|
82
|
-
define_method("#{filter_slug}_counts_group_by") { filter_slug }
|
83
|
-
define_method("#{filter_slug}_join") { scoped }
|
84
|
-
define_method("#{filter_slug}_where") { |values| where(model_name.to_s.tableize => { filter_slug => values }) }
|
85
|
-
define_method("#{filter_slug}_filter_counts") { filter_slug_filter_counts(filter_slug) }
|
86
|
-
define_method("#{filter_slug}_filter_option_key_name") { new.respond_to?(filter_slug.to_sym) ? filter_slug.to_sym : :id }
|
87
|
-
define_method("#{filter_slug}_relation") { |values| send("#{filter_slug}_join").send("#{filter_slug}_where", values) }
|
88
|
-
define_method("#{filter_slug}_filter_option_count") { |option| filter_option_count(filter_slug, option) }
|
89
|
-
|
90
|
-
define_method("#{filter_slug}_filter_option_value") do |option|
|
91
|
-
[String, Integer].any?{|klass| option.kind_of?(klass)} ? option.to_s : option.send(:id).to_s
|
92
|
-
end
|
93
|
-
|
94
|
-
define_method("#{filter_slug}_filter_option_selected?") do |filter, option|
|
95
|
-
@filter_params[filter].nil? ? false : @filter_params[filter].include?(option.to_s)
|
96
|
-
end
|
97
|
-
|
98
|
-
define_method "#{filter_slug}_filter" do |options|
|
99
|
-
{
|
100
|
-
:filter_slug => send("#{filter_slug}_filter_slug"),
|
101
|
-
:name => send("#{filter_slug}_filter_name"),
|
102
|
-
:options => options
|
103
|
-
}
|
104
|
-
end
|
105
|
-
|
106
|
-
define_method "#{filter_slug}_filter_values" do
|
107
|
-
key = send("#{filter_slug}_filter_option_key_name")
|
108
|
-
collection = filter_collection(filter_slug)
|
109
|
-
if collection.any?{|object| object.kind_of?(Hash) && object.has_key?(key)}
|
110
|
-
collection.map{|object| object[key]}
|
111
|
-
elsif collection.any?{|object| object.respond_to?(key)}
|
112
|
-
collection.map(&key)
|
113
|
-
elsif collection.kind_of?(Array)
|
114
|
-
collection
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
define_method "#{filter_slug}_filter_option_slug" do |option|
|
119
|
-
if [String, Integer].any?{|klass| option.kind_of?(klass)}
|
120
|
-
option.to_s
|
121
|
-
else
|
122
|
-
option.send(:id).to_s
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
define_method "#{filter_slug}_filter_option_name" do |option|
|
127
|
-
collection = filter_collection(filter_slug)
|
128
|
-
if collection.any?{|object| object.kind_of?(Hash) && object.has_key?(:name)}
|
129
|
-
option_object = collection.detect{|object| object[:state] == option.to_s}
|
130
|
-
option_object[:name]
|
131
|
-
elsif collection.any?{|object| object.respond_to?(:name)}
|
132
|
-
option_object = collection.detect{|object| object.send(:id).to_s == option.to_s}
|
133
|
-
option_object.send(:name)
|
134
|
-
else
|
135
|
-
option.to_s
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
define_method "#{filter_slug}_filter_option" do |option, selected|
|
140
|
-
{
|
141
|
-
:slug => send("#{filter_slug}_filter_option_slug", option),
|
142
|
-
:name => send("#{filter_slug}_filter_option_name", option),
|
143
|
-
:value => send("#{filter_slug}_filter_option_value", option),
|
144
|
-
:count => send("#{filter_slug}_filter_option_count", option),
|
145
|
-
:selected => selected
|
146
|
-
}
|
147
|
-
end
|
148
|
-
|
149
|
-
define_method "#{filter_slug}_filters" do |filter|
|
150
|
-
send("#{filter_slug}_filter_values").map do |option|
|
151
|
-
selected = send("#{filter_slug}_filter_option_selected?", filter, option)
|
152
|
-
send("#{filter_slug}_filter_option", option, selected)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
25
|
end
|
158
26
|
end
|
159
27
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'livelist/rails/filter_criteria'
|
3
|
+
|
4
|
+
module Livelist
|
5
|
+
module Rails
|
6
|
+
|
7
|
+
class Filter
|
8
|
+
attr_accessor :slug,
|
9
|
+
:name,
|
10
|
+
:key_name,
|
11
|
+
:model_name,
|
12
|
+
:model_class,
|
13
|
+
:join,
|
14
|
+
:type,
|
15
|
+
:criteria
|
16
|
+
|
17
|
+
# slug should always be a symbol
|
18
|
+
def initialize(options = {})
|
19
|
+
@filter_collection = options[:filter_collection]
|
20
|
+
@slug = options[:slug].to_sym
|
21
|
+
@name = options[:name] || @slug.to_s.capitalize
|
22
|
+
@model_name = options[:model_name]
|
23
|
+
@type = options[:type] || initialize_type
|
24
|
+
@key_name = options[:key_name] || default_key_name
|
25
|
+
@criteria = FilterCriteria.new(
|
26
|
+
:filter => self,
|
27
|
+
:reference_criteria => options[:reference_criteria],
|
28
|
+
:slug => @key_name
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def group_by
|
33
|
+
case @type
|
34
|
+
when :attribute then "#{model_name.tableize}.#{@slug}"
|
35
|
+
when :association then "#{@slug}.id"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def exclude_filter_relation?(matching_filter, params)
|
40
|
+
params.nil? || (self == matching_filter)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_criteria_counts(query, params)
|
44
|
+
@criteria.counts = counts(query, params)
|
45
|
+
end
|
46
|
+
|
47
|
+
def counts_relation(query, filter, params)
|
48
|
+
exclude_params_relation = exclude_filter_relation?(filter, params[@slug])
|
49
|
+
counts_scope = filter.relation(query, params[filter.slug], exclude_params_relation)
|
50
|
+
query.merge(counts_scope)
|
51
|
+
end
|
52
|
+
|
53
|
+
def counts(query, params)
|
54
|
+
@filter_collection.filters.each do |filter|
|
55
|
+
query = counts_relation(query, filter, params)
|
56
|
+
end
|
57
|
+
query.except(:order).group(group_by).count.stringify_keys
|
58
|
+
end
|
59
|
+
|
60
|
+
def relation(query, params, exclude_params_relation)
|
61
|
+
query = query.includes(@slug) if @type == :association
|
62
|
+
query = query.where(where(@criteria.slugs))
|
63
|
+
query = query.where(where(params)) unless exclude_params_relation
|
64
|
+
query
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_key_name
|
68
|
+
case @type
|
69
|
+
when :association then :id
|
70
|
+
when :attribute then @slug
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def table_name
|
75
|
+
case @type
|
76
|
+
when :association then @slug
|
77
|
+
when :attribute then model_name.tableize
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def model_class
|
82
|
+
@model_name.classify.constantize
|
83
|
+
end
|
84
|
+
|
85
|
+
def where(slug_params)
|
86
|
+
{ table_name => { @key_name => slug_params } }
|
87
|
+
end
|
88
|
+
|
89
|
+
def as_json(params)
|
90
|
+
{
|
91
|
+
:filter_slug => @slug,
|
92
|
+
:name => @name,
|
93
|
+
:options => @criteria.as_json(params)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize_type
|
98
|
+
if model_class.column_names.include?(@slug.to_s)
|
99
|
+
:attribute
|
100
|
+
elsif model_class.reflect_on_all_associations.map(&:name).include?(@slug)
|
101
|
+
:association
|
102
|
+
else
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
require 'livelist/rails/filter'
|
3
|
+
|
4
|
+
module Livelist
|
5
|
+
module Rails
|
6
|
+
|
7
|
+
class FilterCollection < HashWithIndifferentAccess
|
8
|
+
alias :filters :values
|
9
|
+
alias :find_filter :[]
|
10
|
+
|
11
|
+
def create_filter(options)
|
12
|
+
options.merge!(:filter_collection => self)
|
13
|
+
self[options[:slug]] = Filter.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def relation(query, params)
|
17
|
+
params ||= {}
|
18
|
+
filters.each do |filter|
|
19
|
+
query = filter.relation(query, params[filter.slug.to_s], params.empty?)
|
20
|
+
end
|
21
|
+
query
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_json(query, params)
|
25
|
+
params ||= {}
|
26
|
+
filters.map do |filter|
|
27
|
+
filter.set_criteria_counts(query, params)
|
28
|
+
filter.as_json(params[filter.slug])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
require 'livelist/rails/filter_criterion'
|
3
|
+
|
4
|
+
module Livelist
|
5
|
+
module Rails
|
6
|
+
|
7
|
+
class FilterCriteria < HashWithIndifferentAccess
|
8
|
+
alias :criteria :values
|
9
|
+
alias :find_criteria :[]
|
10
|
+
attr_reader :slug
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@filter = options[:filter]
|
14
|
+
@reference_criteria = options[:reference_criteria] || default_reference_criteria
|
15
|
+
@reference_criteria = @reference_criteria.call if @reference_criteria.respond_to?(:call)
|
16
|
+
@slug = options[:slug]
|
17
|
+
|
18
|
+
@reference_criteria.each do |reference|
|
19
|
+
create_criterion(reference)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_reference_criteria
|
24
|
+
@filter.model_class.select("distinct #{@filter.slug}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_criterion(reference)
|
28
|
+
filter_criterion = FilterCriterion.new(:filter => @filter, :criteria => self, :reference => reference)
|
29
|
+
self[filter_criterion.slug] = filter_criterion
|
30
|
+
end
|
31
|
+
|
32
|
+
def slugs
|
33
|
+
criteria.map(&:slug)
|
34
|
+
end
|
35
|
+
|
36
|
+
def counts=(counts_hash)
|
37
|
+
criteria.each do |criterion|
|
38
|
+
criterion.count = counts_hash[criterion.slug.to_s] || 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def as_json(params)
|
43
|
+
criteria.map do |criterion|
|
44
|
+
criterion.as_json(params)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Livelist
|
4
|
+
module Rails
|
5
|
+
|
6
|
+
class FilterCriterion
|
7
|
+
attr_accessor :slug,
|
8
|
+
:name,
|
9
|
+
:count,
|
10
|
+
:value,
|
11
|
+
:type,
|
12
|
+
:name_key
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@filter = options[:filter]
|
16
|
+
@criteria = options[:criteria]
|
17
|
+
@reference = options[:reference]
|
18
|
+
@type = infer_type
|
19
|
+
@name_key = options[:name_key] || infer_name_key
|
20
|
+
@slug = infer_slug
|
21
|
+
@name = infer_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def selected?(params)
|
25
|
+
params.nil? ? false : params.include?(slug.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_json(params)
|
29
|
+
{
|
30
|
+
:slug => @filter.slug,
|
31
|
+
:name => @name,
|
32
|
+
:value => @slug.to_s,
|
33
|
+
:count => @count,
|
34
|
+
:selected => selected?(params)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def infer_type
|
41
|
+
raise ArgumentError, "reference is not valid \n #{@reference.inspect}" if @reference.nil?
|
42
|
+
if [String, Symbol, Integer].any?{|klass| @reference.kind_of?(klass)}
|
43
|
+
:scalar
|
44
|
+
elsif @reference.kind_of?(Hash)
|
45
|
+
:hash
|
46
|
+
else
|
47
|
+
:model
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def infer_name_key
|
52
|
+
case @type
|
53
|
+
when :scalar then nil
|
54
|
+
when :hash then :name
|
55
|
+
when :model
|
56
|
+
if @reference.respond_to?(:name)
|
57
|
+
:name
|
58
|
+
elsif @reference.respond_to?(@criteria.slug)
|
59
|
+
@criteria.slug
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def infer_slug
|
65
|
+
case @type
|
66
|
+
when :scalar then @reference
|
67
|
+
when :hash, :model then @reference[@criteria.slug]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def infer_name
|
72
|
+
case @type
|
73
|
+
when :scalar then @reference
|
74
|
+
when :hash then @reference[@name_key]
|
75
|
+
when :model then @reference.send(@name_key)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|