monster-queries 1.3.3

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 85f1857d151c3e35c4e06617e0dccfac90507e8b921c53ba06c48d10017b004f
4
+ data.tar.gz: c216ed5afe37826d7bb25ed75c2ee28d330723e24b5a4fe7f20e8bf4644e9ed3
5
+ SHA512:
6
+ metadata.gz: 336797ea6a5cb43daecbae9d744d4a6fb7b512b412e98495893dcf57f5f865a1172c246bbf57856a4d37613b49793f480700c87aae9bf57da69c431491e182cb
7
+ data.tar.gz: 4d1332d1c5e49fafdea99126555dcd98d130f0e2eb09ec4c8e1a7dbb0edc6e6a1a2dbb3dcf40d761a14f466d6e144e856c0d1bc14d43403e01f4a664d9a529de
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'MonsterQueries'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
@@ -0,0 +1,7 @@
1
+ {{#unless count}}
2
+ LIMIT {{per_page}}
3
+ {{#if page}}
4
+ OFFSET (({{page}} - 1) * {{per_page}})
5
+ {{/if}}
6
+ {{/unless}}
7
+
@@ -0,0 +1,12 @@
1
+ SELECT
2
+ {{#if column}}
3
+ COUNT({{column}}) as total_entries,
4
+ {{else}}
5
+ COUNT(*) as total_entries,
6
+ {{/if}}
7
+ {{per_page}} as per_page,
8
+ {{#if page}}
9
+ {{page}} as page
10
+ {{else}}
11
+ 1 as page
12
+ {{/if}}
@@ -0,0 +1,4 @@
1
+ require 'monster_queries/query'
2
+ ActiveRecord::Base.send :include, MonsterQueries::ActiveRecord
3
+ ActionController::Base.send :include, MonsterQueries::ActionController
4
+ Q = MonsterQueries::Query
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,6 @@
1
+ require "monster_queries/builder"
2
+ require "monster_queries/query"
3
+ require "monster_queries/active_record"
4
+ require "monster_queries/action_controller"
5
+ require "monster_queries/engine"
6
+ module MonsterQueries; end
@@ -0,0 +1,55 @@
1
+ module MonsterQueries
2
+ module ActionController
3
+ def set_pagination_headers count_json
4
+ json = JSON.parse(count_json)
5
+ json = json.first if json.is_a?(Array)
6
+ page = json['page']
7
+ total = json['total_entries']
8
+ per_page = json['per_page'] || 20
9
+ pages = (total.to_f / per_page).ceil
10
+ end_entry = page * per_page
11
+ end_entry = total if end_entry > total
12
+ headers["X-Pagination"] = {
13
+ page: page,
14
+ total: total,
15
+ total_pages: pages,
16
+ first_page: page == 1,
17
+ last_page: page >= pages,
18
+ previous_page: page - 1,
19
+ next_page: page + 1,
20
+ out_of_bounds: page < 1 || page > pages,
21
+ first_entry: (page - 1) * per_page + 1,
22
+ end_entry: end_entry
23
+ }.to_json
24
+ end
25
+
26
+ # AB - We should phase out {{sort}} in favour
27
+ # of {{order}} {{by}} for more finetune control
28
+ def render_paginated target, method, attrs
29
+ if attrs.key?(:sort)
30
+ name,dir = attrs[:sort].split ','
31
+ v = [name]
32
+ v.push dir.upcase if dir
33
+ v = v.join ' '
34
+ attrs[:sort] = v
35
+ attrs[:order] = name
36
+ attrs[:by] = (dir || 'ASC').upcase
37
+ end
38
+ attrs[:count] = true
39
+ count_json = target.send method,attrs
40
+ set_pagination_headers count_json
41
+ attrs.delete(:count)
42
+ target.send method, attrs
43
+ end
44
+
45
+ def render_paginated_json target, method, attrs
46
+ json = render_paginated target, method, attrs
47
+ render json: json
48
+ end
49
+
50
+ def index_params
51
+ params.permit :page, :search, :sort
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,101 @@
1
+ module MonsterQueries
2
+ module ActiveRecord
3
+ extend ActiveSupport::Concern
4
+
5
+ def execute query
6
+ begin
7
+ self.class.connection.execute query
8
+ rescue
9
+ File.open(Rails.root.join('tmp','failed.sql'), 'w') { |file| file.write(query) }
10
+ Rails.root.join
11
+ raise
12
+ end
13
+ end
14
+
15
+ def select_value query
16
+ begin
17
+ self.class.connection.select_value query
18
+ rescue
19
+ File.open(Rails.root.join('tmp','failed.sql'), 'w') { |file| file.write(query) }
20
+ raise
21
+ end
22
+ end
23
+
24
+ def select_values query
25
+ begin
26
+ self.class.connection.select_values query
27
+ rescue
28
+ File.open(Rails.root.join('tmp','failed.sql'), 'w') { |file| file.write(query) }
29
+ raise
30
+ end
31
+ end
32
+
33
+ def select_json query, count
34
+ if count
35
+ select_object query
36
+ else
37
+ select_array query
38
+ end
39
+ end
40
+
41
+ def select_array query
42
+ sql = <<-SQL
43
+ SELECT COALESCE(array_to_json(array_agg(row_to_json(query_row))), '[]'::json)
44
+ FROM (#{query}) query_row
45
+ SQL
46
+ select_value sql
47
+ end
48
+
49
+ def select_object query
50
+ sql = <<-SQL
51
+ SELECT COALESCE(row_to_json(query_row),'{}'::json)
52
+ FROM (#{query}) query_row
53
+ SQL
54
+ select_value sql
55
+ end
56
+
57
+
58
+ def select_all query
59
+ self.class.connection.select_all query
60
+ end
61
+
62
+
63
+ module ClassMethods
64
+ def execute query
65
+ connection.execute query
66
+ end
67
+
68
+ def select_value query
69
+ connection.select_value query
70
+ end
71
+
72
+ def select_values query
73
+ connection.select_values query
74
+ end
75
+
76
+ def select_json query, count
77
+ if count
78
+ select_object query
79
+ else
80
+ select_array query
81
+ end
82
+ end
83
+
84
+ def select_array query
85
+ sql = <<-SQL
86
+ SELECT COALESCE(array_to_json(array_agg(row_to_json(query_row))), '[]'::json)
87
+ FROM (#{query}) query_row
88
+ SQL
89
+ select_value sql
90
+ end
91
+
92
+ def select_object query
93
+ sql = <<-SQL
94
+ SELECT COALESCE(row_to_json(query_row),'{}'::json)
95
+ FROM (#{query}) query_row
96
+ SQL
97
+ select_value sql
98
+ end
99
+ end
100
+ end # module
101
+ end # module
@@ -0,0 +1,49 @@
1
+ module MonsterQueries
2
+ class Builder
3
+ def self.with_scope args
4
+ q = self.new nil
5
+ q.scope = args
6
+ q
7
+ end
8
+
9
+ attr_writer :scope, :paginate
10
+
11
+ def initialize name
12
+ @paginate = false
13
+ @scope = [name]
14
+ end
15
+
16
+ def method_missing name, args=nil
17
+ @scope << name
18
+ file_exists? ? file_contents(args) : self
19
+ end
20
+
21
+ def to_s args={}
22
+ if file_exists?
23
+ ::Rails.logger.tagged('MONSTER QUERY') do
24
+ ::Rails.logger.info {"Rendered #{@scope.join('.')}"}
25
+ end
26
+ file_contents args
27
+ else
28
+ raise "Query doesn't exist: #{@scope.join('.')}"
29
+ end
30
+ end
31
+
32
+ def paginate
33
+ @paginate = true
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ def file_exists?
40
+ Q.exists? @scope
41
+ end
42
+
43
+ def file_contents variables
44
+ template = MonsterQueries::Query.template @scope
45
+ result = template.call variables
46
+ result
47
+ end
48
+ end # class
49
+ end # module
@@ -0,0 +1,4 @@
1
+ module MonsterQueries
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module MonsterQueries
2
+ class Helpers
3
+ end # class
4
+ end # module
@@ -0,0 +1,147 @@
1
+ require 'active_record'
2
+ require 'handlebars'
3
+ require 'pry'
4
+ module MonsterQueries
5
+ class Query
6
+ @template_cache = {}
7
+ @exists_cache = {}
8
+ SKIP_CACHE = true
9
+
10
+ def self.template_from_parts parts, vars
11
+ MonsterQueries::Builder.with_scope(parts).to_s vars
12
+ end
13
+
14
+ def self.template_from_string string, vars
15
+ template = handlebars.compile string, noEscape: true
16
+ result = template.call vars
17
+ result
18
+ end
19
+
20
+ # Method Missing is used to create a chain path
21
+ # to the query eg. Q.admin.users.index
22
+ def self.method_missing name
23
+ MonsterQueries::Builder.new name
24
+ end
25
+
26
+ def self.paginate
27
+ q = MonsterQueries::Builder.new nil
28
+ q.paginate = true
29
+ q
30
+ end
31
+
32
+ def self.locate_file scope
33
+ search_paths = [Rails.root,MonsterQueries::Engine.root]
34
+ search_paths.each do |path|
35
+ file = path.join('app','queries',*scope.compact.map(&:to_s)).to_s + '.sql'
36
+ return file if File.exists?(file)
37
+ end
38
+ return false
39
+ end
40
+
41
+ def self.template scope
42
+ if !@template_cache[scope.join('.')] || SKIP_CACHE
43
+ file_name = locate_file(scope)
44
+ data = File.read file_name
45
+ @template_cache[scope.join('.')] = handlebars.compile(data, noEscape: true)
46
+ end
47
+ @template_cache[scope.join('.')]
48
+ end
49
+
50
+ def self.exists? scope
51
+ if !@exists_cache[scope] || SKIP_CACHE
52
+ file_name = locate_file(scope)
53
+ @exists_cache[scope] = file_name && File.file?(file_name)
54
+ end
55
+ @exists_cache[scope]
56
+ end
57
+
58
+ def self.handlebars
59
+ return @handlebars if @handlebars
60
+ @handlebars = Handlebars::Context.new
61
+ @handlebars.register_helper :include , &(method(:helper_include).to_proc)
62
+ @handlebars.register_helper :paginate , &(method(:helper_paginate).to_proc)
63
+ @handlebars.register_helper :paginate_offset, &(method(:helper_paginate_offset).to_proc)
64
+ @handlebars.register_helper :wildcard , &(method(:helper_wildcard).to_proc)
65
+ @handlebars.register_helper :quote , &(method(:helper_quote).to_proc)
66
+ @handlebars.register_helper :int , &(method(:helper_int).to_proc)
67
+ @handlebars.register_helper :float , &(method(:helper_float).to_proc)
68
+ @handlebars.register_helper :array , &(method(:helper_array).to_proc)
69
+ @handlebars.register_helper :object , &(method(:helper_object).to_proc)
70
+ @handlebars
71
+ end
72
+
73
+ def self.helper_include context, name, options
74
+ vars = {}
75
+ context.each do |k,v|
76
+ vars[k] = v
77
+ end
78
+ options['hash'].each do |k,v|
79
+ vars[k] = v
80
+ end if options
81
+ parts = name.split('.')
82
+ MonsterQueries::Builder.with_scope(parts).to_s vars
83
+ end
84
+
85
+ def self.helper_paginate context, value, options
86
+ if value.is_a?(String)
87
+ count = !!context["count"]
88
+ name = count ? 'pagination.select' : value
89
+ self.helper_include context, name, options
90
+ else
91
+ value.fn context
92
+ end
93
+ end
94
+
95
+ def self.helper_paginate_offset context, value, options
96
+ self.helper_include context, 'pagination.offset', options
97
+ end
98
+
99
+ def self.helper_wildcard context, value, options
100
+ ::ActiveRecord::Base.connection.quote "%#{value.gsub('\\','\\\\\\')}%"
101
+ end
102
+
103
+ def self.helper_quote context, value, options
104
+ if value.is_a?(V8::Array)
105
+ value.collect{|v| ::ActiveRecord::Base.connection.quote v}.join(',')
106
+ else
107
+ ::ActiveRecord::Base.connection.quote value
108
+ end
109
+ end
110
+
111
+ def self.helper_int context, value, options
112
+ value.to_i
113
+ end
114
+
115
+ def self.helper_float context, value, options
116
+ value.to_f
117
+ end
118
+
119
+ def self.helper_array context, block, options
120
+ content =
121
+ if block.is_a?(String)
122
+ "\n" + helper_include(context, block, options)
123
+ else
124
+ block.fn context
125
+ end
126
+ <<-HEREDOC
127
+ (SELECT COALESCE(array_to_json(array_agg(row_to_json(array_row))),'[]'::json) FROM (
128
+ #{content}
129
+ ) array_row)
130
+ HEREDOC
131
+ end
132
+
133
+ def self.helper_object context, block, options
134
+ content =
135
+ if block.is_a?(String)
136
+ "\n" + helper_include(context, block, options)
137
+ else
138
+ block.fn context
139
+ end
140
+ <<-HEREDOC
141
+ (SELECT COALESCE(row_to_json(object_row),'{}'::json) FROM (
142
+ #{content}
143
+ ) object_row)
144
+ HEREDOC
145
+ end
146
+ end # class
147
+ end # module
@@ -0,0 +1,3 @@
1
+ module MonsterQueries
2
+ VERSION = "1.3.3"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :monster_queries do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monster-queries
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.3
5
+ platform: ruby
6
+ authors:
7
+ - Monsterbox Productions
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: handlebars
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: libv8
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Queries
70
+ email:
71
+ - andrew@monsterboxpro.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - MIT-LICENSE
77
+ - Rakefile
78
+ - app/queries/pagination/offset.sql
79
+ - app/queries/pagination/select.sql
80
+ - config/initializers/query.rb
81
+ - config/routes.rb
82
+ - lib/monster-queries.rb
83
+ - lib/monster_queries/action_controller.rb
84
+ - lib/monster_queries/active_record.rb
85
+ - lib/monster_queries/builder.rb
86
+ - lib/monster_queries/engine.rb
87
+ - lib/monster_queries/helpers.rb
88
+ - lib/monster_queries/query.rb
89
+ - lib/monster_queries/version.rb
90
+ - lib/tasks/monster_queries_tasks.rake
91
+ homepage: http://monsterboxpro.com
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.7.8
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Queries
115
+ test_files: []