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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37c0984cf29025e6a939c3c414c8adc08ed35fa2
4
- data.tar.gz: 4344e373d9059ea8517db8f1dc8a41e9176358cb
3
+ metadata.gz: d5142c37a719ede88896bbdcfa06b0690ddc4b75
4
+ data.tar.gz: 9da10d459cb7e1fa8a52305d12f1fb0ca945a537
5
5
  SHA512:
6
- metadata.gz: 7f53b62f6ab162cd2e9b36dd5989117e7faea7da8febb6c6419f42c164afd6af97a782fd3c58f4097a1a86fed470af5efff3b1615b673c6b3f914ca0c831efd9
7
- data.tar.gz: 567ec3f76cc130715855dbb0a674b3e0a89291f1945fd100c297ba7864b3473adc25bce974af00c272786fc14f5c5630adc0b7209bc41550ef42d7628970f5be
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
@@ -38,3 +38,9 @@ Style/FileName:
38
38
 
39
39
  Style/MethodCallWithArgsParentheses:
40
40
  Enabled: false
41
+
42
+ Performance/DoubleStartEndWith:
43
+ Enabled: true
44
+
45
+ Performance/RedundantMerge:
46
+ Enabled: true
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: oraclejdk7
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: false
131
+ email:
132
+ notifications:
133
+ - julien.delporte@faveod.com
134
+ - yann.azoury@faveod.com
@@ -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
- var PhantomError = require('node-phantom-simple/headless_error');
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
- // Configure the browser, open a tab
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
- // Configure the tab
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
- // Request the file, wait for the login page
41
+ // Begin login, wait for the login page
35
42
  .then(function () {
36
- return page.openPromise("https://edelivery.oracle.com/akam/otn/linux/" + env['ORACLE_FILE']).then(function (status) {
37
- if (status != 'success') throw "Unable to connect to oracle.com";
38
- return page.waitForSelectorPromise('input[type=password]', 5000);
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
- .catch(PhantomError, function (err) {
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
- throw err;
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 page.getPromise('cookies').then(function (cookies) {
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.promisifyAll(require('fs')).writeFileAsync(env['COOKIES'], data);
119
+ return Promise.promisify(require('fs').writeFile)(env['COOKIES'], data);
59
120
  });
60
121
  })
61
122
 
62
- // Submit the login form using cURL
123
+ // Download file using cURL
63
124
  .then(function () {
64
- return page.evaluatePromise(function () {
65
- var $form = jQuery(document.forms[0]);
66
- return {
67
- action: $form.prop('action'),
68
- data: $form.serialize()
69
- };
70
- })
71
- .then(function (form) {
72
- return browser.exitPromise().then(function () {
73
- var unapplied = credentials.filter(function (tuple) {
74
- var applied = false;
75
- form.data = form.data.replace(tuple[0] + '=', function (name) {
76
- applied = true;
77
- return name + encodeURIComponent(tuple[1]);
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) {
@@ -17,6 +17,7 @@ fi
17
17
 
18
18
  cd "$(dirname "$(readlink -f "$0")")"
19
19
 
20
+ echo "PhantomJS version $(phantomjs --version)"
20
21
  npm install bluebird node-phantom-simple
21
22
 
22
23
  export ORACLE_DOWNLOAD_FILE
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", :require => false, :platforms => [:mingw, :x64_mingw, :mswin]
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://secure.travis-ci.org/Faveod/arel-extensions.svg)](http://travis-ci.org/Faveod/arel-extensions)
4
- [![Latest Release](https://img.shields.io/gem/v/arel_extensions.svg)](https://rubygems.org/gems/arel_extensions)
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
- ![](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?type=total)
7
- ![](https://ruby-gem-downloads-badge.herokuapp.com/arel_extensions?label=downloads-current-version)
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 (in next version)
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 (name = 'str') UNION SELECT * FROM my_table WHERE (name= 'test')
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
- - codeclimate.com
2
+ + codeclimate.com
3
+ + gemnasium
4
+ + appveyor.com (windows)
5
+ + hakiri.io
3
6
  - coveralls.io
4
- - gemnasium
5
7
  - inch-ci.org
6
- - appveyor.com (windows)
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
@@ -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", "Mathilde Pechdimaldjian", "Félix Bellanger"]
9
- s.email = ["yann.azoury@faveod.com", "mathilde.pechdimaldjian@gmail.com", "felix.bellanger@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="12"><div>String functions</div></th>
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>
@@ -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'
@@ -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 => "../"
@@ -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 => "../"
@@ -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
+
@@ -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
- arg = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s].type
33
- if arg == :integer
34
- other = other.to_i if other.is_a?(String)
35
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
36
- elsif arg == :decimal || arg == :float
37
- other = Arel.sql(other) if other.is_a?(String) # Arel should accept Float & BigDecimal!
38
- Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new self, other)
39
- elsif arg == :datetime || arg == :date
40
- ArelExtensions::Nodes::DateAdd.new [self, other]
41
- elsif arg == :string || arg == :text
42
- ArelExtensions::Nodes::Concat.new [self, other]
43
- end
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
- arg = Arel::Table.engine.connection.schema_cache.columns_hash(self.relation.table_name)[self.name.to_s].type
60
- if (arg == :date || arg == :datetime)
61
- case other
62
- when Arel::Attributes::Attribute
63
- arg2 = Arel::Table.engine.connection.schema_cache.columns_hash(other.relation.table_name)[other.name.to_s].type
64
- if arg2 == :date || arg2 == :datetime
65
- ArelExtensions::Nodes::DateDiff.new [self, other]
66
- else
67
- ArelExtensions::Nodes::DateSub.new [self, other]
68
- end
69
- when Arel::Nodes::Node, DateTime, Time, String, Date
70
- ArelExtensions::Nodes::DateDiff.new [self, other]
71
- when Integer
72
- ArelExtensions::Nodes::DateSub.new [self, other]
73
- end
74
- else
75
- case other
76
- when Integer, Float, BigDecimal
77
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other.to_s)))
78
- when String
79
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, Arel.sql(other)))
80
- else
81
- Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
82
- end
83
- end
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,12 @@
1
+ module ArelExtensions
2
+ module Nodes
3
+ class As < Arel::Nodes::As
4
+
5
+ def initialize left,right
6
+ return super(left,right)
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+
@@ -10,7 +10,7 @@ module ArelExtensions
10
10
 
11
11
  # overrides as to make new Node like AliasPredication
12
12
  def as other
13
- Arel::Nodes::As.new(self, Arel.sql(other))
13
+ ArelExtensions::Nodes::As.new(self, Arel.sql(other))
14
14
  end
15
15
 
16
16
  def expr
@@ -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
+
@@ -1,3 +1,3 @@
1
1
  module ArelExtensions
2
- VERSION = "0.9.8".freeze
2
+ VERSION = "1.0".freeze
3
3
  end
@@ -69,6 +69,8 @@ module ArelExtensions
69
69
  collector
70
70
  end
71
71
 
72
+
73
+
72
74
  end
73
75
  end
74
76
  end
@@ -316,6 +316,7 @@ module ArelExtensions
316
316
  collector << "))"
317
317
  collector
318
318
  end
319
+
319
320
 
320
321
  end
321
322
  end
@@ -159,6 +159,7 @@ module ArelExtensions
159
159
  collector
160
160
  end
161
161
 
162
+
162
163
  end
163
164
  end
164
165
  end
@@ -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.left.each_with_index do |row, idx|
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.zip(v.columns).each_with_index { |(value, attr), i|
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
- if attr && attr.able_to_type_cast?
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
- collector << "DATEDIFF('second', "
104
- else
105
- collector << "DATEDIFF('day', "
106
- end
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
- if attr && attr.able_to_type_cast?
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
- collector << 'TIMEDIFF('
239
- else
240
- collector << "DATEDIFF("
241
- end
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 << "VALUES "
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 << "VALUES "
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
- if attr && attr.able_to_type_cast?
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', "sdfdsfdsfsdfsd fdsf dsf dsf sdf afdg fsdg sg sd gsdfg e 54435 344", '2016-01-01'],
43
- [25, 'nom2', "sdfdsfdsfsdf", '2016-01-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.9.8
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-06-02 00:00:00.000000000 Z
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