arel_extensions 0.9.8 → 1.0
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 +4 -4
- data/.codeclimate.yml +25 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +23 -15
- data/.travis/oracle/download.js +94 -58
- data/.travis/oracle/download.sh +1 -0
- data/Gemfile +1 -1
- data/README.md +30 -7
- data/TODO +8 -15
- data/arel_extensions.gemspec +2 -2
- data/functions.html +13 -2
- data/gemfiles/rails4.gemfile +2 -2
- data/gemfiles/rails5.gemfile +2 -2
- data/gemfiles/rails5_0.gemfile +2 -2
- data/lib/arel_extensions.rb +27 -0
- data/lib/arel_extensions/math.rb +72 -38
- data/lib/arel_extensions/nodes/as.rb +12 -0
- data/lib/arel_extensions/nodes/function.rb +1 -1
- data/lib/arel_extensions/nodes/union.rb +24 -0
- data/lib/arel_extensions/nodes/union_all.rb +20 -0
- data/lib/arel_extensions/version.rb +1 -1
- data/lib/arel_extensions/visitors/ibm_db.rb +2 -0
- data/lib/arel_extensions/visitors/mssql.rb +1 -0
- data/lib/arel_extensions/visitors/mysql.rb +1 -0
- data/lib/arel_extensions/visitors/oracle.rb +16 -7
- data/lib/arel_extensions/visitors/postgresql.rb +6 -5
- data/lib/arel_extensions/visitors/sqlite.rb +32 -6
- data/lib/arel_extensions/visitors/to_sql.rb +36 -14
- data/test/visitors/test_to_sql.rb +48 -2
- data/test/with_ar/all_agnostic_test.rb +22 -2
- data/test/with_ar/insert_agnostic_test.rb +26 -2
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5142c37a719ede88896bbdcfa06b0690ddc4b75
|
4
|
+
data.tar.gz: 9da10d459cb7e1fa8a52305d12f1fb0ca945a537
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61d6268f8785ffd105d29b1fead496f0e7e35bfd1f660174e6fd922df197341449c2cb4379d4de37d91c62de24f48bfcb3be5aba83178f139c98d15189302e69
|
7
|
+
data.tar.gz: cd7c81aeedf284bcb67501ea045d82dce9cd2ff8d52f622262b7f3f0633f0c3681613de4977ef3c62aeca0f43d60d201a5fbb1f7f5f8ca5a4df36e17f7ca0073
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
rubocop:
|
4
|
+
enabled: true
|
5
|
+
#checks:
|
6
|
+
# Rubocop/Metrics/ClassLength:
|
7
|
+
# enabled: false
|
8
|
+
duplication:
|
9
|
+
enabled: false
|
10
|
+
brakeman:
|
11
|
+
enabled: false
|
12
|
+
reek:
|
13
|
+
enabled: true
|
14
|
+
"bundler-audit":
|
15
|
+
enabled: false
|
16
|
+
markdownlint:
|
17
|
+
enabled: false
|
18
|
+
ratings:
|
19
|
+
paths:
|
20
|
+
- lib/**
|
21
|
+
- "**.rb"
|
22
|
+
- "**.md"
|
23
|
+
exclude_paths:
|
24
|
+
- .travis/**/*
|
25
|
+
- test/**/*
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -59,39 +59,41 @@ rvm:
|
|
59
59
|
- ruby-head
|
60
60
|
jdk:
|
61
61
|
- openjdk7
|
62
|
-
- oraclejdk7
|
63
62
|
- oraclejdk8
|
63
|
+
- oraclejdk9
|
64
64
|
matrix:
|
65
65
|
fast_finish: true
|
66
66
|
exclude:
|
67
67
|
- rvm: rbx-2
|
68
68
|
jdk: openjdk7
|
69
|
-
- rvm: rbx-2
|
70
|
-
jdk: oraclejdk7
|
71
69
|
- rvm: 2.0.0
|
72
70
|
jdk: openjdk7
|
73
|
-
- rvm: 2.0.0
|
74
|
-
jdk: oraclejdk7
|
75
71
|
- rvm: 2.1
|
76
72
|
jdk: openjdk7
|
77
|
-
- rvm: 2.1
|
78
|
-
jdk: oraclejdk7
|
79
73
|
- rvm: 2.2.5
|
80
74
|
jdk: openjdk7
|
81
|
-
- rvm: 2.2.5
|
82
|
-
jdk: oraclejdk7
|
83
75
|
- rvm: 2.3.1
|
84
76
|
jdk: openjdk7
|
85
|
-
- rvm: 2.3.1
|
86
|
-
jdk: oraclejdk7
|
87
77
|
- rvm: 2.4.0
|
88
78
|
jdk: openjdk7
|
89
|
-
- rvm: 2.4.0
|
90
|
-
jdk: oraclejdk7
|
91
79
|
- rvm: ruby-head
|
80
|
+
jdk: openjdk7
|
81
|
+
- rvm: jruby-head
|
92
82
|
jdk: openjdk7
|
83
|
+
- rvm: rbx-2
|
84
|
+
jdk: oraclejdk8
|
85
|
+
- rvm: 2.0.0
|
86
|
+
jdk: oraclejdk8
|
87
|
+
- rvm: 2.1
|
88
|
+
jdk: oraclejdk8
|
89
|
+
- rvm: 2.2.5
|
90
|
+
jdk: oraclejdk8
|
91
|
+
- rvm: 2.3.1
|
92
|
+
jdk: oraclejdk8
|
93
|
+
- rvm: 2.4.0
|
94
|
+
jdk: oraclejdk8
|
93
95
|
- rvm: ruby-head
|
94
|
-
jdk:
|
96
|
+
jdk: oraclejdk8
|
95
97
|
- rvm: 2.0.0
|
96
98
|
gemfile: gemfiles/rails5.gemfile
|
97
99
|
- rvm: 2.1
|
@@ -121,6 +123,12 @@ matrix:
|
|
121
123
|
gemfile: gemfiles/rails5.gemfile
|
122
124
|
- rvm: jruby-head
|
123
125
|
gemfile: gemfiles/rails5.gemfile
|
126
|
+
- rvm: jruby-head
|
127
|
+
gemfile: gemfiles/rails4.gemfile
|
128
|
+
jdk: oraclejdk9
|
124
129
|
bundler_args: "--jobs 3 --retry 2"
|
125
130
|
notifications:
|
126
|
-
email:
|
131
|
+
email:
|
132
|
+
notifications:
|
133
|
+
- julien.delporte@faveod.com
|
134
|
+
- yann.azoury@faveod.com
|
data/.travis/oracle/download.js
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
// vim: set et sw=2 ts=2:
|
2
2
|
"use strict";
|
3
3
|
var env = process.env;
|
4
|
+
var url = require('url');
|
4
5
|
var Promise = require('bluebird');
|
5
6
|
var Phantom = Promise.promisifyAll(require('node-phantom-simple'));
|
6
|
-
|
7
|
+
|
8
|
+
var login = {
|
9
|
+
begin: url.parse(env['ORACLE_LOGIN_BEGIN'] || "https://www.oracle.com/webapps/redirect/signon?nexturl=https://www.oracle.com/favicon.ico"),
|
10
|
+
end: url.parse(env['ORCALE_LOGIN_END'] || "https://www.oracle.com/favicon.ico"),
|
11
|
+
};
|
12
|
+
delete env['ORACLE_LOGIN_BEGIN'];
|
13
|
+
delete env['ORACLE_LOGIN_END'];
|
7
14
|
|
8
15
|
var credentials = Object.keys(env)
|
9
16
|
.filter(function (key) { return key.indexOf('ORACLE_LOGIN_') == 0 })
|
@@ -16,38 +23,92 @@ if (credentials.length <= 0) {
|
|
16
23
|
|
17
24
|
Phantom.createAsync({ parameters: { 'ssl-protocol': 'tlsv1' } }).then(function (browser) {
|
18
25
|
browser = Promise.promisifyAll(browser, { suffix: 'Promise' });
|
26
|
+
browser.addCookie({'name': 'oraclelicense', 'value': "accept-" + env['ORACLE_COOKIE'] + "-cookie", 'domain': '.oracle.com' });
|
19
27
|
|
20
|
-
//
|
21
|
-
return browser
|
22
|
-
.addCookiePromise({'name': 'oraclelicense', 'value': "accept-" + env['ORACLE_COOKIE'] + "-cookie", 'domain': '.oracle.com' })
|
23
|
-
.then(function () {
|
24
|
-
return browser.createPagePromise();
|
25
|
-
})
|
26
|
-
.then(function (page) {
|
28
|
+
// Open a tab, configure it
|
29
|
+
return browser.createPagePromise().then(function (page) {
|
27
30
|
page = Promise.promisifyAll(page, { suffix: 'Promise' });
|
28
31
|
|
29
|
-
|
32
|
+
var received = "";
|
33
|
+
page.onNavigationRequested = function () { console.info("%s %j", (new Date()).toISOString(), arguments["0"]); };
|
30
34
|
page.onResourceError = console.error.bind(console);
|
35
|
+
page.onResourceReceived = function (response) { if (response.stage == "end") received = response.url; };
|
36
|
+
page.set('settings.loadImages', false);
|
37
|
+
|
31
38
|
return page
|
32
39
|
.setPromise('settings.userAgent', env['USER_AGENT']) // PhantomJS configures the UA per tab
|
33
40
|
|
34
|
-
//
|
41
|
+
// Begin login, wait for the login page
|
35
42
|
.then(function () {
|
36
|
-
return page.openPromise(
|
37
|
-
if (status != 'success') throw "Unable to connect to
|
38
|
-
|
43
|
+
return page.openPromise(login.begin.href).then(function (status) {
|
44
|
+
if (status != 'success') throw "Unable to connect to " + login.begin.host;
|
45
|
+
|
46
|
+
return new Promise(function (resolve, reject) {
|
47
|
+
var deadline = Date.now() + 6000;
|
48
|
+
var interval = 100;
|
49
|
+
|
50
|
+
var check = function () {
|
51
|
+
if (deadline < Date.now()) return reject("Timeout waiting for form");
|
52
|
+
|
53
|
+
page.evaluate(function () {
|
54
|
+
return window['jQuery'] && document.querySelectorAll('input[type=password]').length;
|
55
|
+
}, function (err, result) {
|
56
|
+
if (result) { resolve(); } else { setTimeout(check, interval); }
|
57
|
+
});
|
58
|
+
};
|
59
|
+
|
60
|
+
check();
|
61
|
+
});
|
39
62
|
})
|
40
|
-
.
|
63
|
+
.tapCatch(function (err) {
|
41
64
|
return page.getPromise('plainText').then(function (text) {
|
42
65
|
console.error("Unable to load login page. Last response was:\n" + text);
|
43
|
-
|
66
|
+
});
|
67
|
+
});
|
68
|
+
})
|
69
|
+
|
70
|
+
// Submit the login form
|
71
|
+
.then(function () {
|
72
|
+
return page.evaluatePromise(function (credentials) {
|
73
|
+
var $form = jQuery(document.forms[0]);
|
74
|
+
return credentials.filter(function (tuple) {
|
75
|
+
return $form.find("[name='"+tuple[0]+"']").val(tuple[1]).length == 0;
|
76
|
+
})
|
77
|
+
.map(function (tuple) { return tuple[0]; });
|
78
|
+
}, credentials)
|
79
|
+
.then(function (unapplied) {
|
80
|
+
if (unapplied.length > 0) {
|
81
|
+
console.warn("Unable to use all ORACLE_LOGIN environment variables: %j", unapplied);
|
82
|
+
}
|
83
|
+
return page.evaluatePromise(function () {
|
84
|
+
jQuery(function () { document.forms[0].submit(); });
|
85
|
+
});
|
86
|
+
});
|
87
|
+
})
|
88
|
+
|
89
|
+
// Wait for login result
|
90
|
+
.then(function () {
|
91
|
+
return new Promise(function (resolve, reject) {
|
92
|
+
var deadline = Date.now() + 6000;
|
93
|
+
var interval = 100;
|
94
|
+
|
95
|
+
var check = function () {
|
96
|
+
if (deadline < Date.now()) return reject("Timeout waiting for " + login.end.href);
|
97
|
+
if (received == login.end.href) { resolve(); } else { setTimeout(check, interval); }
|
98
|
+
};
|
99
|
+
|
100
|
+
check();
|
101
|
+
})
|
102
|
+
.tapCatch(function (err) {
|
103
|
+
return page.getPromise('plainText').then(function (text) {
|
104
|
+
console.error("Unable to load login result. Last response was:\n" + text);
|
44
105
|
});
|
45
106
|
});
|
46
107
|
})
|
47
108
|
|
48
109
|
// Export cookies for cURL
|
49
110
|
.then(function () {
|
50
|
-
return
|
111
|
+
return browser.getPromise('cookies').then(function (cookies) {
|
51
112
|
var data = "";
|
52
113
|
for (var i = 0; i < cookies.length; ++i) {
|
53
114
|
var cookie = cookies[i];
|
@@ -55,52 +116,27 @@ Phantom.createAsync({ parameters: { 'ssl-protocol': 'tlsv1' } }).then(function (
|
|
55
116
|
+ (cookie.secure ? "TRUE" : "FALSE") + "\t0\t"
|
56
117
|
+ cookie.name + "\t" + cookie.value + "\n";
|
57
118
|
}
|
58
|
-
return Promise.
|
119
|
+
return Promise.promisify(require('fs').writeFile)(env['COOKIES'], data);
|
59
120
|
});
|
60
121
|
})
|
61
122
|
|
62
|
-
//
|
123
|
+
// Download file using cURL
|
63
124
|
.then(function () {
|
64
|
-
return
|
65
|
-
var
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
return !applied;
|
80
|
-
})
|
81
|
-
.map(function (tuple) { return tuple[0] });
|
82
|
-
|
83
|
-
if (unapplied.length > 0) {
|
84
|
-
console.warn("Unable to use all ORACLE_LOGIN environment variables: %j", unapplied);
|
85
|
-
}
|
86
|
-
|
87
|
-
var cmd = ['curl', [
|
88
|
-
'--cookie', env['COOKIES'],
|
89
|
-
'--cookie-jar', env['COOKIES'],
|
90
|
-
'--data', '@-',
|
91
|
-
'--location',
|
92
|
-
'--output', env['ORACLE_DOWNLOAD_FILE'],
|
93
|
-
'--user-agent', env['USER_AGENT'],
|
94
|
-
form.action
|
95
|
-
]];
|
96
|
-
|
97
|
-
console.info("Executing %j", cmd);
|
98
|
-
|
99
|
-
var child_process = require('child_process');
|
100
|
-
var child = child_process.spawn.apply(child_process, cmd.concat({ stdio: ['pipe', 1, 2] }));
|
101
|
-
child.on('exit', process.exit);
|
102
|
-
child.stdin.end(form.data);
|
103
|
-
});
|
125
|
+
return browser.exitPromise().then(function () {
|
126
|
+
var cmd = ['curl', [
|
127
|
+
'--cookie', env['COOKIES'],
|
128
|
+
'--cookie-jar', env['COOKIES'],
|
129
|
+
'--location',
|
130
|
+
'--output', env['ORACLE_DOWNLOAD_FILE'],
|
131
|
+
'--user-agent', env['USER_AGENT'],
|
132
|
+
"https://edelivery.oracle.com/akam/otn/linux/" + env['ORACLE_FILE']
|
133
|
+
]];
|
134
|
+
|
135
|
+
console.info("Executing %j", cmd);
|
136
|
+
|
137
|
+
var child_process = require('child_process');
|
138
|
+
var child = child_process.spawn.apply(child_process, cmd.concat({ stdio: [0, 1, 2] }));
|
139
|
+
child.on('exit', process.exit);
|
104
140
|
});
|
105
141
|
})
|
106
142
|
.catch(function (err) {
|
data/.travis/oracle/download.sh
CHANGED
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ group :development, :test do
|
|
13
13
|
gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby
|
14
14
|
|
15
15
|
|
16
|
-
gem "tiny_tds",
|
16
|
+
gem "tiny_tds", '~> 1.3.0' ,:require => false, :platforms => [:mingw, :x64_mingw, :mswin]
|
17
17
|
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mingw, :x64_mingw, :mswin]
|
18
18
|
|
19
19
|
gem 'activesupport', '~> 4.0'
|
data/README.md
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# Arel Extensions
|
2
2
|
|
3
|
-
[![Build Status](https://
|
4
|
-
[![
|
3
|
+
[![Travis Build Status](https://img.shields.io/travis/Faveod/arel-extensions.svg?label=Travis%20build)](http://travis-ci.org/Faveod/arel-extensions)
|
4
|
+
[![AppVeyor Build Status](https://img.shields.io/appveyor/ci/yazfav/arel-extensions.svg?label=AppVeyor%20build)](https://ci.appveyor.com/project/yazfav/arel-extensions)
|
5
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/Faveod/arel-extensions.svg)](https://codeclimate.com/github/Faveod/arel-extensions)
|
6
|
+
[![Gemnasium](https://img.shields.io/gemnasium/Faveod/arel-extensions.svg)](https://gemnasium.com/github.com/Faveod/arel-extensions)
|
7
|
+
[![Security](https://hakiri.io/github/Faveod/arel-extensions/master.svg)](https://hakiri.io/github/Faveod/arel-extensions/master)
|
5
8
|
![](http://img.shields.io/badge/license-MIT-brightgreen.svg)
|
6
|
-
|
7
|
-
![](https://
|
9
|
+
|
10
|
+
Gem: [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
|
11
|
+
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)](https://rubygems.org/gems/arel_extensions)
|
12
|
+
[![Gem](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)](https://rubygems.org/gems/arel_extensions)
|
8
13
|
|
9
14
|
Arel Extensions adds shortcuts, fixes and new ORM mappings (ruby to SQL) to Arel.
|
10
15
|
It aims to ensure pure ruby syntax for the biggest number of usual cases.
|
@@ -121,11 +126,11 @@ t[:birthdate].format('%Y-%m-%d').to_sql
|
|
121
126
|
# => DATE_FORMAT(my_table.birthdate, '%Y-%m-%d')
|
122
127
|
```
|
123
128
|
|
124
|
-
## Unions
|
129
|
+
## Unions
|
125
130
|
|
126
131
|
```ruby
|
127
132
|
(t.where(t[:name].eq('str')) + t.where(t[:name].eq('test'))).to_sql
|
128
|
-
# => SELECT * FROM my_table WHERE
|
133
|
+
# => (SELECT * FROM my_table WHERE name='str') UNION (SELECT * FROM my_table WHERE name='test')
|
129
134
|
```
|
130
135
|
|
131
136
|
## Stored Procedures and User-defined functions
|
@@ -496,6 +501,24 @@ User.connection.execute(insert_manager.to_sql)
|
|
496
501
|
<td class="ok">✔</td>
|
497
502
|
<td class="ok">✔</td>
|
498
503
|
<td class="ok">✔</td>
|
504
|
+
</tr> <tr>
|
505
|
+
<th class="set_operators" rowspan="1"><div>Set <br/> Operators</div></th>
|
506
|
+
<td class="tg-yw4l">UNION (+)<br/>query + query</td>
|
507
|
+
<td class="ok">✔</td>
|
508
|
+
<td class="ok">✔</td>
|
509
|
+
<td class="ok">✔</td>
|
510
|
+
<td class="ok">✔</td>
|
511
|
+
<td class="ok">✔</td>
|
512
|
+
<td class="ok">✔</td>
|
513
|
+
</tr> <tr>
|
514
|
+
<th class="set_operators" rowspan="1"><div>Set <br/> Operators</div></th>
|
515
|
+
<td class="tg-yw4l">UNION ALL<br/>query.union_all(query)</td>
|
516
|
+
<td class="ok">✔</td>
|
517
|
+
<td class="ok">✔</td>
|
518
|
+
<td class="ok">✔</td>
|
519
|
+
<td class="ok">✔</td>
|
520
|
+
<td class="ok">✔</td>
|
521
|
+
<td class="ok">✔</td>
|
499
522
|
</tr>
|
500
523
|
</tbody>
|
501
|
-
</table>
|
524
|
+
</table>
|
data/TODO
CHANGED
@@ -1,30 +1,23 @@
|
|
1
1
|
Code quality:
|
2
|
-
|
2
|
+
+ codeclimate.com
|
3
|
+
+ gemnasium
|
4
|
+
+ appveyor.com (windows)
|
5
|
+
+ hakiri.io
|
3
6
|
- coveralls.io
|
4
|
-
- gemnasium
|
5
7
|
- inch-ci.org
|
6
|
-
-
|
7
|
-
- hakiri.io
|
8
|
-
|
9
|
-
[![Gem Version](https://img.shields.io/gem/v/arel_extensions.svg)][gem]
|
10
|
-
[![Build Status](https://img.shields.io/travis/faveod/arel_extensions.svg)][travis]
|
11
|
-
[![Dependency Status](https://img.shields.io/gemnasium/faveod/arel_extensions.svg)][https://gemnasium.com/faveod/arel_extensions]
|
12
|
-
[![Code Climate](https://img.shields.io/codeclimate/github/faveod/arel_extensions.svg)][https://codeclimate.com/github/faveod/arel_extensions]
|
13
|
-
[![Coverage Status](https://img.shields.io/coveralls/faveod/arel_extensions.svg)][https://coveralls.io/r/faveod/arel_extensions]
|
14
|
-
[![Inline docs](http://inch-ci.org/github/faveod/arel_extensions.svg)][https://inch-ci.org/github/faveod/https://gemnasium.com/faveod/arel_extensions]
|
15
|
-
[![security](https://hakiri.io/github/faveod/arel_extensions/master.svg)](https://hakiri.io/github/faveod/arel_extensions/master)
|
16
|
-
[![codebeat](https://codebeat.co/badges/<change>)](https://codebeat.co/projects/github-com-faveod-arel_extensions)
|
8
|
+
- codebeat.co
|
17
9
|
|
18
10
|
TODO:
|
19
11
|
- all skips in tests
|
12
|
+
- all SQL Server date formats
|
20
13
|
|
21
14
|
New features:
|
22
15
|
- POSIX format numbers (%.2f), dates (man 3 printf)
|
23
|
-
- easy unions
|
24
16
|
- cast, to_char
|
17
|
+
- auto-remove order by in sub queries
|
25
18
|
- avoid incompatible queries in PgSQL/Oracle/SQLServer when columns are used outside of group by columns (in select or order by)
|
26
19
|
|
27
20
|
Tests improvements:
|
28
21
|
- SQL Server
|
29
22
|
- DB2
|
30
|
-
- Informix
|
23
|
+
- Informix
|
data/arel_extensions.gemspec
CHANGED
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.name = "arel_extensions"
|
6
6
|
s.version = ArelExtensions::VERSION
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
|
-
s.authors = ["Yann Azoury", "
|
9
|
-
s.email = ["yann.azoury@faveod.com", "
|
8
|
+
s.authors = ["Yann Azoury", "Félix Bellanger", "Julien Delporte"]
|
9
|
+
s.email = ["yann.azoury@faveod.com", "felix.bellanger@faveod.com", "julien.delporte@faveod.com"]
|
10
10
|
s.homepage = "https://github.com/Faveod/arel-extensions"
|
11
11
|
s.description = "Adds new features to Arel"
|
12
12
|
s.summary = "Extending Arel"
|
data/functions.html
CHANGED
@@ -23,6 +23,7 @@
|
|
23
23
|
.arel-functions th[rowspan] { vertical-align: middle; width:3em;}
|
24
24
|
.arel-functions th[rowspan] > div { transform:rotate(-90deg); transform-origin: center center; white-space: nowrap; }
|
25
25
|
.arel-functions td.ok { color:green; text-align:center; }
|
26
|
+
.arel-functions td.ko { color:red; text-align:center; }
|
26
27
|
</style>
|
27
28
|
</head>
|
28
29
|
<body>
|
@@ -96,7 +97,7 @@
|
|
96
97
|
<td class="ok">✔</td>
|
97
98
|
</tr>
|
98
99
|
<tr>
|
99
|
-
<th class="tg-ffjm" rowspan="
|
100
|
+
<th class="tg-ffjm" rowspan="14"><div>String functions</div></th>
|
100
101
|
<td class="tg-yw4l">CONCAT<br>column + "string"</td>
|
101
102
|
<td class="ok">✔</td>
|
102
103
|
<td class="ok">✔</td>
|
@@ -378,9 +379,19 @@
|
|
378
379
|
<td class="ok">✔</td>
|
379
380
|
<td class="ok">✔</td>
|
380
381
|
<td class="ok">✔</td>
|
382
|
+
</tr>
|
383
|
+
<tr>
|
384
|
+
<th class="bulk_insert" rowspan="1"><div>Set<br/> Operators</div></th>
|
385
|
+
<td class="tg-yw4l">UNION ( + )<br/>query + query</td>
|
386
|
+
<td class="ko">to be tested</td>
|
387
|
+
<td class="ko">to be tested</td>
|
388
|
+
<td class="ko">to be tested</td>
|
389
|
+
<td class="ko">to be tested</td>
|
390
|
+
<td class="ko">to be tested</td>
|
391
|
+
<td class="ko">to be tested</td>
|
381
392
|
</tr>
|
382
393
|
</tbody>
|
383
394
|
</table>
|
384
395
|
|
385
396
|
</body>
|
386
|
-
</html>
|
397
|
+
</html>
|
data/gemfiles/rails4.gemfile
CHANGED
@@ -11,8 +11,8 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw, :mswin]
|
15
|
-
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :mswin]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw, :mswin] if RUBY_PLATFORM =~ /windows/
|
15
|
+
gem "activerecord-sqlserver-adapter", '~> 4.2.0', :platforms => [:mri, :mingw, :mswin] if RUBY_PLATFORM =~ /windows/
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
18
18
|
gem 'activerecord-oracle_enhanced-adapter', '~> 1.6.0' if ENV.has_key? 'ORACLE_HOME'
|
data/gemfiles/rails5.gemfile
CHANGED
@@ -11,7 +11,7 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
|
15
15
|
# gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
@@ -26,4 +26,4 @@ group :development, :test do
|
|
26
26
|
gem "activerecord-jdbcmssql-adapter", :platforms => :jruby
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :path => "../"
|
29
|
+
gemspec :path => "../"
|
data/gemfiles/rails5_0.gemfile
CHANGED
@@ -11,7 +11,7 @@ group :development, :test do
|
|
11
11
|
gem "mysql2", :platforms => [:mri, :mswin, :mingw]
|
12
12
|
gem "pg", :platforms => [:mri, :mingw]
|
13
13
|
|
14
|
-
gem "tiny_tds", :platforms => [:mri, :mingw]
|
14
|
+
gem "tiny_tds", :platforms => [:mri, :mingw] if RUBY_PLATFORM =~ /windows/
|
15
15
|
# gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw]
|
16
16
|
|
17
17
|
gem 'ruby-oci8', :platforms => [:mri, :mswin, :mingw] if ENV.has_key? 'ORACLE_HOME'
|
@@ -26,4 +26,4 @@ group :development, :test do
|
|
26
26
|
gem "activerecord-jdbcmssql-adapter", :platforms => :jruby
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :path => "../"
|
29
|
+
gemspec :path => "../"
|
data/lib/arel_extensions.rb
CHANGED
@@ -44,6 +44,12 @@ require 'arel_extensions/insert_manager'
|
|
44
44
|
|
45
45
|
require 'arel_extensions/common_sql_functions'
|
46
46
|
|
47
|
+
require 'arel_extensions/nodes/union'
|
48
|
+
require 'arel_extensions/nodes/union_all'
|
49
|
+
require 'arel_extensions/nodes/as'
|
50
|
+
|
51
|
+
|
52
|
+
|
47
53
|
module Arel
|
48
54
|
def self.rand
|
49
55
|
ArelExtensions::Nodes::Rand.new
|
@@ -90,3 +96,24 @@ end
|
|
90
96
|
Arel::InsertManager.class_eval do
|
91
97
|
include ArelExtensions::InsertManager
|
92
98
|
end
|
99
|
+
|
100
|
+
Arel::SelectManager.class_eval do
|
101
|
+
include ArelExtensions::Math
|
102
|
+
include ArelExtensions::Nodes
|
103
|
+
end
|
104
|
+
|
105
|
+
Arel::Nodes::Union.class_eval do
|
106
|
+
include ArelExtensions::Math
|
107
|
+
include ArelExtensions::Nodes
|
108
|
+
end
|
109
|
+
|
110
|
+
Arel::Nodes::UnionAll.class_eval do
|
111
|
+
include ArelExtensions::Math
|
112
|
+
include ArelExtensions::Nodes
|
113
|
+
end
|
114
|
+
|
115
|
+
Arel::Nodes::As.class_eval do
|
116
|
+
include ArelExtensions::Nodes
|
117
|
+
end
|
118
|
+
|
119
|
+
|
data/lib/arel_extensions/math.rb
CHANGED
@@ -4,6 +4,8 @@ require 'arel_extensions/nodes/concat'
|
|
4
4
|
require 'arel_extensions/nodes/date_diff'
|
5
5
|
require 'arel_extensions/nodes/duration'
|
6
6
|
require 'arel_extensions/nodes/wday'
|
7
|
+
require 'arel_extensions/nodes/union'
|
8
|
+
require 'arel_extensions/nodes/union_all'
|
7
9
|
|
8
10
|
module ArelExtensions
|
9
11
|
module Math
|
@@ -11,6 +13,7 @@ module ArelExtensions
|
|
11
13
|
#String and others (convert in string) allows you to concatenate 2 or more strings together.
|
12
14
|
#Date and integer adds or subtracts a specified time interval from a date.
|
13
15
|
def +(other)
|
16
|
+
return ArelExtensions::Nodes::Union.new self, other if self.is_a?(Arel::SelectManager) || self.is_a?(Arel::Nodes::Union)
|
14
17
|
return ArelExtensions::Nodes::Concat.new [self, other] if self.is_a?(Arel::Nodes::Quoted)
|
15
18
|
if self.is_a?(Arel::Nodes::Grouping)
|
16
19
|
if self.expr.left.is_a?(String) || self.expr.right.is_a?(String)
|
@@ -29,18 +32,23 @@ module ArelExtensions
|
|
29
32
|
else
|
30
33
|
ArelExtensions::Nodes::Concat.new [self, other]
|
31
34
|
end if self.is_a?(ArelExtensions::Nodes::Function)
|
32
|
-
|
33
|
-
if
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
36
|
+
if (!col) #if the column doesn't exist in the database
|
37
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new(self, other))
|
38
|
+
else
|
39
|
+
arg = col.type
|
40
|
+
if arg == :integer || (!arg)
|
41
|
+
other = other.to_i if other.is_a?(String)
|
42
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
|
43
|
+
elsif arg == :decimal || arg == :float
|
44
|
+
other = Arel.sql(other) if other.is_a?(String) # Arel should accept Float & BigDecimal!
|
45
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
|
46
|
+
elsif arg == :datetime || arg == :date
|
47
|
+
ArelExtensions::Nodes::DateAdd.new [self, other]
|
48
|
+
elsif arg == :string || arg == :text
|
49
|
+
ArelExtensions::Nodes::Concat.new [self, other]
|
50
|
+
end
|
51
|
+
end
|
44
52
|
end
|
45
53
|
|
46
54
|
#function returns the time between two dates
|
@@ -56,32 +64,58 @@ module ArelExtensions
|
|
56
64
|
else
|
57
65
|
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
58
66
|
end if self.is_a?(ArelExtensions::Nodes::Function)
|
59
|
-
|
60
|
-
if (
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
67
|
+
col = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s]
|
68
|
+
if (!col) #if the column doesn't exist in the database
|
69
|
+
return Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
70
|
+
else
|
71
|
+
arg = col.type
|
72
|
+
if (arg == :date || arg == :datetime)
|
73
|
+
case other
|
74
|
+
when Arel::Attributes::Attribute
|
75
|
+
col2 = Arel::Table.engine.connection.schema_cache.columns_hash(other.relation.table_name)[other.name.to_s]
|
76
|
+
if (!col2) #if the column doesn't exist in the database
|
77
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
78
|
+
else
|
79
|
+
arg2 = col2.type
|
80
|
+
if arg2 == :date || arg2 == :datetime
|
81
|
+
ArelExtensions::Nodes::DateDiff.new [self, other]
|
82
|
+
else
|
83
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
when Arel::Nodes::Node, DateTime, Time, String, Date
|
87
|
+
ArelExtensions::Nodes::DateDiff.new [self, other]
|
88
|
+
when Integer
|
89
|
+
ArelExtensions::Nodes::DateSub.new [self, other]
|
90
|
+
end
|
91
|
+
else
|
92
|
+
case other
|
93
|
+
when Integer, Float, BigDecimal
|
94
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
|
95
|
+
when String
|
96
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
|
97
|
+
else
|
98
|
+
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
84
102
|
end
|
85
|
-
|
103
|
+
|
104
|
+
#def *(other)
|
105
|
+
#if self.is_a?(Arel::SelectManager) || self.is_a?(Arel::Nodes::UnionAll) || self.is_a?(Arel::Nodes::Union)
|
106
|
+
#return ArelExtensions::Nodes::UnionAll.new self, other
|
107
|
+
#else
|
108
|
+
#return super(other)
|
109
|
+
#end
|
110
|
+
#end
|
111
|
+
|
112
|
+
def union(other)
|
113
|
+
return ArelExtensions::Nodes::Union.new(self,other)
|
114
|
+
end
|
115
|
+
|
116
|
+
def union_all(other)
|
117
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
118
|
+
end
|
119
|
+
|
86
120
|
end
|
87
121
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ArelExtensions
|
2
|
+
module Nodes
|
3
|
+
class Union < Arel::Nodes::Union
|
4
|
+
|
5
|
+
def initialize left,right
|
6
|
+
return super(left,right)
|
7
|
+
end
|
8
|
+
|
9
|
+
def +(other)
|
10
|
+
return ArelExtensions::Nodes::Union.new(self,other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def union(other)
|
14
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
15
|
+
end
|
16
|
+
|
17
|
+
def as other
|
18
|
+
ArelExtensions::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ArelExtensions
|
2
|
+
module Nodes
|
3
|
+
class UnionAll < Arel::Nodes::UnionAll
|
4
|
+
|
5
|
+
def initialize left,right
|
6
|
+
return super(left,right)
|
7
|
+
end
|
8
|
+
|
9
|
+
def union_all(other)
|
10
|
+
return ArelExtensions::Nodes::UnionAll.new(self,other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def as other
|
14
|
+
ArelExtensions::Nodes::As.new self, Arel::Nodes::SqlLiteral.new(other.to_s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -266,21 +266,32 @@ module ArelExtensions
|
|
266
266
|
collector
|
267
267
|
end
|
268
268
|
|
269
|
+
# add primary_key if not present, avoid zip
|
269
270
|
if Arel::VERSION.to_i < 7
|
270
271
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
272
|
+
raise ArgumentError, "missing columns" if o.cols.empty?
|
271
273
|
table = collector.value.sub(/\AINSERT INTO/, '')
|
272
274
|
into = " INTO#{table}"
|
273
275
|
collector = Arel::Collectors::SQLString.new
|
274
276
|
collector << "INSERT ALL\n"
|
275
|
-
o.
|
277
|
+
pk_name = @connection.primary_key(o.cols.first.relation.name)
|
278
|
+
if pk_name
|
279
|
+
pk_missing = !o.cols.detect{|c| c.name == pk_name }
|
280
|
+
into.sub!(/\(/, %Q[("#{pk_name.upcase}", ]) if pk_missing
|
281
|
+
else
|
282
|
+
pk_missing = false
|
283
|
+
end
|
284
|
+
o.left.each_with_index do |row, idx| # values
|
276
285
|
collector << "#{into} VALUES ("
|
286
|
+
collector << "NULL, " if pk_missing # expects to have a trigger to set the value before insert
|
277
287
|
v = Arel::Nodes::Values.new(row, o.cols)
|
278
288
|
len = v.expressions.length - 1
|
279
|
-
v.expressions.
|
289
|
+
v.expressions.each_with_index { |value, i|
|
280
290
|
case value
|
281
291
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
282
292
|
collector = visit value, collector
|
283
293
|
else
|
294
|
+
attr = v.columns[i]
|
284
295
|
collector << quote(value, attr && column_for(attr)).to_s
|
285
296
|
end
|
286
297
|
collector << Arel::Visitors::Oracle::COMMA unless i == len
|
@@ -305,11 +316,7 @@ module ArelExtensions
|
|
305
316
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
306
317
|
collector = visit value, collector
|
307
318
|
else
|
308
|
-
|
309
|
-
collector << quote(attr.type_cast_for_database(value))
|
310
|
-
else
|
311
|
-
collector << quote(value).to_s
|
312
|
-
end
|
319
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
313
320
|
end
|
314
321
|
collector << Arel::Visitors::Oracle::COMMA unless i == len
|
315
322
|
}
|
@@ -320,6 +327,8 @@ module ArelExtensions
|
|
320
327
|
end
|
321
328
|
end
|
322
329
|
|
330
|
+
|
331
|
+
|
323
332
|
end
|
324
333
|
end
|
325
334
|
end
|
@@ -99,11 +99,11 @@ module ArelExtensions
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def visit_ArelExtensions_Nodes_DateDiff o, collector
|
102
|
-
if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
103
|
+
"DATEDIFF('second', "
|
104
|
+
else
|
105
|
+
"DATEDIFF('day', "
|
106
|
+
end
|
107
107
|
collector = visit o.right, collector
|
108
108
|
collector << (o.right_node_type == :date ? '::date' : '::timestamp')
|
109
109
|
collector << Arel::Visitors::PostgreSQL::COMMA
|
@@ -159,6 +159,7 @@ module ArelExtensions
|
|
159
159
|
collector
|
160
160
|
end
|
161
161
|
|
162
|
+
|
162
163
|
end
|
163
164
|
end
|
164
165
|
end
|
@@ -187,12 +187,7 @@ module ArelExtensions
|
|
187
187
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
188
188
|
collector = visit value.as(attr.name), collector
|
189
189
|
else
|
190
|
-
|
191
|
-
collector << quote(attr.type_cast_for_database(value))
|
192
|
-
else
|
193
|
-
# collector << quote(value, column_for(attr))
|
194
|
-
collector << quote(value).to_s
|
195
|
-
end
|
190
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
196
191
|
if idx == 0
|
197
192
|
collector << " AS "
|
198
193
|
collector << quote(attr.name)
|
@@ -205,6 +200,37 @@ module ArelExtensions
|
|
205
200
|
collector
|
206
201
|
end
|
207
202
|
end
|
203
|
+
|
204
|
+
|
205
|
+
def visit_ArelExtensions_Nodes_Union o, collector
|
206
|
+
if o.left.is_a?(Arel::SelectManager)
|
207
|
+
collector = visit o.left.ast, collector
|
208
|
+
else
|
209
|
+
collector = visit o.left, collector
|
210
|
+
end
|
211
|
+
collector << " UNION "
|
212
|
+
if o.right.is_a?(Arel::SelectManager)
|
213
|
+
collector = visit o.right.ast, collector
|
214
|
+
else
|
215
|
+
collector = visit o.right, collector
|
216
|
+
end
|
217
|
+
collector
|
218
|
+
end
|
219
|
+
|
220
|
+
def visit_ArelExtensions_Nodes_UnionAll o, collector
|
221
|
+
if o.left.is_a?(Arel::SelectManager)
|
222
|
+
collector = visit o.left.ast, collector
|
223
|
+
else
|
224
|
+
collector = visit o.left, collector
|
225
|
+
end
|
226
|
+
collector << " UNION ALL "
|
227
|
+
if o.right.is_a?(Arel::SelectManager)
|
228
|
+
collector = visit o.right.ast, collector
|
229
|
+
else
|
230
|
+
collector = visit o.right, collector
|
231
|
+
end
|
232
|
+
collector
|
233
|
+
end
|
208
234
|
|
209
235
|
end
|
210
236
|
end
|
@@ -234,11 +234,11 @@ module ArelExtensions
|
|
234
234
|
end
|
235
235
|
|
236
236
|
def visit_ArelExtensions_Nodes_DateDiff o, collector
|
237
|
-
if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
237
|
+
collector << if o.left_node_type == :ruby_time || o.left_node_type == :datetime || o.left_node_type == :time
|
238
|
+
'TIMEDIFF('
|
239
|
+
else
|
240
|
+
'DATEDIFF('
|
241
|
+
end
|
242
242
|
collector = visit o.left, collector
|
243
243
|
collector << Arel::Visitors::ToSql::COMMA
|
244
244
|
collector = visit o.right, collector
|
@@ -331,13 +331,13 @@ module ArelExtensions
|
|
331
331
|
collector = visit o.left, collector
|
332
332
|
collector << Arel::Visitors::ToSql::COMMA
|
333
333
|
collector = visit o.sqlite_value(o.right), collector
|
334
|
-
collector <<
|
334
|
+
collector << ')'
|
335
335
|
collector
|
336
336
|
end
|
337
337
|
|
338
338
|
if Arel::VERSION.to_i < 7
|
339
339
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
340
|
-
collector <<
|
340
|
+
collector << 'VALUES '
|
341
341
|
row_nb = o.left.length
|
342
342
|
o.left.each_with_index do |row, idx|
|
343
343
|
collector << '('
|
@@ -358,7 +358,7 @@ module ArelExtensions
|
|
358
358
|
end
|
359
359
|
else
|
360
360
|
def visit_ArelExtensions_InsertManager_BulkValues o, collector
|
361
|
-
collector <<
|
361
|
+
collector << 'VALUES '
|
362
362
|
row_nb = o.left.length
|
363
363
|
o.left.each_with_index do |row, idx|
|
364
364
|
collector << '('
|
@@ -369,11 +369,7 @@ module ArelExtensions
|
|
369
369
|
when Arel::Nodes::SqlLiteral, Arel::Nodes::BindParam
|
370
370
|
collector = visit value, collector
|
371
371
|
else
|
372
|
-
|
373
|
-
collector << quote(attr.type_cast_for_database(value))
|
374
|
-
else
|
375
|
-
collector << quote(value).to_s
|
376
|
-
end
|
372
|
+
collector << (attr && attr.able_to_type_cast? ? quote(attr.type_cast_for_database(value)) : quote(value).to_s)
|
377
373
|
end
|
378
374
|
collector << Arel::Visitors::ToSql::COMMA unless i == len
|
379
375
|
}
|
@@ -383,7 +379,33 @@ module ArelExtensions
|
|
383
379
|
end
|
384
380
|
end
|
385
381
|
|
386
|
-
|
382
|
+
def visit_ArelExtensions_Nodes_Union o, collector
|
383
|
+
collector = visit o.left, collector
|
384
|
+
collector << " UNION "
|
385
|
+
collector = visit o.right, collector
|
386
|
+
collector
|
387
|
+
end
|
388
|
+
|
389
|
+
def visit_ArelExtensions_Nodes_UnionAll o, collector
|
390
|
+
collector = visit o.left, collector
|
391
|
+
collector << " UNION ALL "
|
392
|
+
collector = visit o.right, collector
|
393
|
+
collector
|
394
|
+
end
|
395
|
+
|
396
|
+
def visit_ArelExtensions_Nodes_As o, collector
|
397
|
+
if o.left.is_a?(ArelExtensions::Nodes::Union) || o.left.is_a?(ArelExtensions::Nodes::UnionAll)
|
398
|
+
collector << "("
|
399
|
+
collector = visit o.left, collector
|
400
|
+
collector << ") "
|
401
|
+
visit o.right, collector
|
402
|
+
else
|
403
|
+
collector = visit o.left, collector
|
404
|
+
collector << " AS "
|
405
|
+
visit o.right, collector
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
387
409
|
end
|
388
410
|
|
389
411
|
end
|
@@ -25,6 +25,18 @@ module ArelExtensions
|
|
25
25
|
# Math Functions
|
26
26
|
it "should not break Arel functions" do
|
27
27
|
compile(@price + 42).must_be_like %{("products"."price" + 42)}
|
28
|
+
compile(@table[:id] + @table[:pas_en_base])
|
29
|
+
.must_be_like %{("users"."id" + "users"."pas_en_base")}
|
30
|
+
compile(@table[:pas_en_base] + @table[:id])
|
31
|
+
.must_be_like %{("users"."pas_en_base" + "users"."id")}
|
32
|
+
compile(@table[:id] - @table[:pas_en_base])
|
33
|
+
.must_be_like %{("users"."id" - "users"."pas_en_base")}
|
34
|
+
compile(@table[:pas_en_base] - @table[:id])
|
35
|
+
.must_be_like %{("users"."pas_en_base" - "users"."id")}
|
36
|
+
compile(@table[:id] * @table[:pas_en_base])
|
37
|
+
.must_be_like %{"users"."id" * "users"."pas_en_base"}
|
38
|
+
compile(@table[:pas_en_base] * @table[:id])
|
39
|
+
.must_be_like %{"users"."pas_en_base" * "users"."id"}
|
28
40
|
end
|
29
41
|
|
30
42
|
it "should return right calculations on numbers" do
|
@@ -152,7 +164,41 @@ module ArelExtensions
|
|
152
164
|
(c.length / 42).round(2).floor > (@table[:updated_at] - Date.new(2000, 3, 31)).abs.ceil
|
153
165
|
).must_be_like %{FLOOR(ROUND(LENGTH("users"."name") / 42, 2)) > CEIL(ABS(TIMEDIFF("users"."updated_at", '2000-03-31 00:00:00 UTC')))}
|
154
166
|
end
|
155
|
-
|
167
|
+
|
168
|
+
# Unions
|
169
|
+
it "should accept union operators on queries and union nodes" do
|
170
|
+
c = @table.project(@table[:name])
|
171
|
+
compile(c + c)
|
172
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
173
|
+
(c + c).to_sql
|
174
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
175
|
+
(c + (c + c)).to_sql
|
176
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
177
|
+
((c + c) + c).to_sql
|
178
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
179
|
+
(c + c + c).to_sql
|
180
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")}
|
181
|
+
|
182
|
+
(c + c).as('union_table').to_sql
|
183
|
+
.must_be_like %{((SELECT "users"."name" FROM "users") UNION (SELECT "users"."name" FROM "users")) union_table}
|
184
|
+
|
185
|
+
|
186
|
+
c = @table.project(@table[:name])
|
187
|
+
compile(c.union_all(c))
|
188
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
189
|
+
(c.union_all(c)).to_sql
|
190
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
191
|
+
(c.union_all(c.union_all(c))).to_sql
|
192
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
193
|
+
((c.union_all(c)).union_all(c)).to_sql
|
194
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
195
|
+
(c.union_all(c).union_all(c)).to_sql
|
196
|
+
.must_be_like %{(SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")}
|
197
|
+
(c.union_all(c)).as('union_table').to_sql
|
198
|
+
.must_be_like %{((SELECT "users"."name" FROM "users") UNION ALL (SELECT "users"."name" FROM "users")) union_table}
|
199
|
+
|
200
|
+
end
|
201
|
+
|
156
202
|
end
|
157
203
|
end
|
158
|
-
end
|
204
|
+
end
|
@@ -74,6 +74,10 @@ module ArelExtensions
|
|
74
74
|
@updated_at = User.arel_table[:updated_at]
|
75
75
|
@comments = User.arel_table[:comments]
|
76
76
|
@price = Product.arel_table[:price]
|
77
|
+
@not_in_table = User.arel_table[:not_in_table]
|
78
|
+
|
79
|
+
@ut = User.arel_table
|
80
|
+
@pt = Product.arel_table
|
77
81
|
end
|
78
82
|
|
79
83
|
def teardown
|
@@ -352,7 +356,7 @@ module ArelExtensions
|
|
352
356
|
else
|
353
357
|
assert_equal 42, t(@lucas, @updated_at - Time.utc(2014, 3, 3, 12, 41, 18)).to_i
|
354
358
|
assert_equal(-3600, t(@lucas, @updated_at - Time.utc(2014, 3, 3, 13, 42)).to_i)
|
355
|
-
if @env_db == 'mssql'
|
359
|
+
if @env_db == 'mssql' || @env_db == 'oracle' # can't select booleans
|
356
360
|
assert_equal 0, @lucas.where((@updated_at - Time.utc(2014, 3, 3, 12, 41, 18)) < -1).count
|
357
361
|
else
|
358
362
|
assert_includes [nil, 0, 'f', false], t(@lucas, (@updated_at - Time.utc(2014, 3, 3, 12, 41, 18)) < -1)
|
@@ -360,6 +364,7 @@ module ArelExtensions
|
|
360
364
|
end
|
361
365
|
end
|
362
366
|
|
367
|
+
# TODO; cast types
|
363
368
|
def test_cast_types
|
364
369
|
skip "not implemented yet"
|
365
370
|
assert_equal true, t(@arthur, @score =~ /22/)
|
@@ -414,8 +419,23 @@ module ArelExtensions
|
|
414
419
|
(@score.round > 19).⋀(@score.round < 21).⋁(@score.round(1) >= 20.1)
|
415
420
|
).count
|
416
421
|
end
|
422
|
+
|
423
|
+
# Union operator
|
424
|
+
def test_union_operator
|
425
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.gt(22)) + @ut.project(@age).where(@age.lt(0))).to_sql).length
|
426
|
+
assert_equal 2, User.find_by_sql((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(21))).to_sql).length
|
427
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.gt(22)) + @ut.project(@age).where(@age.lt(0))).as('my_union')).length
|
428
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(23)) + @ut.project(@age).where(@age.eq(21))).as('my_union')).length
|
429
|
+
assert_equal 2, User.select('*').from((@ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(20)) + @ut.project(@age).where(@age.eq(21))).as('my_union')).length
|
430
|
+
|
431
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0)))).to_sql).length
|
432
|
+
assert_equal 3, User.find_by_sql((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21)))).to_sql).length
|
433
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.gt(22)).union_all(@ut.project(@age).where(@age.lt(0)))).as('my_union')).length
|
434
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(23))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
|
435
|
+
assert_equal 3, User.select('*').from((@ut.project(@age).where(@age.eq(20)).union_all(@ut.project(@age).where(@age.eq(20))).union_all(@ut.project(@age).where(@age.eq(21)))).as('my_union')).length
|
436
|
+
end
|
417
437
|
|
418
438
|
|
419
439
|
end
|
420
440
|
end
|
421
|
-
end
|
441
|
+
end
|
@@ -32,6 +32,20 @@ module ArelExtensions
|
|
32
32
|
t.column :updated_at, :datetime
|
33
33
|
t.column :score, :decimal, :precision => 20, :scale => 10
|
34
34
|
end
|
35
|
+
if @env_db == 'oracle'
|
36
|
+
@cnx.execute(%q[CREATE OR REPLACE trigger user_tests_trg
|
37
|
+
before insert on user_tests
|
38
|
+
for each row
|
39
|
+
BEGIN
|
40
|
+
IF :new.id IS NULL THEN
|
41
|
+
:new.id := user_tests_seq.nextval;
|
42
|
+
END IF;
|
43
|
+
END;])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class User < ActiveRecord::Base
|
48
|
+
self.table_name = 'user_tests'
|
35
49
|
end
|
36
50
|
|
37
51
|
def setup
|
@@ -39,8 +53,13 @@ module ArelExtensions
|
|
39
53
|
@table = Arel::Table.new(:user_tests)
|
40
54
|
@cols = ['id', 'name', 'comments', 'created_at']
|
41
55
|
@data = [
|
42
|
-
[23, 'nom1', "
|
43
|
-
[25, 'nom2', "sdfdsfdsfsdf", '2016-01-
|
56
|
+
[23, 'nom1', "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.", '2016-01-01'],
|
57
|
+
[25, 'nom2', "sdfdsfdsfsdf", '2016-01-02']
|
58
|
+
]
|
59
|
+
@cols2 = ['name', 'comments', 'created_at']
|
60
|
+
@data2 = [
|
61
|
+
['nom3', "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.", '2016-01-01'],
|
62
|
+
['nom4', "sdfdsfdsfsdf", '2016-01-04']
|
44
63
|
]
|
45
64
|
end
|
46
65
|
|
@@ -53,6 +72,11 @@ module ArelExtensions
|
|
53
72
|
insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(Arel::Table.engine).into(@table)
|
54
73
|
insert_manager.bulk_insert(@cols, @data)
|
55
74
|
@cnx.execute(insert_manager.to_sql)
|
75
|
+
assert_equal 2, User.count, "insertions failed"
|
76
|
+
insert_manager = Arel::VERSION.to_i > 6 ? Arel::InsertManager.new().into(@table) : Arel::InsertManager.new(Arel::Table.engine).into(@table)
|
77
|
+
insert_manager.bulk_insert(@cols2, @data2)
|
78
|
+
@cnx.execute(insert_manager.to_sql)
|
79
|
+
assert_equal 4, User.count, "insertions failed"
|
56
80
|
end
|
57
81
|
|
58
82
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arel_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0
|
4
|
+
version: '1.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yann Azoury
|
8
|
-
- Mathilde Pechdimaldjian
|
9
8
|
- Félix Bellanger
|
9
|
+
- Julien Delporte
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-
|
13
|
+
date: 2017-12-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: arel
|
@@ -71,8 +71,8 @@ dependencies:
|
|
71
71
|
description: Adds new features to Arel
|
72
72
|
email:
|
73
73
|
- yann.azoury@faveod.com
|
74
|
-
- mathilde.pechdimaldjian@gmail.com
|
75
74
|
- felix.bellanger@faveod.com
|
75
|
+
- julien.delporte@faveod.com
|
76
76
|
executables: []
|
77
77
|
extensions: []
|
78
78
|
extra_rdoc_files:
|
@@ -80,6 +80,7 @@ extra_rdoc_files:
|
|
80
80
|
- README.md
|
81
81
|
- functions.html
|
82
82
|
files:
|
83
|
+
- ".codeclimate.yml"
|
83
84
|
- ".gitignore"
|
84
85
|
- ".rubocop.yml"
|
85
86
|
- ".travis.yml"
|
@@ -118,6 +119,7 @@ files:
|
|
118
119
|
- lib/arel_extensions/math_functions.rb
|
119
120
|
- lib/arel_extensions/nodes.rb
|
120
121
|
- lib/arel_extensions/nodes/abs.rb
|
122
|
+
- lib/arel_extensions/nodes/as.rb
|
121
123
|
- lib/arel_extensions/nodes/blank.rb
|
122
124
|
- lib/arel_extensions/nodes/ceil.rb
|
123
125
|
- lib/arel_extensions/nodes/change_case.rb
|
@@ -140,6 +142,8 @@ files:
|
|
140
142
|
- lib/arel_extensions/nodes/substring.rb
|
141
143
|
- lib/arel_extensions/nodes/then.rb
|
142
144
|
- lib/arel_extensions/nodes/trim.rb
|
145
|
+
- lib/arel_extensions/nodes/union.rb
|
146
|
+
- lib/arel_extensions/nodes/union_all.rb
|
143
147
|
- lib/arel_extensions/nodes/wday.rb
|
144
148
|
- lib/arel_extensions/null_functions.rb
|
145
149
|
- lib/arel_extensions/railtie.rb
|