arel_extensions 0.9.8 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
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