livelist-rails 0.0.11 → 0.0.12
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.
- data/.gitignore +1 -0
- data/.rspec +1 -1
- data/app/assets/javascripts/livelist-rails.js +1 -1
- data/app/assets/javascripts/livelist.coffee +4 -3
- data/app/assets/javascripts/livelist.js +3 -2
- data/app/assets/javascripts/livelist.min.js +1 -1
- data/app/assets/javascripts/mustache.min.js +6 -0
- data/app/assets/javascripts/underscore-min.js +26 -25
- data/lib/livelist/rails/active_record.rb +12 -9
- data/lib/livelist/rails/filter.rb +10 -0
- data/lib/livelist/rails/filter_collection.rb +8 -2
- data/lib/livelist/rails/filter_criteria.rb +18 -8
- data/lib/livelist/rails/railtie.rb +1 -1
- data/lib/livelist/rails/version.rb +4 -4
- data/livelist-rails.gemspec +3 -1
- data/spec/livelist/rails/active_record_spec.rb +66 -7
- data/spec/livelist/rails/filter_criterion_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -4
- metadata +30 -8
- data/app/assets/javascripts/mustache.js +0 -435
data/.gitignore
CHANGED
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
--color
|
2
|
-
--format
|
2
|
+
--format documentation
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class window.Utilities
|
2
|
-
setOptions: (options, context
|
2
|
+
setOptions: (options, context = @) =>
|
3
3
|
_.each( options, (value, option) => context[option] = value )
|
4
4
|
|
5
5
|
class window.LiveList extends Utilities
|
@@ -7,8 +7,9 @@ class window.LiveList extends Utilities
|
|
7
7
|
@listSelector = options.list.renderTo
|
8
8
|
@resourceName = options.global.resourceName
|
9
9
|
@resourceNameSingular = options.global.resourceNameSingular
|
10
|
+
@urlPrefix = options.global.urlPrefix || "/#{@resourceName}"
|
11
|
+
@httpMethod = options.global.httpMethod || 'get'
|
10
12
|
@eventName = "livelist:#{@resourceName}"
|
11
|
-
@urlPrefix = "/#{@resourceName}"
|
12
13
|
@search = new Search(options.search, @)
|
13
14
|
@filters = new Filters(options.filters, @)
|
14
15
|
@pagination = new Pagination(options.pagination, @)
|
@@ -65,7 +66,7 @@ class window.List extends Utilities
|
|
65
66
|
$(@renderTo).html( listHTML )
|
66
67
|
@removeFetchingIndication()
|
67
68
|
|
68
|
-
window.LiveList.version = '0.0.
|
69
|
+
window.LiveList.version = '0.0.7'
|
69
70
|
|
70
71
|
class window.Filters extends Utilities
|
71
72
|
constructor: (options, livelist) ->
|
@@ -29,8 +29,9 @@
|
|
29
29
|
this.listSelector = options.list.renderTo;
|
30
30
|
this.resourceName = options.global.resourceName;
|
31
31
|
this.resourceNameSingular = options.global.resourceNameSingular;
|
32
|
+
this.urlPrefix = options.global.urlPrefix || ("/" + this.resourceName);
|
33
|
+
this.httpMethod = options.global.httpMethod || 'get';
|
32
34
|
this.eventName = "livelist:" + this.resourceName;
|
33
|
-
this.urlPrefix = "/" + this.resourceName;
|
34
35
|
this.search = new Search(options.search, this);
|
35
36
|
this.filters = new Filters(options.filters, this);
|
36
37
|
this.pagination = new Pagination(options.pagination, this);
|
@@ -114,7 +115,7 @@
|
|
114
115
|
|
115
116
|
})(Utilities);
|
116
117
|
|
117
|
-
window.LiveList.version = '0.0.
|
118
|
+
window.LiveList.version = '0.0.7';
|
118
119
|
|
119
120
|
window.Filters = (function(_super) {
|
120
121
|
|
@@ -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.listSelector=a.list.renderTo,this.resourceName=a.global.resourceName,this.resourceNameSingular=a.global.resourceNameSingular,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.urlPrefix=a.global.urlPrefix||"/"+this.resourceName,this.httpMethod=a.global.httpMethod||"get",this.eventName="livelist:"+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.7",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);
|
@@ -0,0 +1,6 @@
|
|
1
|
+
/*! Version: 0.4.2 */
|
2
|
+
/*
|
3
|
+
mustache.js — Logic-less templates in JavaScript
|
4
|
+
|
5
|
+
See http://mustache.github.com/ for more info.
|
6
|
+
*/var Mustache=function(){function g(a){return String(a).replace(/&(?!#?\w+;)|[<>"']/g,function(a){return f[a]||a})}var a=Object.prototype.toString;Array.isArray=Array.isArray||function(b){return a.call(b)=="[object Array]"};var b=String.prototype.trim,c;if(b)c=function(a){return a==null?"":b.call(a)};else{var d,e;/\S/.test(" ")?(d=/^[\s\xA0]+/,e=/[\s\xA0]+$/):(d=/^\s+/,e=/\s+$/),c=function(a){return a==null?"":a.toString().replace(d,"").replace(e,"")}}var f={"&":"&","<":"<",">":">",'"':""","'":"'"},h={},i=function(){};return i.prototype={otag:"{{",ctag:"}}",pragmas:{},buffer:[],pragmas_implemented:{"IMPLICIT-ITERATOR":!0},context:{},render:function(a,b,c,d){d||(this.context=b,this.buffer=[]);if(!this.includes("",a)){if(d)return a;this.send(a);return}a=this.render_pragmas(a);var e=this.render_section(a,b,c);e===!1&&(e=this.render_tags(a,b,c,d));if(d)return e;this.sendLines(e)},send:function(a){a!==""&&this.buffer.push(a)},sendLines:function(a){if(a){var b=a.split("\n");for(var c=0;c<b.length;c++)this.send(b[c])}},render_pragmas:function(a){if(!this.includes("%",a))return a;var b=this,c=this.getCachedRegex("render_pragmas",function(a,b){return new RegExp(a+"%([\\w-]+) ?([\\w]+=[\\w]+)?"+b,"g")});return a.replace(c,function(a,c,d){if(!b.pragmas_implemented[c])throw{message:"This implementation of mustache doesn't understand the '"+c+"' pragma"};b.pragmas[c]={};if(d){var e=d.split("=");b.pragmas[c][e[0]]=e[1]}return""})},render_partial:function(a,b,d){a=c(a);if(!d||d[a]===undefined)throw{message:"unknown_partial '"+a+"'"};return!b||typeof b[a]!="object"?this.render(d[a],b,d,!0):this.render(d[a],b[a],d,!0)},render_section:function(a,b,c){if(!this.includes("#",a)&&!this.includes("^",a))return!1;var d=this,e=this.getCachedRegex("render_section",function(a,b){return new RegExp("^([\\s\\S]*?)"+a+"(\\^|\\#)\\s*(.+?)\\s*"+b+"\n*([\\s\\S]*?)"+a+"\\/\\s*\\3\\s*"+b+"\\s*([\\s\\S]*)$","g")});return a.replace(e,function(a,e,f,g,h,i){var j=e?d.render_tags(e,b,c,!0):"",k=i?d.render(i,b,c,!0):"",l,m=d.find(g,b);return f==="^"?!m||Array.isArray(m)&&m.length===0?l=d.render(h,b,c,!0):l="":f==="#"&&(Array.isArray(m)?l=d.map(m,function(a){return d.render(h,d.create_context(a),c,!0)}).join(""):d.is_object(m)?l=d.render(h,d.create_context(m),c,!0):typeof m=="function"?l=m.call(b,h,function(a){return d.render(a,b,c,!0)}):m?l=d.render(h,b,c,!0):l=""),j+l+k})},render_tags:function(a,b,c,d){var e=this,f=function(){return e.getCachedRegex("render_tags",function(a,b){return new RegExp(a+"(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?"+b+"+","g")})},h=f(),i=function(a,d,i){switch(d){case"!":return"";case"=":return e.set_delimiters(i),h=f(),"";case">":return e.render_partial(i,b,c);case"{":case"&":return e.find(i,b);default:return g(e.find(i,b))}},j=a.split("\n");for(var k=0;k<j.length;k++)j[k]=j[k].replace(h,i,this),d||this.send(j[k]);if(d)return j.join("\n")},set_delimiters:function(a){var b=a.split(" ");this.otag=this.escape_regex(b[0]),this.ctag=this.escape_regex(b[1])},escape_regex:function(a){if(!arguments.callee.sRE){var b=["/",".","*","+","?","|","(",")","[","]","{","}","\\"];arguments.callee.sRE=new RegExp("(\\"+b.join("|\\")+")","g")}return a.replace(arguments.callee.sRE,"\\$1")},find:function(a,b){function d(a){return a===!1||a===0||a}a=c(a);var e;if(a.match(/([a-z_]+)\./ig)){var f=this.walk_context(a,b);d(f)&&(e=f)}else d(b[a])?e=b[a]:d(this.context[a])&&(e=this.context[a]);return typeof e=="function"?e.apply(b):e!==undefined?e:""},walk_context:function(a,b){var c=a.split("."),d=b[c[0]]!=undefined?b:this.context,e=d[c.shift()];while(e!=undefined&&c.length>0)d=e,e=e[c.shift()];return typeof e=="function"?e.apply(d):e},includes:function(a,b){return b.indexOf(this.otag+a)!=-1},create_context:function(a){if(this.is_object(a))return a;var b=".";this.pragmas["IMPLICIT-ITERATOR"]&&(b=this.pragmas["IMPLICIT-ITERATOR"].iterator);var c={};return c[b]=a,c},is_object:function(a){return a&&typeof a=="object"},map:function(a,b){if(typeof a.map=="function")return a.map(b);var c=[],d=a.length;for(var e=0;e<d;e++)c.push(b(a[e]));return c},getCachedRegex:function(a,b){var c=h[this.otag];c||(c=h[this.otag]={});var d=c[this.ctag];d||(d=c[this.ctag]={});var e=d[a];return e||(e=d[a]=b(this.otag,this.ctag)),e}},{name:"mustache.js",version:"0.4.2",to_html:function(a,b,c,d){var e=new i;d&&(e.send=d),e.render(a,b||{},c);if(!d)return e.buffer.join("\n")}}}();
|
@@ -1,30 +1,31 @@
|
|
1
|
-
// Underscore.js 1.
|
2
|
-
// (c) 2009-
|
1
|
+
// Underscore.js 1.3.1
|
2
|
+
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
3
3
|
// Underscore is freely distributable under the MIT license.
|
4
4
|
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
5
|
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6
6
|
// For all details and documentation:
|
7
7
|
// http://documentcloud.github.com/underscore
|
8
|
-
(function(){function
|
9
|
-
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&
|
10
|
-
h)&&!f--)break;g=!f}}d.pop();return g}var
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
var
|
22
|
-
c){var d={};c||(c=b.identity);return function(){var
|
23
|
-
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=
|
24
|
-
function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=
|
25
|
-
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return
|
26
|
-
|
27
|
-
|
28
|
-
b[c]=a[c])})};var
|
29
|
-
|
30
|
-
a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];
|
8
|
+
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
9
|
+
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
|
10
|
+
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
|
11
|
+
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
|
12
|
+
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
|
13
|
+
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
|
14
|
+
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
|
15
|
+
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
|
16
|
+
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
|
17
|
+
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
|
18
|
+
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
|
19
|
+
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
|
20
|
+
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
|
21
|
+
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
|
22
|
+
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
|
23
|
+
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
|
24
|
+
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
|
25
|
+
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
|
26
|
+
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
|
27
|
+
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),
|
28
|
+
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
|
29
|
+
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
|
30
|
+
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
|
31
|
+
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
|
@@ -5,23 +5,26 @@ module Livelist
|
|
5
5
|
module Rails
|
6
6
|
module ActiveRecord
|
7
7
|
|
8
|
-
def
|
8
|
+
def filters
|
9
9
|
@filters ||= FilterCollection.new
|
10
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def filter_for(slug, options = {})
|
13
|
+
filters.create_filter(
|
11
14
|
:reference_criteria => options[:reference_criteria],
|
15
|
+
:name => options[:name],
|
12
16
|
:model_name => model_name,
|
13
17
|
:slug => slug
|
14
18
|
)
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
def filter(params)
|
21
|
-
@filters.relation(scoped, params)
|
22
|
-
end
|
21
|
+
def filters_as_json(params, options = {})
|
22
|
+
filters.as_json(scoped, params, options)
|
23
23
|
end
|
24
24
|
|
25
|
+
def filter(params, options = {})
|
26
|
+
filters.relation(scoped, params, options)
|
27
|
+
end
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
@@ -5,6 +5,10 @@ module Livelist
|
|
5
5
|
module Rails
|
6
6
|
|
7
7
|
class Filter
|
8
|
+
DEFAULT_FILTER_OPTIONS = {
|
9
|
+
:reference_criteria => nil
|
10
|
+
}
|
11
|
+
|
8
12
|
attr_accessor :slug,
|
9
13
|
:name,
|
10
14
|
:key_name,
|
@@ -29,6 +33,12 @@ module Livelist
|
|
29
33
|
)
|
30
34
|
end
|
31
35
|
|
36
|
+
def prepare_options(options)
|
37
|
+
options ||= {}
|
38
|
+
options.reverse_merge!(DEFAULT_FILTER_OPTIONS)
|
39
|
+
@criteria.set_criteria(options[:reference_criteria]) if options[:reference_criteria]
|
40
|
+
end
|
41
|
+
|
32
42
|
def group_by
|
33
43
|
case @type
|
34
44
|
when :attribute then "#{model_name.tableize}.#{@slug}"
|
@@ -8,22 +8,28 @@ module Livelist
|
|
8
8
|
alias :filters :values
|
9
9
|
alias :find_filter :[]
|
10
10
|
|
11
|
+
def slugs
|
12
|
+
keys.map(&:to_sym)
|
13
|
+
end
|
14
|
+
|
11
15
|
def create_filter(options)
|
12
16
|
options.merge!(:filter_collection => self)
|
13
17
|
self[options[:slug]] = Filter.new(options)
|
14
18
|
end
|
15
19
|
|
16
|
-
def relation(query, params)
|
20
|
+
def relation(query, params, options = {})
|
17
21
|
params ||= {}
|
18
22
|
filters.each do |filter|
|
23
|
+
filter.prepare_options(options[filter.slug])
|
19
24
|
query = filter.relation(query, params[filter.slug.to_s], params.empty?)
|
20
25
|
end
|
21
26
|
query
|
22
27
|
end
|
23
28
|
|
24
|
-
def as_json(query, params)
|
29
|
+
def as_json(query, params, options = {})
|
25
30
|
params ||= {}
|
26
31
|
filters.map do |filter|
|
32
|
+
filter.prepare_options(options[filter.slug])
|
27
33
|
filter.set_criteria_counts(query, params)
|
28
34
|
filter.as_json(params[filter.slug])
|
29
35
|
end
|
@@ -10,18 +10,28 @@ module Livelist
|
|
10
10
|
attr_reader :slug
|
11
11
|
|
12
12
|
def initialize(options)
|
13
|
-
@filter
|
14
|
-
@
|
15
|
-
@reference_criteria = @reference_criteria.call if @reference_criteria.respond_to?(:call)
|
16
|
-
@slug = options[:slug]
|
13
|
+
@filter = options[:filter]
|
14
|
+
@slug = options[:slug]
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
initialize_criteria(options[:reference_criteria])
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_criteria(reference_criteria)
|
20
|
+
reference_criteria ||= default_reference_criteria
|
21
|
+
reference_criteria = reference_criteria.call if reference_criteria.respond_to?(:call)
|
22
|
+
reference_criteria.each { |reference| create_criterion(reference) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_criteria(reference_criteria)
|
26
|
+
clear
|
27
|
+
reference_criteria.each { |reference| create_criterion(reference) }
|
21
28
|
end
|
22
29
|
|
23
30
|
def default_reference_criteria
|
24
|
-
|
31
|
+
case @filter.type
|
32
|
+
when :attribute then @filter.model_class.select("distinct #{@filter.slug}")
|
33
|
+
when :association then @filter.slug.to_s.classify.constantize.scoped
|
34
|
+
end
|
25
35
|
end
|
26
36
|
|
27
37
|
def create_criterion(reference)
|
@@ -18,7 +18,7 @@ module Livelist
|
|
18
18
|
livelist_filename = 'livelist'
|
19
19
|
end
|
20
20
|
|
21
|
-
config.action_view.javascript_expansions[:livelist_dependencies] = %W(mustache underscore
|
21
|
+
config.action_view.javascript_expansions[:livelist_dependencies] = %W(mustache.min underscore-min)
|
22
22
|
config.action_view.javascript_expansions[:livelist] = [livelist_filename]
|
23
23
|
end
|
24
24
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Livelist
|
2
2
|
module Rails
|
3
|
-
VERSION = '0.0.
|
4
|
-
LIVELIST_VERSION = '
|
5
|
-
MUSTACHE_VERSION = '
|
6
|
-
UNDERSCORE_VERSION = '1.
|
3
|
+
VERSION = '0.0.12'
|
4
|
+
LIVELIST_VERSION = '0.0.7'
|
5
|
+
MUSTACHE_VERSION = '0.4.2'
|
6
|
+
UNDERSCORE_VERSION = '1.3.1'
|
7
7
|
end
|
8
8
|
end
|
data/livelist-rails.gemspec
CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
+
s.add_runtime_dependency 'activesupport'
|
22
|
+
s.add_runtime_dependency 'activerecord'
|
21
23
|
s.add_development_dependency 'rspec'
|
22
|
-
|
24
|
+
s.add_development_dependency 'simplecov'
|
23
25
|
end
|
@@ -1,23 +1,82 @@
|
|
1
1
|
require 'spec_helper.rb'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
2
3
|
require File.expand_path('./lib/livelist/rails/active_record.rb')
|
3
4
|
|
4
5
|
describe Livelist::Rails::ActiveRecord do
|
6
|
+
module ActiveRecord::ClassMethods
|
7
|
+
def model_name
|
8
|
+
'User'
|
9
|
+
end
|
10
|
+
|
11
|
+
def column_names
|
12
|
+
['state', 'status']
|
13
|
+
end
|
14
|
+
|
15
|
+
def reflect_on_all_associations
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
def select(*args)
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
|
23
|
+
def scoped
|
24
|
+
ActiveRecord::Relation.new(nil, nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
5
28
|
class User
|
29
|
+
extend ActiveRecord::ClassMethods
|
6
30
|
extend Livelist::Rails::ActiveRecord
|
7
|
-
filters :status, :state
|
8
31
|
end
|
9
32
|
|
10
33
|
subject { User }
|
11
34
|
|
12
|
-
context :
|
13
|
-
|
35
|
+
context :filters do
|
36
|
+
it 'filters should be a FilterCollection object' do
|
37
|
+
subject.filters.should be_kind_of(Livelist::Rails::FilterCollection)
|
38
|
+
end
|
14
39
|
end
|
15
40
|
|
16
|
-
|
41
|
+
context :filter_for do
|
42
|
+
let(:name) { 'State' }
|
43
|
+
let(:reference_criteria) { ['South Carolina', 'Virginia'] }
|
44
|
+
let(:options) do
|
45
|
+
{
|
46
|
+
:reference_criteria => reference_criteria,
|
47
|
+
:name => name,
|
48
|
+
:model_name => 'User',
|
49
|
+
:slug => :state
|
50
|
+
}
|
51
|
+
end
|
17
52
|
|
18
|
-
|
53
|
+
it 'should call create a filter with the proper options' do
|
54
|
+
subject.filters.should_receive(:create_filter).with(options)
|
55
|
+
subject.filter_for(:state, :reference_criteria => reference_criteria, :name => name)
|
56
|
+
end
|
57
|
+
end
|
19
58
|
|
20
|
-
|
59
|
+
context 'Runtime Methods' do
|
60
|
+
let(:options) do
|
61
|
+
{}
|
62
|
+
end
|
21
63
|
|
22
|
-
|
64
|
+
let(:params) do
|
65
|
+
{}
|
66
|
+
end
|
67
|
+
|
68
|
+
context :filters_as_json do
|
69
|
+
it do
|
70
|
+
subject.filters.should_receive(:as_json).with(nil, params, options)
|
71
|
+
subject.filters_as_json(params, options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context :filter do
|
76
|
+
it do
|
77
|
+
subject.filters.should_receive(:filter).with(nil, params, options)
|
78
|
+
subject.filter(params, options)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
23
82
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
3
|
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
4
4
|
# loaded once.
|
5
|
-
|
5
|
+
|
6
|
+
require 'simplecov'
|
7
|
+
SimpleCov.start
|
8
|
+
|
6
9
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
10
|
RSpec.configure do |config|
|
8
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
-
config.run_all_when_everything_filtered = true
|
10
|
-
config.filter_run :focus
|
11
|
+
#config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
+
#config.run_all_when_everything_filtered = true
|
13
|
+
#config.filter_run :focus
|
11
14
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: livelist-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,33 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-28 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &8072460 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *8072460
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activerecord
|
27
|
+
requirement: &8072040 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *8072040
|
14
36
|
- !ruby/object:Gem::Dependency
|
15
37
|
name: rspec
|
16
|
-
requirement: &
|
38
|
+
requirement: &8071620 !ruby/object:Gem::Requirement
|
17
39
|
none: false
|
18
40
|
requirements:
|
19
41
|
- - ! '>='
|
@@ -21,10 +43,10 @@ dependencies:
|
|
21
43
|
version: '0'
|
22
44
|
type: :development
|
23
45
|
prerelease: false
|
24
|
-
version_requirements: *
|
46
|
+
version_requirements: *8071620
|
25
47
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
27
|
-
requirement: &
|
48
|
+
name: simplecov
|
49
|
+
requirement: &8071200 !ruby/object:Gem::Requirement
|
28
50
|
none: false
|
29
51
|
requirements:
|
30
52
|
- - ! '>='
|
@@ -32,7 +54,7 @@ dependencies:
|
|
32
54
|
version: '0'
|
33
55
|
type: :development
|
34
56
|
prerelease: false
|
35
|
-
version_requirements: *
|
57
|
+
version_requirements: *8071200
|
36
58
|
description: ! 'livelist-rails is a Rails 3.1 Engine/Extension incorporating the following
|
37
59
|
javascript libraries: Mustache.js, underscore.js, jQuery and livelist.js, and providing
|
38
60
|
ActiveRecord filtering extenstions.'
|
@@ -52,7 +74,7 @@ files:
|
|
52
74
|
- app/assets/javascripts/livelist.coffee
|
53
75
|
- app/assets/javascripts/livelist.js
|
54
76
|
- app/assets/javascripts/livelist.min.js
|
55
|
-
- app/assets/javascripts/mustache.js
|
77
|
+
- app/assets/javascripts/mustache.min.js
|
56
78
|
- app/assets/javascripts/underscore-min.js
|
57
79
|
- lib/livelist-rails.rb
|
58
80
|
- lib/livelist/rails.rb
|
@@ -1,435 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
mustache.js — Logic-less templates in JavaScript
|
3
|
-
|
4
|
-
See http://mustache.github.com/ for more info.
|
5
|
-
*/
|
6
|
-
|
7
|
-
var Mustache = function () {
|
8
|
-
var _toString = Object.prototype.toString;
|
9
|
-
|
10
|
-
Array.isArray = Array.isArray || function (obj) {
|
11
|
-
return _toString.call(obj) == "[object Array]";
|
12
|
-
}
|
13
|
-
|
14
|
-
var _trim = String.prototype.trim, trim;
|
15
|
-
|
16
|
-
if (_trim) {
|
17
|
-
trim = function (text) {
|
18
|
-
return text == null ? "" : _trim.call(text);
|
19
|
-
}
|
20
|
-
} else {
|
21
|
-
var trimLeft, trimRight;
|
22
|
-
|
23
|
-
// IE doesn't match non-breaking spaces with \s.
|
24
|
-
if ((/\S/).test("\xA0")) {
|
25
|
-
trimLeft = /^[\s\xA0]+/;
|
26
|
-
trimRight = /[\s\xA0]+$/;
|
27
|
-
} else {
|
28
|
-
trimLeft = /^\s+/;
|
29
|
-
trimRight = /\s+$/;
|
30
|
-
}
|
31
|
-
|
32
|
-
trim = function (text) {
|
33
|
-
return text == null ? "" :
|
34
|
-
text.toString().replace(trimLeft, "").replace(trimRight, "");
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
var escapeMap = {
|
39
|
-
"&": "&",
|
40
|
-
"<": "<",
|
41
|
-
">": ">",
|
42
|
-
'"': '"',
|
43
|
-
"'": '''
|
44
|
-
};
|
45
|
-
|
46
|
-
function escapeHTML(string) {
|
47
|
-
return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
|
48
|
-
return escapeMap[s] || s;
|
49
|
-
});
|
50
|
-
}
|
51
|
-
|
52
|
-
var regexCache = {};
|
53
|
-
var Renderer = function () {};
|
54
|
-
|
55
|
-
Renderer.prototype = {
|
56
|
-
otag: "{{",
|
57
|
-
ctag: "}}",
|
58
|
-
pragmas: {},
|
59
|
-
buffer: [],
|
60
|
-
pragmas_implemented: {
|
61
|
-
"IMPLICIT-ITERATOR": true
|
62
|
-
},
|
63
|
-
context: {},
|
64
|
-
|
65
|
-
render: function (template, context, partials, in_recursion) {
|
66
|
-
// reset buffer & set context
|
67
|
-
if (!in_recursion) {
|
68
|
-
this.context = context;
|
69
|
-
this.buffer = []; // TODO: make this non-lazy
|
70
|
-
}
|
71
|
-
|
72
|
-
// fail fast
|
73
|
-
if (!this.includes("", template)) {
|
74
|
-
if (in_recursion) {
|
75
|
-
return template;
|
76
|
-
} else {
|
77
|
-
this.send(template);
|
78
|
-
return;
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
// get the pragmas together
|
83
|
-
template = this.render_pragmas(template);
|
84
|
-
|
85
|
-
// render the template
|
86
|
-
var html = this.render_section(template, context, partials);
|
87
|
-
|
88
|
-
// render_section did not find any sections, we still need to render the tags
|
89
|
-
if (html === false) {
|
90
|
-
html = this.render_tags(template, context, partials, in_recursion);
|
91
|
-
}
|
92
|
-
|
93
|
-
if (in_recursion) {
|
94
|
-
return html;
|
95
|
-
} else {
|
96
|
-
this.sendLines(html);
|
97
|
-
}
|
98
|
-
},
|
99
|
-
|
100
|
-
/*
|
101
|
-
Sends parsed lines
|
102
|
-
*/
|
103
|
-
send: function (line) {
|
104
|
-
if (line !== "") {
|
105
|
-
this.buffer.push(line);
|
106
|
-
}
|
107
|
-
},
|
108
|
-
|
109
|
-
sendLines: function (text) {
|
110
|
-
if (text) {
|
111
|
-
var lines = text.split("\n");
|
112
|
-
for (var i = 0; i < lines.length; i++) {
|
113
|
-
this.send(lines[i]);
|
114
|
-
}
|
115
|
-
}
|
116
|
-
},
|
117
|
-
|
118
|
-
/*
|
119
|
-
Looks for %PRAGMAS
|
120
|
-
*/
|
121
|
-
render_pragmas: function (template) {
|
122
|
-
// no pragmas
|
123
|
-
if (!this.includes("%", template)) {
|
124
|
-
return template;
|
125
|
-
}
|
126
|
-
|
127
|
-
var that = this;
|
128
|
-
var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
|
129
|
-
return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
|
130
|
-
});
|
131
|
-
|
132
|
-
return template.replace(regex, function (match, pragma, options) {
|
133
|
-
if (!that.pragmas_implemented[pragma]) {
|
134
|
-
throw({message:
|
135
|
-
"This implementation of mustache doesn't understand the '" +
|
136
|
-
pragma + "' pragma"});
|
137
|
-
}
|
138
|
-
that.pragmas[pragma] = {};
|
139
|
-
if (options) {
|
140
|
-
var opts = options.split("=");
|
141
|
-
that.pragmas[pragma][opts[0]] = opts[1];
|
142
|
-
}
|
143
|
-
return "";
|
144
|
-
// ignore unknown pragmas silently
|
145
|
-
});
|
146
|
-
},
|
147
|
-
|
148
|
-
/*
|
149
|
-
Tries to find a partial in the curent scope and render it
|
150
|
-
*/
|
151
|
-
render_partial: function (name, context, partials) {
|
152
|
-
name = trim(name);
|
153
|
-
if (!partials || partials[name] === undefined) {
|
154
|
-
throw({message: "unknown_partial '" + name + "'"});
|
155
|
-
}
|
156
|
-
if (!context || typeof context[name] != "object") {
|
157
|
-
return this.render(partials[name], context, partials, true);
|
158
|
-
}
|
159
|
-
return this.render(partials[name], context[name], partials, true);
|
160
|
-
},
|
161
|
-
|
162
|
-
/*
|
163
|
-
Renders inverted (^) and normal (#) sections
|
164
|
-
*/
|
165
|
-
render_section: function (template, context, partials) {
|
166
|
-
if (!this.includes("#", template) && !this.includes("^", template)) {
|
167
|
-
// did not render anything, there were no sections
|
168
|
-
return false;
|
169
|
-
}
|
170
|
-
|
171
|
-
var that = this;
|
172
|
-
|
173
|
-
var regex = this.getCachedRegex("render_section", function (otag, ctag) {
|
174
|
-
// This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
|
175
|
-
return new RegExp(
|
176
|
-
"^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1)
|
177
|
-
|
178
|
-
otag + // {{
|
179
|
-
"(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3)
|
180
|
-
ctag + // }}
|
181
|
-
|
182
|
-
"\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped
|
183
|
-
|
184
|
-
otag + // {{
|
185
|
-
"\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag).
|
186
|
-
ctag + // }}
|
187
|
-
|
188
|
-
"\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped.
|
189
|
-
|
190
|
-
"g");
|
191
|
-
});
|
192
|
-
|
193
|
-
|
194
|
-
// for each {{#foo}}{{/foo}} section do...
|
195
|
-
return template.replace(regex, function (match, before, type, name, content, after) {
|
196
|
-
// before contains only tags, no sections
|
197
|
-
var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
|
198
|
-
|
199
|
-
// after may contain both sections and tags, so use full rendering function
|
200
|
-
renderedAfter = after ? that.render(after, context, partials, true) : "",
|
201
|
-
|
202
|
-
// will be computed below
|
203
|
-
renderedContent,
|
204
|
-
|
205
|
-
value = that.find(name, context);
|
206
|
-
|
207
|
-
if (type === "^") { // inverted section
|
208
|
-
if (!value || Array.isArray(value) && value.length === 0) {
|
209
|
-
// false or empty list, render it
|
210
|
-
renderedContent = that.render(content, context, partials, true);
|
211
|
-
} else {
|
212
|
-
renderedContent = "";
|
213
|
-
}
|
214
|
-
} else if (type === "#") { // normal section
|
215
|
-
if (Array.isArray(value)) { // Enumerable, Let's loop!
|
216
|
-
renderedContent = that.map(value, function (row) {
|
217
|
-
return that.render(content, that.create_context(row), partials, true);
|
218
|
-
}).join("");
|
219
|
-
} else if (that.is_object(value)) { // Object, Use it as subcontext!
|
220
|
-
renderedContent = that.render(content, that.create_context(value),
|
221
|
-
partials, true);
|
222
|
-
} else if (typeof value == "function") {
|
223
|
-
// higher order section
|
224
|
-
renderedContent = value.call(context, content, function (text) {
|
225
|
-
return that.render(text, context, partials, true);
|
226
|
-
});
|
227
|
-
} else if (value) { // boolean section
|
228
|
-
renderedContent = that.render(content, context, partials, true);
|
229
|
-
} else {
|
230
|
-
renderedContent = "";
|
231
|
-
}
|
232
|
-
}
|
233
|
-
|
234
|
-
return renderedBefore + renderedContent + renderedAfter;
|
235
|
-
});
|
236
|
-
},
|
237
|
-
|
238
|
-
/*
|
239
|
-
Replace {{foo}} and friends with values from our view
|
240
|
-
*/
|
241
|
-
render_tags: function (template, context, partials, in_recursion) {
|
242
|
-
// tit for tat
|
243
|
-
var that = this;
|
244
|
-
|
245
|
-
var new_regex = function () {
|
246
|
-
return that.getCachedRegex("render_tags", function (otag, ctag) {
|
247
|
-
return new RegExp(otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + ctag + "+", "g");
|
248
|
-
});
|
249
|
-
};
|
250
|
-
|
251
|
-
var regex = new_regex();
|
252
|
-
var tag_replace_callback = function (match, operator, name) {
|
253
|
-
switch(operator) {
|
254
|
-
case "!": // ignore comments
|
255
|
-
return "";
|
256
|
-
case "=": // set new delimiters, rebuild the replace regexp
|
257
|
-
that.set_delimiters(name);
|
258
|
-
regex = new_regex();
|
259
|
-
return "";
|
260
|
-
case ">": // render partial
|
261
|
-
return that.render_partial(name, context, partials);
|
262
|
-
case "{": // the triple mustache is unescaped
|
263
|
-
return that.find(name, context);
|
264
|
-
default: // escape the value
|
265
|
-
return escapeHTML(that.find(name, context));
|
266
|
-
}
|
267
|
-
};
|
268
|
-
var lines = template.split("\n");
|
269
|
-
for(var i = 0; i < lines.length; i++) {
|
270
|
-
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
|
271
|
-
if (!in_recursion) {
|
272
|
-
this.send(lines[i]);
|
273
|
-
}
|
274
|
-
}
|
275
|
-
|
276
|
-
if (in_recursion) {
|
277
|
-
return lines.join("\n");
|
278
|
-
}
|
279
|
-
},
|
280
|
-
|
281
|
-
set_delimiters: function (delimiters) {
|
282
|
-
var dels = delimiters.split(" ");
|
283
|
-
this.otag = this.escape_regex(dels[0]);
|
284
|
-
this.ctag = this.escape_regex(dels[1]);
|
285
|
-
},
|
286
|
-
|
287
|
-
escape_regex: function (text) {
|
288
|
-
// thank you Simon Willison
|
289
|
-
if (!arguments.callee.sRE) {
|
290
|
-
var specials = [
|
291
|
-
'/', '.', '*', '+', '?', '|',
|
292
|
-
'(', ')', '[', ']', '{', '}', '\\'
|
293
|
-
];
|
294
|
-
arguments.callee.sRE = new RegExp(
|
295
|
-
'(\\' + specials.join('|\\') + ')', 'g'
|
296
|
-
);
|
297
|
-
}
|
298
|
-
return text.replace(arguments.callee.sRE, '\\$1');
|
299
|
-
},
|
300
|
-
|
301
|
-
/*
|
302
|
-
find `name` in current `context`. That is find me a value
|
303
|
-
from the view object
|
304
|
-
*/
|
305
|
-
find: function (name, context) {
|
306
|
-
name = trim(name);
|
307
|
-
|
308
|
-
// Checks whether a value is thruthy or false or 0
|
309
|
-
function is_kinda_truthy(bool) {
|
310
|
-
return bool === false || bool === 0 || bool;
|
311
|
-
}
|
312
|
-
|
313
|
-
var value;
|
314
|
-
|
315
|
-
// check for dot notation eg. foo.bar
|
316
|
-
if (name.match(/([a-z_]+)\./ig)) {
|
317
|
-
var childValue = this.walk_context(name, context);
|
318
|
-
if (is_kinda_truthy(childValue)) {
|
319
|
-
value = childValue;
|
320
|
-
}
|
321
|
-
} else {
|
322
|
-
if (is_kinda_truthy(context[name])) {
|
323
|
-
value = context[name];
|
324
|
-
} else if (is_kinda_truthy(this.context[name])) {
|
325
|
-
value = this.context[name];
|
326
|
-
}
|
327
|
-
}
|
328
|
-
|
329
|
-
if (typeof value == "function") {
|
330
|
-
return value.apply(context);
|
331
|
-
}
|
332
|
-
if (value !== undefined) {
|
333
|
-
return value;
|
334
|
-
}
|
335
|
-
// silently ignore unkown variables
|
336
|
-
return "";
|
337
|
-
},
|
338
|
-
|
339
|
-
walk_context: function (name, context) {
|
340
|
-
var path = name.split('.');
|
341
|
-
// if the var doesn't exist in current context, check the top level context
|
342
|
-
var value_context = (context[path[0]] != undefined) ? context : this.context;
|
343
|
-
var value = value_context[path.shift()];
|
344
|
-
while (value != undefined && path.length > 0) {
|
345
|
-
value_context = value;
|
346
|
-
value = value[path.shift()];
|
347
|
-
}
|
348
|
-
// if the value is a function, call it, binding the correct context
|
349
|
-
if (typeof value == "function") {
|
350
|
-
return value.apply(value_context);
|
351
|
-
}
|
352
|
-
return value;
|
353
|
-
},
|
354
|
-
|
355
|
-
// Utility methods
|
356
|
-
|
357
|
-
/* includes tag */
|
358
|
-
includes: function (needle, haystack) {
|
359
|
-
return haystack.indexOf(this.otag + needle) != -1;
|
360
|
-
},
|
361
|
-
|
362
|
-
// by @langalex, support for arrays of strings
|
363
|
-
create_context: function (_context) {
|
364
|
-
if (this.is_object(_context)) {
|
365
|
-
return _context;
|
366
|
-
} else {
|
367
|
-
var iterator = ".";
|
368
|
-
if (this.pragmas["IMPLICIT-ITERATOR"]) {
|
369
|
-
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
|
370
|
-
}
|
371
|
-
var ctx = {};
|
372
|
-
ctx[iterator] = _context;
|
373
|
-
return ctx;
|
374
|
-
}
|
375
|
-
},
|
376
|
-
|
377
|
-
is_object: function (a) {
|
378
|
-
return a && typeof a == "object";
|
379
|
-
},
|
380
|
-
|
381
|
-
/*
|
382
|
-
Why, why, why? Because IE. Cry, cry cry.
|
383
|
-
*/
|
384
|
-
map: function (array, fn) {
|
385
|
-
if (typeof array.map == "function") {
|
386
|
-
return array.map(fn);
|
387
|
-
} else {
|
388
|
-
var r = [];
|
389
|
-
var l = array.length;
|
390
|
-
for(var i = 0; i < l; i++) {
|
391
|
-
r.push(fn(array[i]));
|
392
|
-
}
|
393
|
-
return r;
|
394
|
-
}
|
395
|
-
},
|
396
|
-
|
397
|
-
getCachedRegex: function (name, generator) {
|
398
|
-
var byOtag = regexCache[this.otag];
|
399
|
-
if (!byOtag) {
|
400
|
-
byOtag = regexCache[this.otag] = {};
|
401
|
-
}
|
402
|
-
|
403
|
-
var byCtag = byOtag[this.ctag];
|
404
|
-
if (!byCtag) {
|
405
|
-
byCtag = byOtag[this.ctag] = {};
|
406
|
-
}
|
407
|
-
|
408
|
-
var regex = byCtag[name];
|
409
|
-
if (!regex) {
|
410
|
-
regex = byCtag[name] = generator(this.otag, this.ctag);
|
411
|
-
}
|
412
|
-
|
413
|
-
return regex;
|
414
|
-
}
|
415
|
-
};
|
416
|
-
|
417
|
-
return({
|
418
|
-
name: "mustache.js",
|
419
|
-
version: "0.4.0-dev",
|
420
|
-
|
421
|
-
/*
|
422
|
-
Turns a template and view into HTML
|
423
|
-
*/
|
424
|
-
to_html: function (template, view, partials, send_fun) {
|
425
|
-
var renderer = new Renderer();
|
426
|
-
if (send_fun) {
|
427
|
-
renderer.send = send_fun;
|
428
|
-
}
|
429
|
-
renderer.render(template, view || {}, partials);
|
430
|
-
if (!send_fun) {
|
431
|
-
return renderer.buffer.join("\n");
|
432
|
-
}
|
433
|
-
}
|
434
|
-
});
|
435
|
-
}();
|