mincer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8e653063bab2730285042586172b1542c9049bb3
4
+ data.tar.gz: ca4b849d4f77baa0bda11828ac41cc24fae54569
5
+ SHA512:
6
+ metadata.gz: f08dfbf9d5b4a37b0232421a2265fa6759e837827847ed3ab9aa552dc902d695a5c9a2ea98aae97c01a3719d557ac33e3489f03c40cbc244b61cdce03c53c376
7
+ data.tar.gz: c144b4b21d23414def052b9b904187246d3d134d13ce0242f2507ff6a581eac2a8a97a34422f03cff4b5b122bc53e569b999d4ac1f47f35dcfa03733fdd2b0f2
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
19
+ Guardfile
20
+ atlassian-ide-plugin.xml
21
+ .rspec
22
+ spec/database.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mincer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alex Krasinsky
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Mincer
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mincer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install mincer
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+
31
+ ## License
32
+
33
+ Copyright (c) 2013-2014 Alex Krasynskyi
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining
36
+ a copy of this software and associated documentation files (the
37
+ "Software"), to deal in the Software without restriction, including
38
+ without limitation the rights to use, copy, modify, merge, publish,
39
+ distribute, sublicense, and/or sell copies of the Software, and to
40
+ permit persons to whom the Software is furnished to do so, subject to
41
+ the following conditions:
42
+
43
+ The above copyright notice and this permission notice shall be
44
+ included in all copies or substantial portions of the Software.
45
+
46
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
47
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
48
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
49
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
50
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
51
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
52
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/mincer.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'mincer/version'
2
+
3
+
4
+ require 'mincer/base'
5
+
6
+ module Mincer
7
+ def self.processors
8
+ @processors ||= []
9
+ end
10
+
11
+ def self.postgres?
12
+ self.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) rescue false
13
+ end
14
+
15
+ def self.connection
16
+ ::ActiveRecord::Base.connection()
17
+ end
18
+ end
19
+
20
+
21
+ # Loading processors
22
+ require 'mincer/processors/sort'
23
+ require 'mincer/processors/paginate'
24
+ require 'mincer/processors/search'
25
+ require 'mincer/processors/cache_digest'
26
+ require 'mincer/processors/pg_json_dumper'
27
+ ::Mincer::Processors.constants.each do |k|
28
+ klass = ::Mincer::Processors.const_get(k)
29
+ if klass.is_a?(Class)
30
+ ::Mincer.processors << klass
31
+ elsif klass.is_a?(Module)
32
+ ::Mincer::Base.send(:include, klass)
33
+ end
34
+ end
35
+
36
+
37
+ # Loading ActionView helpers
38
+ if defined?(ActionView)
39
+ require 'mincer/action_view/sort_helper'
40
+ ::Mincer::ActionView.constants.each do |k|
41
+ klass = ::Mincer::ActionView.const_get(k)
42
+ ActionView::Base.send(:include, klass) if klass.is_a?(Module)
43
+ end
44
+ end
45
+
46
+
47
+ #if defined?(::Rails)
48
+ # module Mincer
49
+ # class Railtie < ::Rails::Railtie
50
+ # initializer 'mincer.setup_paths' do
51
+ # end
52
+ #
53
+ # initializer 'carrierwave.active_record' do
54
+ # #ActiveSupport.on_load :active_record do
55
+ # # require 'carrierwave/orm/activerecord'
56
+ # #end
57
+ # end
58
+ # end
59
+ # end
60
+ #end
@@ -0,0 +1,41 @@
1
+ module Mincer
2
+ module ActionView
3
+ module SortHelper
4
+ # Returns sorting URL for collection and attribute
5
+ #
6
+ # <tt>collection</tt> - instance of QueryObject
7
+ # <tt>attribute</tt> - Attribute that will be used to sort table
8
+ def sort_url_for(collection, attribute)
9
+ url_for(params.merge(:sort => attribute, :order => opposite_order_for(collection, attribute)))
10
+ end
11
+
12
+ def opposite_order_for(collection, attribute)
13
+ return nil unless collection.sort_attribute == attribute.to_s
14
+ if collection.sort_order.to_s.upcase == 'ASC'
15
+ 'DESC'
16
+ elsif collection.sort_order.to_s.upcase == 'DESC'
17
+ 'ASC'
18
+ else
19
+ 'ASC'
20
+ end
21
+ end
22
+
23
+
24
+ # Returns chevron class, if attribute is the one that was used for sorting
25
+ #
26
+ # <tt>collection</tt> - instance of QueryObject
27
+ # <tt>attribute</tt> - Attribute that will be used to sort table
28
+ def sort_class_for(collection, attribute)
29
+ return nil unless collection.sort_attribute == attribute.to_s
30
+ if collection.sort_order.upcase == 'ASC'
31
+ 'sorted order_down'
32
+ elsif collection.sort_order.upcase == 'DESC'
33
+ 'sorted order_up'
34
+ else
35
+ ''
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ # This should be extracted and moved to gem
2
+ module Mincer
3
+ class Base
4
+ include Enumerable
5
+
6
+ attr_accessor :args, :sql, :relation
7
+
8
+ # Builds query object
9
+ def initialize(scope, args = {})
10
+ @scope, @args, @relation = scope, args, build_query(scope, args)
11
+ execute_processors
12
+ end
13
+
14
+ def execute_processors
15
+ self.class.active_processors.each {|processor| @relation = processor.new(self).apply }
16
+ end
17
+
18
+ def self.active_processors
19
+ @processors ||= Mincer.processors.clone
20
+ end
21
+
22
+ # Grabs relation raw sql
23
+ def sql
24
+ @sql ||= @relation.connection.unprepared_statement { @relation.to_sql }
25
+ end
26
+
27
+ # Allows enumerable methods to be called directly on object
28
+ def each(&block)
29
+ @collection ||= if @relation.is_a?(ActiveRecord::Relation)
30
+ @relation.to_a
31
+ else
32
+ @relation.all
33
+ end
34
+ @collection.each(&block)
35
+ end
36
+
37
+ # Must be implemented in any subclass
38
+ def build_query(relation, args)
39
+ relation
40
+ end
41
+
42
+ # Pass methods to relation object
43
+ def method_missing(method_id, *params)
44
+ @relation.send(method_id, *params)
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,50 @@
1
+ module Mincer
2
+ module Processors
3
+ class CacheDigest
4
+
5
+ def initialize(mincer)
6
+ @mincer, @args, @relation = mincer, mincer.args, mincer.relation
7
+ end
8
+
9
+ def apply
10
+ @relation
11
+ end
12
+
13
+ def digest
14
+ Mincer.connection.execute(digest_sql).first.values.first
15
+ end
16
+
17
+ private
18
+
19
+ def digest_sql
20
+ <<-SQL
21
+ SELECT digest(#{digest_columns_as_sql}, 'md5') as digest
22
+ FROM (#{@relation.connection.unprepared_statement { @relation.to_sql }}) as digest_q
23
+ SQL
24
+ end
25
+
26
+ def digest_columns_as_sql
27
+ @mincer.class.digest_columns.map { |column| "string_agg(digest_q.#{column}::text, '')" }.join(' || ')
28
+ end
29
+
30
+ end
31
+
32
+ module CacheDigestOptions
33
+ extend ActiveSupport::Concern
34
+
35
+ def digest
36
+ CacheDigest.new(self).digest
37
+ end
38
+
39
+ module ClassMethods
40
+ def digest!(*digest_columns)
41
+ @digest_columns = digest_columns
42
+ end
43
+
44
+ def digest_columns
45
+ @digest_columns || []
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ module Mincer
2
+ module Processors
3
+
4
+ class Paginate
5
+ def initialize(mincer)
6
+ @mincer, @args, @relation = mincer, mincer.args, mincer.relation
7
+ end
8
+
9
+ def apply
10
+ if kaminari?
11
+ @relation.page(@args['page']).per(@args['per_page'])
12
+ elsif will_paginate?
13
+ @relation.paginate(page: @args['page'], per_page: @args['per_page'])
14
+ else
15
+ warn 'To enable pagination please add kaminari or will_paginate to your Gemfile'
16
+ @relation
17
+ end
18
+ end
19
+
20
+ #private
21
+
22
+ def kaminari?
23
+ defined?(::Kaminari)
24
+ end
25
+
26
+ def will_paginate?
27
+ defined?(::WillPaginate)
28
+ end
29
+ end
30
+
31
+ module PaginatorOptions
32
+ extend ActiveSupport::Concern
33
+
34
+ module ClassMethods
35
+ def skip_pagination!
36
+ active_processors.delete(Mincer::Processors::Paginate)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,51 @@
1
+ module Mincer
2
+ module Processors
3
+ class PgJsonDumper
4
+
5
+ def initialize(mincer, options = {})
6
+ @mincer, @args, @relation, @options = mincer, mincer.args, mincer.relation, options
7
+ end
8
+
9
+ def apply
10
+ @relation
11
+ end
12
+
13
+ def to_json
14
+ if dump_supported?
15
+ Mincer.connection.execute(@options[:root] ? query_with_root(@options[:root]) : query).first['json']
16
+ else
17
+ warn 'To dump data to json with postgres you need to use postgres server version >= 9.2'
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def dump_supported?
24
+ Mincer.postgres? && (Mincer.connection.send(:postgresql_version) >= 90200)
25
+ end
26
+
27
+ def query(root = 'json')
28
+ <<-SQL
29
+ SELECT COALESCE(array_to_json(array_agg(row_to_json(subq))), '[]') AS #{root}
30
+ FROM (#{@mincer.sql}) as subq
31
+ SQL
32
+ end
33
+
34
+ def query_with_root(root)
35
+ <<-SQL
36
+ SELECT row_to_json(t) as json FROM ( #{query(root)} ) as t
37
+ SQL
38
+ end
39
+
40
+ end
41
+
42
+ module PgJsonDumperOptions
43
+ extend ActiveSupport::Concern
44
+
45
+ def to_json(options = {})
46
+ PgJsonDumper.new(self, options).to_json
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,34 @@
1
+ module Mincer
2
+ module Processors
3
+ class Search
4
+ def initialize(mincer)
5
+ @mincer, @args, @relation = mincer, mincer.args, mincer.relation
6
+ end
7
+
8
+ def apply
9
+ if Mincer.postgres? && !textacular?
10
+ warn 'You must include "textacular" to your Gemfile to use search'
11
+ @relation
12
+ elsif Mincer.postgres? && @args['pattern']
13
+ @relation.basic_search(@args['pattern']).presence || @relation.fuzzy_search(@args['pattern'])
14
+ else
15
+ @relation
16
+ end
17
+ end
18
+
19
+ def textacular?
20
+ defined?(::Textacular)
21
+ end
22
+ end
23
+
24
+ module SearchOptions
25
+ extend ActiveSupport::Concern
26
+
27
+ module ClassMethods
28
+ def skip_search!
29
+ active_processors.delete(Mincer::Processors::Search)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,55 @@
1
+ module Mincer
2
+ module Processors
3
+
4
+ class Sort
5
+ def initialize(mincer)
6
+ @mincer, @args, @relation = mincer, mincer.args, mincer.relation
7
+ end
8
+
9
+ def apply
10
+ relation = @relation.order("#{sort_attr} #{order_attr}")
11
+ @mincer.sort_attribute, @mincer.sort_order = relation.try(:order_values).try(:first).try(:split)
12
+ relation
13
+ end
14
+
15
+ def sort_attr
16
+ (@mincer.allowed_sort_attributes.include?(@args['sort']) && @args['sort']) || @mincer.default_sort_attribute
17
+ end
18
+
19
+ def order_attr
20
+ (%w{ASC DESC}.include?(@args['order']) && @args['order']) || @mincer.default_sort_order
21
+ end
22
+ end
23
+
24
+
25
+ module SortOptions
26
+ extend ActiveSupport::Concern
27
+
28
+ included do
29
+ attr_accessor :sort_attribute, :sort_order
30
+ end
31
+
32
+ module ClassMethods
33
+ def skip_sorting!
34
+ active_processors.delete(Mincer::Processors::Sort)
35
+ end
36
+ end
37
+
38
+ # Default sort attribute. You must override this method if you want something else
39
+ def default_sort_attribute
40
+ 'id'
41
+ end
42
+
43
+ # Default order attribute. You must override this method if you want something else
44
+ def default_sort_order
45
+ 'ASC'
46
+ end
47
+
48
+ # Allowed sort attributes, should return array of strings
49
+ def allowed_sort_attributes
50
+ @scope.attribute_names
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,18 @@
1
+ module Mincer
2
+
3
+ def self.version
4
+ Gem::Version.new '0.0.1'
5
+ end
6
+
7
+ module VERSION #:nodoc:
8
+ MAJOR, MINOR, TINY, PRE = Mincer.version.segments
9
+ STRING = Mincer.version.to_s
10
+ end
11
+ end
12
+
13
+
14
+
15
+
16
+
17
+
18
+
data/mincer.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mincer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mincer'
8
+ spec.version = Mincer.version
9
+ spec.authors = ['Alex Krasinsky']
10
+ spec.email = ['lyoshakr@gmail.com']
11
+ spec.description = %q{Add pagination, cache_digest, order, json generation with postgres, simple search support to all your queries}
12
+ spec.summary = %q{ActiveRecord::Relation wrapper for pagination, order, json, search, cache_digest support}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activerecord', '>= 4.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'guard-rspec'
27
+ spec.add_development_dependency 'pg'
28
+ spec.add_development_dependency 'sqlite3'
29
+ spec.add_development_dependency 'kaminari'
30
+ spec.add_development_dependency 'will_paginate'
31
+ spec.add_development_dependency 'textacular'
32
+ spec.add_development_dependency 'simplecov'
33
+ end
@@ -0,0 +1,5 @@
1
+ database: mincer
2
+ username: <%= ENV[ 'USER' ] %>
3
+ pool: 5
4
+ timeout: 5000
5
+ adapter: postgresql
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Mincer::Processors::CacheDigest do
4
+ context 'when postgres used' do
5
+ before do
6
+ config = YAML.load_file File.expand_path(File.dirname(__FILE__) + '../../../database.yml')
7
+ ActiveRecord::Base.establish_connection config.merge(:adapter => :postgresql)
8
+ ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS active_record_models')
9
+ ActiveRecord::Base.connection.execute('CREATE TABLE IF NOT EXISTS active_record_models (id SERIAL PRIMARY KEY, text TEXT)')
10
+ class ActiveRecordModel < ActiveRecord::Base
11
+ end
12
+ ActiveRecordModel.create!(text: 'Test1')
13
+ ActiveRecordModel.create!(text: 'Test2')
14
+ end
15
+
16
+ describe 'digesting any basic model without any Mincer::Base configuration' do
17
+ subject(:model) do
18
+ Class.new(Mincer::Base) do
19
+ digest! 'text'
20
+ end
21
+ end
22
+
23
+ it 'dumps data via postgres' do
24
+ query = subject.new(ActiveRecordModel)
25
+ digest = query.digest
26
+ digest.should be_a(String)
27
+ digest.should == '\xe2ffe3413a3d5ee8752a89c0e7a37270'
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ context 'when postgres used' do
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Mincer::Processors::Paginate do
4
+ before do
5
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
6
+ ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER UNIQUE, text STRING)')
7
+ class ActiveRecordModel < ActiveRecord::Base
8
+ end
9
+ 30.times { |i| ActiveRecordModel.create(text: i) }
10
+ end
11
+
12
+ context 'when Kaminari is used for pagination' do
13
+ describe 'paginating with basic model without any Mincer::Base configuration' do
14
+ subject(:model) do
15
+ Class.new(Mincer::Base)
16
+ end
17
+
18
+ it 'paginates by with provided page and per_page in args' do
19
+ query = subject.new(ActiveRecordModel, { 'page' => '2', 'per_page' => '20' })
20
+ query.to_a.count.should eq(10)
21
+ end
22
+
23
+ it 'paginates by default page(1) and per_page(10) when nothing passed to args' do
24
+ query = subject.new(ActiveRecordModel)
25
+ query.to_a.count.should eq(25)
26
+ end
27
+ end
28
+
29
+
30
+ describe 'paginating when basic model has disabled pagination' do
31
+ it 'returns all items' do
32
+ subject = Class.new(Mincer::Base) do
33
+ skip_pagination!
34
+ end
35
+ query = subject.new(ActiveRecordModel)
36
+ query.to_a.count.should eq(30)
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'when WillPaginate is used for pagination' do
42
+ before do
43
+ pending 'Need to fix test, need to unload Kaminari to test this'
44
+ #::Mincer::Processors::Paginator.any_instance.stub(:kaminari?).and_return(false)
45
+ #require 'will_paginate'
46
+ #require 'will_paginate/active_record'
47
+ end
48
+
49
+ describe 'paginating with basic model without any Mincer::Base configuration' do
50
+ subject(:model) do
51
+ Class.new(Mincer::Base)
52
+ end
53
+
54
+ it 'paginates by with provided page and per_page in args' do
55
+ query = subject.new(ActiveRecordModel, { 'page' => '2', 'per_page' => '20' })
56
+ query.to_a.count.should eq(10)
57
+ end
58
+
59
+ it 'paginates by default page(1) and per_page(10) when nothing passed to args' do
60
+ query = subject.new(ActiveRecordModel)
61
+ query.to_a.count.should eq(25)
62
+ end
63
+ end
64
+
65
+
66
+ describe 'paginating when basic model has disabled pagination' do
67
+ it 'returns all items' do
68
+ subject = Class.new(Mincer::Base) do
69
+ skip_pagination!
70
+ end
71
+ query = subject.new(ActiveRecordModel)
72
+ query.to_a.count.should eq(30)
73
+ end
74
+ end
75
+ end
76
+
77
+ context 'when there is no gem for pagination in loaded' do
78
+ it 'returns all items' do
79
+ subject = Class.new(Mincer::Base)
80
+ ::Mincer::Processors::Paginate.any_instance.stub(:kaminari?).and_return(false)
81
+ ::Mincer::Processors::Paginate.any_instance.stub(:will_paginate?).and_return(false)
82
+
83
+ query = subject.new(ActiveRecordModel)
84
+ query.to_a.count.should eq(30)
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Mincer::Processors::PgJsonDumper do
4
+ context 'when postgres is NOT used' do
5
+ before do
6
+ config = YAML.load_file File.expand_path(File.dirname(__FILE__) + '../../../database.yml')
7
+ ActiveRecord::Base.establish_connection config.merge(:adapter => :postgresql)
8
+ ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS active_record_models')
9
+ ActiveRecord::Base.connection.execute('CREATE TABLE IF NOT EXISTS active_record_models (id SERIAL PRIMARY KEY, text TEXT)')
10
+ class ActiveRecordModel < ActiveRecord::Base
11
+ end
12
+ ActiveRecordModel.create!(text: 'Test1')
13
+ ActiveRecordModel.create!(text: 'Test2')
14
+ end
15
+
16
+ describe 'dumping to json with basic model without any Mincer::Base configuration' do
17
+ subject(:model) do
18
+ Class.new(Mincer::Base)
19
+ end
20
+
21
+ it 'dumps data via postgres' do
22
+ query = subject.new(ActiveRecordModel)
23
+ ActiveRecord::Base.connection.should_receive(:execute).and_call_original
24
+ json_string = query.to_json
25
+ json_string.should be_a(String)
26
+ json_hash = JSON.parse(json_string)
27
+ json_hash.size.should eq(2)
28
+ json_hash[0]['id'].should == 1
29
+ json_hash[0]['text'].should == 'Test1'
30
+ json_hash[1]['id'].should == 2
31
+ json_hash[1]['text'].should == 'Test2'
32
+ end
33
+
34
+ context 'when root option is passed' do
35
+ it 'puts responce inside under root key' do
36
+ query = subject.new(ActiveRecordModel)
37
+ ActiveRecord::Base.connection.should_receive(:execute).and_call_original
38
+ json_string = query.to_json(root: 'items')
39
+ json_string.should be_a(String)
40
+ json_hash = JSON.parse(json_string)
41
+ json_hash['items'].size.should eq(2)
42
+ json_hash['items'][0]['id'].should == 1
43
+ json_hash['items'][0]['text'].should == 'Test1'
44
+ json_hash['items'][1]['id'].should == 2
45
+ json_hash['items'][1]['text'].should == 'Test2'
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ context 'when postgres is NOT used' do
52
+ before do
53
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
54
+ ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER UNIQUE, text STRING)')
55
+ class ActiveRecordModel < ActiveRecord::Base
56
+ end
57
+ ActiveRecordModel.create!(id: 1, text: 'Test1')
58
+ ActiveRecordModel.create!(id: 2, text: 'Test2')
59
+ end
60
+
61
+ subject(:model) do
62
+ Class.new(Mincer::Base)
63
+ end
64
+
65
+ it 'dumps data via calling super' do
66
+ query = subject.new(ActiveRecordModel)
67
+ ActiveRecord::Base.connection.should_not_receive(:execute)
68
+ json_string = query.to_json
69
+ json_string.should be_nil
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Mincer::Processors::Search do
4
+ context 'when postgres used' do
5
+ before do
6
+ config = YAML.load_file File.expand_path(File.dirname(__FILE__) + '../../../database.yml')
7
+ ActiveRecord::Base.establish_connection config.merge(:adapter => :postgresql)
8
+ ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS active_record_models')
9
+ ActiveRecord::Base.connection.execute('CREATE TABLE IF NOT EXISTS active_record_models (id SERIAL PRIMARY KEY, text TEXT)')
10
+ class ActiveRecordModel < ActiveRecord::Base
11
+ end
12
+ ActiveRecordModel.create!(text: 'Test')
13
+ ActiveRecordModel.create!(text: 'Bingo')
14
+ ActiveRecordModel.create!(text: 'Bongo')
15
+ end
16
+
17
+ describe 'search without "textacular"' do
18
+ subject(:model) do
19
+ Class.new(Mincer::Base)
20
+ end
21
+
22
+ it 'searches by pattern in args' do
23
+ ::Mincer::Processors::Search.any_instance.stub(:textacular?).and_return(false)
24
+ query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
25
+ query.to_a.count.should eq(3)
26
+ end
27
+ end
28
+
29
+ describe 'search with "textacular"' do
30
+ describe 'searches with basic model without any Mincer::Base configuration' do
31
+ subject(:model) do
32
+ Class.new(Mincer::Base)
33
+ end
34
+
35
+ it 'searches by pattern in args' do
36
+ query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
37
+ query.to_a.count.should eq(1)
38
+ end
39
+ end
40
+
41
+
42
+ describe 'paginating when basic model has disabled pagination' do
43
+ it 'does not modifies relation' do
44
+ subject = Class.new(Mincer::Base) do
45
+ skip_search!
46
+ end
47
+ query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
48
+ query.to_a.count.should eq(3)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'when postgres is NOT used' do
55
+ before do
56
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
57
+ ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER UNIQUE, text STRING)')
58
+ class ActiveRecordModel < ActiveRecord::Base
59
+ end
60
+ ActiveRecordModel.create!(text: 'Test')
61
+ ActiveRecordModel.create!(text: 'Bingo')
62
+ ActiveRecordModel.create!(text: 'Bongo')
63
+ end
64
+
65
+ subject(:model) do
66
+ Class.new(Mincer::Base)
67
+ end
68
+
69
+ it 'returns all records' do
70
+ query = subject.new(ActiveRecordModel, { 'pattern' => 'Bingo' })
71
+ query.to_a.count.should eq(3)
72
+ end
73
+ end
74
+
75
+
76
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Mincer::Processors::Sort do
4
+ before do
5
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
6
+ ActiveRecord::Base.connection.execute('CREATE TABLE active_record_models (id INTEGER UNIQUE, text STRING)')
7
+ class ActiveRecordModel < ActiveRecord::Base
8
+ end
9
+ %w{a c b}.each { |i| ActiveRecordModel.create(text: i) }
10
+ end
11
+
12
+ describe 'sorting with basic model without any Mincer::Base configuration' do
13
+ subject(:model) do
14
+ Class.new(Mincer::Base)
15
+ end
16
+
17
+ it 'sorts by valid attribute and order when they are passed in args' do
18
+ query = subject.new(ActiveRecordModel, { 'sort' => 'text', 'order' => 'DESC' })
19
+ query.to_a.map(&:text).should == %w{c b a}
20
+ end
21
+
22
+ it 'sorts by default attributes(id) abd order(ASC) when nothing passed to args' do
23
+ query = subject.new(ActiveRecordModel)
24
+ query.to_a.map(&:text).should == %w{a c b}
25
+ end
26
+
27
+ it 'ignores sort attribute that is not allowed and use default(id)' do
28
+ query = subject.new(ActiveRecordModel, { 'sort' => 'text2', 'order' => 'DESC' })
29
+ query.to_a.map(&:text).should == %w{b c a}
30
+ end
31
+
32
+ it 'ignores order that is not allowed and use default(ASC)' do
33
+ query = subject.new(ActiveRecordModel, { 'sort' => 'text', 'order' => 'DESCA' })
34
+ query.to_a.map(&:text).should == %w{a b c}
35
+ end
36
+ end
37
+
38
+
39
+ describe 'sorting with basic model with defaults changed' do
40
+ it 'sorts by default attributes(id) abd order(ASC) when nothing passed to args' do
41
+ subject = Class.new(Mincer::Base) do
42
+ def default_sort_attribute
43
+ 'text'
44
+ end
45
+ def default_sort_order
46
+ 'DESC'
47
+ end
48
+ end
49
+ query = subject.new(ActiveRecordModel)
50
+ query.to_a.map(&:text).should == %w{c b a}
51
+ end
52
+ end
53
+
54
+ describe 'sorting with basic model with defaults changed' do
55
+ it 'sorts by default attributes(id) abd order(ASC) when nothing passed to args' do
56
+ subject = Class.new(Mincer::Base) do
57
+ def allowed_sort_attributes
58
+ ['id']
59
+ end
60
+ end
61
+ query = subject.new(ActiveRecordModel, { 'sort' => 'text' })
62
+ query.to_a.map(&:text).should == %w{a c b}
63
+ end
64
+ end
65
+
66
+
67
+ describe 'sorting when basic model has disabled sorting' do
68
+ it 'sorts by default attributes(id) abd order(ASC) when nothing passed to args' do
69
+ subject = Class.new(Mincer::Base) do
70
+ skip_sorting!
71
+ end
72
+ query = subject.new(ActiveRecordModel, { 'sort' => 'text' })
73
+ query.to_a.map(&:text).should == %w{a c b}
74
+ end
75
+ end
76
+
77
+
78
+ end
@@ -0,0 +1,36 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'simplecov'
8
+ SimpleCov.start do
9
+ add_filter 'spec'
10
+ add_group 'Processors', '/lib/mincer/processors'
11
+ add_group 'ActionView', '/lib/mincer/action_view'
12
+ end
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+ require 'active_record'
16
+ require 'kaminari'
17
+ Kaminari::Hooks.init
18
+ require 'textacular'
19
+ ActiveRecord::Base.extend(Textacular)
20
+ require 'mincer'
21
+
22
+ RSpec.configure do |config|
23
+ config.treat_symbols_as_metadata_keys_with_true_values = true
24
+ config.run_all_when_everything_filtered = true
25
+ config.filter_run :focus
26
+
27
+ config.after do
28
+ ActiveRecord::Base.clear_active_connections!
29
+ end
30
+
31
+ # Run specs in random order to surface order dependencies. If you find an
32
+ # order dependency and want to debug it, you can fix the order by providing
33
+ # the seed, which is printed after each run.
34
+ # --seed 1234
35
+ config.order = 'random'
36
+ end
metadata ADDED
@@ -0,0 +1,234 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mincer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Krasinsky
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pg
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: kaminari
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: will_paginate
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: textacular
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Add pagination, cache_digest, order, json generation with postgres, simple
168
+ search support to all your queries
169
+ email:
170
+ - lyoshakr@gmail.com
171
+ executables: []
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - .gitignore
176
+ - .idea/encodings.xml
177
+ - .idea/misc.xml
178
+ - .idea/modules.xml
179
+ - .idea/scopes/scope_settings.xml
180
+ - .idea/vcs.xml
181
+ - Gemfile
182
+ - LICENSE.txt
183
+ - README.md
184
+ - Rakefile
185
+ - lib/mincer.rb
186
+ - lib/mincer/action_view/sort_helper.rb
187
+ - lib/mincer/base.rb
188
+ - lib/mincer/processors/cache_digest.rb
189
+ - lib/mincer/processors/paginate.rb
190
+ - lib/mincer/processors/pg_json_dumper.rb
191
+ - lib/mincer/processors/search.rb
192
+ - lib/mincer/processors/sort.rb
193
+ - lib/mincer/version.rb
194
+ - mincer.gemspec
195
+ - spec/database.yml.example
196
+ - spec/lib/processors/cache_digest_spec.rb
197
+ - spec/lib/processors/paginate_spec.rb
198
+ - spec/lib/processors/pg_json_dumper_spec.rb
199
+ - spec/lib/processors/search_spec.rb
200
+ - spec/lib/processors/sort_spec.rb
201
+ - spec/spec_helper.rb
202
+ homepage: ''
203
+ licenses:
204
+ - MIT
205
+ metadata: {}
206
+ post_install_message:
207
+ rdoc_options: []
208
+ require_paths:
209
+ - lib
210
+ required_ruby_version: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - '>='
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ required_rubygems_version: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - '>='
218
+ - !ruby/object:Gem::Version
219
+ version: '0'
220
+ requirements: []
221
+ rubyforge_project:
222
+ rubygems_version: 2.0.3
223
+ signing_key:
224
+ specification_version: 4
225
+ summary: ActiveRecord::Relation wrapper for pagination, order, json, search, cache_digest
226
+ support
227
+ test_files:
228
+ - spec/database.yml.example
229
+ - spec/lib/processors/cache_digest_spec.rb
230
+ - spec/lib/processors/paginate_spec.rb
231
+ - spec/lib/processors/pg_json_dumper_spec.rb
232
+ - spec/lib/processors/search_spec.rb
233
+ - spec/lib/processors/sort_spec.rb
234
+ - spec/spec_helper.rb