mincer 0.0.1

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.
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