nql 0.0.3 → 0.1.2
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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +18 -18
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +22 -0
- data/Gemfile +6 -4
- data/LICENSE +21 -21
- data/README.md +74 -54
- data/Rakefile +10 -2
- data/lib/nql.rb +12 -25
- data/lib/nql/error.rb +26 -0
- data/lib/nql/extension/active_record.rb +19 -0
- data/lib/nql/extension/arel.rb +7 -0
- data/lib/nql/extension/hash.rb +9 -0
- data/lib/nql/grammar.rb +842 -760
- data/lib/nql/grammar.treetop +110 -108
- data/lib/nql/query.rb +62 -0
- data/lib/nql/version.rb +3 -3
- data/nql.gemspec +27 -25
- data/spec/comparison_parser_spec.rb +164 -147
- data/spec/coordination_parser_spec.rb +42 -42
- data/spec/coverage_helper.rb +8 -0
- data/spec/migrations/20121108154439_create_countries.rb +9 -9
- data/spec/migrations/20121108154508_create_cities.rb +10 -10
- data/spec/models/city.rb +2 -2
- data/spec/models/country.rb +2 -2
- data/spec/query_spec.rb +48 -0
- data/spec/ransack_spec.rb +163 -134
- data/spec/spec_helper.rb +45 -44
- data/spec/sql_spec.rb +138 -116
- metadata +94 -39
- data/lib/nql/invalid_expression_error.rb +0 -7
@@ -1,43 +1,43 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe NQL::SyntaxParser, '-> Coordination' do
|
4
|
-
|
5
|
-
let(:parser) { NQL::SyntaxParser.new }
|
6
|
-
|
7
|
-
it 'And' do
|
8
|
-
tree = parser.parse('var1 = value1 & var2 = value2')
|
9
|
-
|
10
|
-
tree.left.text_value.strip.should eq 'var1 = value1'
|
11
|
-
tree.coordinator.text_value.should eq '&'
|
12
|
-
tree.right.text_value.strip.should eq 'var2 = value2'
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'Or' do
|
16
|
-
tree = parser.parse('var1 = value1 | var2 = value2')
|
17
|
-
|
18
|
-
tree.left.text_value.strip.should eq 'var1 = value1'
|
19
|
-
tree.coordinator.text_value.should eq '|'
|
20
|
-
tree.right.text_value.strip.should eq 'var2 = value2'
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'And then Or' do
|
24
|
-
tree = parser.parse('var1 = value1 & var2 = value2 | var3 = value3')
|
25
|
-
|
26
|
-
tree.left.text_value.strip.should eq 'var1 = value1'
|
27
|
-
tree.coordinator.text_value.should eq '&'
|
28
|
-
tree.right.left.text_value.strip.should eq 'var2 = value2'
|
29
|
-
tree.right.coordinator.text_value.strip.should eq '|'
|
30
|
-
tree.right.right.text_value.strip.should eq 'var3 = value3'
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'With parentheses' do
|
34
|
-
tree = parser.parse('(var1 = value1 & var2 = value2) | var3 = value3')
|
35
|
-
|
36
|
-
tree.left.expression.left.text_value.strip.should eq 'var1 = value1'
|
37
|
-
tree.left.expression.coordinator.text_value.should eq '&'
|
38
|
-
tree.left.expression.right.text_value.strip.should eq 'var2 = value2'
|
39
|
-
tree.coordinator.text_value.strip.should eq '|'
|
40
|
-
tree.right.text_value.strip.should eq 'var3 = value3'
|
41
|
-
end
|
42
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NQL::SyntaxParser, '-> Coordination' do
|
4
|
+
|
5
|
+
let(:parser) { NQL::SyntaxParser.new }
|
6
|
+
|
7
|
+
it 'And' do
|
8
|
+
tree = parser.parse('var1 = value1 & var2 = value2')
|
9
|
+
|
10
|
+
tree.left.text_value.strip.should eq 'var1 = value1'
|
11
|
+
tree.coordinator.text_value.should eq '&'
|
12
|
+
tree.right.text_value.strip.should eq 'var2 = value2'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'Or' do
|
16
|
+
tree = parser.parse('var1 = value1 | var2 = value2')
|
17
|
+
|
18
|
+
tree.left.text_value.strip.should eq 'var1 = value1'
|
19
|
+
tree.coordinator.text_value.should eq '|'
|
20
|
+
tree.right.text_value.strip.should eq 'var2 = value2'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'And then Or' do
|
24
|
+
tree = parser.parse('var1 = value1 & var2 = value2 | var3 = value3')
|
25
|
+
|
26
|
+
tree.left.text_value.strip.should eq 'var1 = value1'
|
27
|
+
tree.coordinator.text_value.should eq '&'
|
28
|
+
tree.right.left.text_value.strip.should eq 'var2 = value2'
|
29
|
+
tree.right.coordinator.text_value.strip.should eq '|'
|
30
|
+
tree.right.right.text_value.strip.should eq 'var3 = value3'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'With parentheses' do
|
34
|
+
tree = parser.parse('(var1 = value1 & var2 = value2) | var3 = value3')
|
35
|
+
|
36
|
+
tree.left.expression.left.text_value.strip.should eq 'var1 = value1'
|
37
|
+
tree.left.expression.coordinator.text_value.should eq '&'
|
38
|
+
tree.left.expression.right.text_value.strip.should eq 'var2 = value2'
|
39
|
+
tree.coordinator.text_value.strip.should eq '|'
|
40
|
+
tree.right.text_value.strip.should eq 'var3 = value3'
|
41
|
+
end
|
42
|
+
|
43
43
|
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
class CreateCountries < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :countries do |t|
|
4
|
-
t.string :name, null: false
|
5
|
-
|
6
|
-
t.timestamps
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
1
|
+
class CreateCountries < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :countries do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.boolean :landlocked, null: true
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
class CreateCities < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :cities do |t|
|
4
|
-
t.string :name, null: false
|
5
|
-
t.references :country
|
6
|
-
|
7
|
-
t.timestamps
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
1
|
+
class CreateCities < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :cities do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.references :country
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/spec/models/city.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
class City < ActiveRecord::Base
|
2
|
-
belongs_to :country
|
1
|
+
class City < ActiveRecord::Base
|
2
|
+
belongs_to :country
|
3
3
|
end
|
data/spec/models/country.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
class Country < ActiveRecord::Base
|
2
|
-
has_many :cities, dependent: :destroy
|
1
|
+
class Country < ActiveRecord::Base
|
2
|
+
has_many :cities, dependent: :destroy
|
3
3
|
end
|
data/spec/query_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NQL::Query do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ":memory:"
|
7
|
+
ActiveRecord::Base.connection
|
8
|
+
ActiveRecord::Migrator.migrate ActiveRecord::Migrator.migrations_path
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'Valid expression' do
|
12
|
+
query = NQL::Query.new Country, 'name: Argentina'
|
13
|
+
|
14
|
+
query.text.should eq 'name: Argentina'
|
15
|
+
query.expression.should be_a Treetop::Runtime::SyntaxNode
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'Valid with empty expression' do
|
19
|
+
query = NQL::Query.new Country, ''
|
20
|
+
|
21
|
+
query.text.should eq ''
|
22
|
+
query.expression.should be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'Valid with nil expression' do
|
26
|
+
query = NQL::Query.new Country, nil
|
27
|
+
|
28
|
+
query.text.should be_nil
|
29
|
+
query.expression.should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'Invalid model type' do
|
33
|
+
expect { NQL::Query.new Object, nil }.to raise_error NQL::InvalidModelError
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Invalid expression type' do
|
37
|
+
expect { NQL::Query.new Country, Object.new }.to raise_error NQL::DataTypeError
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'Invalid expression syntax' do
|
41
|
+
expect { NQL::Query.new Country, 'xyz1234' }.to raise_error NQL::SyntaxError
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'Invalid fields' do
|
45
|
+
expect { NQL::Query.new Country, 'name: Argentina | xyz: 1234 | abc: 0000'}.to raise_error NQL::AttributesNotFoundError
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/spec/ransack_spec.rb
CHANGED
@@ -1,135 +1,164 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe 'Ransack Query' do
|
4
|
-
|
5
|
-
let(:parser) { NQL::SyntaxParser.new }
|
6
|
-
|
7
|
-
context 'Single comparisons' do
|
8
|
-
|
9
|
-
it 'Equals' do
|
10
|
-
q = parser.parse('id = 1234').to_ransack
|
11
|
-
|
12
|
-
q[:c][0].should have_attribute 'id'
|
13
|
-
q[:c][0].should have_predicate 'eq'
|
14
|
-
q[:c][0].should have_value '1234'
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'Not equals' do
|
18
|
-
q = parser.parse('id != 1234').to_ransack
|
19
|
-
|
20
|
-
q[:c][0].should have_attribute 'id'
|
21
|
-
q[:c][0].should have_predicate 'not_eq'
|
22
|
-
q[:c][0].should have_value '1234'
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'Greater than' do
|
26
|
-
q = parser.parse('id > 1234').to_ransack
|
27
|
-
|
28
|
-
q[:c][0].should have_attribute 'id'
|
29
|
-
q[:c][0].should have_predicate 'gt'
|
30
|
-
q[:c][0].should have_value '1234'
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'Greater or equals than' do
|
34
|
-
q = parser.parse('id >= 1234').to_ransack
|
35
|
-
|
36
|
-
q[:c][0].should have_attribute 'id'
|
37
|
-
q[:c][0].should have_predicate 'gteq'
|
38
|
-
q[:c][0].should have_value '1234'
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'Less than' do
|
42
|
-
q = parser.parse('id < 1234').to_ransack
|
43
|
-
|
44
|
-
q[:c][0].should have_attribute 'id'
|
45
|
-
q[:c][0].should have_predicate 'lt'
|
46
|
-
q[:c][0].should have_value '1234'
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'Less or equals than' do
|
50
|
-
q = parser.parse('id <= 1234').to_ransack
|
51
|
-
|
52
|
-
q[:c][0].should have_attribute 'id'
|
53
|
-
q[:c][0].should have_predicate 'lteq'
|
54
|
-
q[:c][0].should have_value '1234'
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'Contains' do
|
58
|
-
q = parser.parse('id : 1234').to_ransack
|
59
|
-
|
60
|
-
q[:c][0].should have_attribute 'id'
|
61
|
-
q[:c][0].should have_predicate 'cont'
|
62
|
-
q[:c][0].should have_value '1234'
|
63
|
-
end
|
64
|
-
|
65
|
-
it '
|
66
|
-
q = parser.parse('
|
67
|
-
|
68
|
-
q[:c][0].should have_attribute '
|
69
|
-
q[:c][0].should have_predicate '
|
70
|
-
q[:c][0].should have_value '1234'
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
q
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
q
|
83
|
-
q[:
|
84
|
-
q[:
|
85
|
-
q[:
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
q
|
91
|
-
|
92
|
-
q[:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
q
|
97
|
-
q[:
|
98
|
-
q[:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
q
|
108
|
-
|
109
|
-
q[:g][0][:
|
110
|
-
q[:g][0][:
|
111
|
-
q[:g][0][:
|
112
|
-
q[:g][0][:
|
113
|
-
q[:g][0][:
|
114
|
-
q[:g][0][:
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
q[:g][0][:
|
122
|
-
q[:g][0][:
|
123
|
-
q[:g][0][:
|
124
|
-
q[:g][0][:
|
125
|
-
q[:g][0][:
|
126
|
-
q[:g][0][:
|
127
|
-
q[:g][0][:
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Ransack Query' do
|
4
|
+
|
5
|
+
let(:parser) { NQL::SyntaxParser.new }
|
6
|
+
|
7
|
+
context 'Single comparisons' do
|
8
|
+
|
9
|
+
it 'Equals' do
|
10
|
+
q = parser.parse('id = 1234').to_ransack
|
11
|
+
|
12
|
+
q[:c][0].should have_attribute 'id'
|
13
|
+
q[:c][0].should have_predicate 'eq'
|
14
|
+
q[:c][0].should have_value '1234'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'Not equals' do
|
18
|
+
q = parser.parse('id != 1234').to_ransack
|
19
|
+
|
20
|
+
q[:c][0].should have_attribute 'id'
|
21
|
+
q[:c][0].should have_predicate 'not_eq'
|
22
|
+
q[:c][0].should have_value '1234'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'Greater than' do
|
26
|
+
q = parser.parse('id > 1234').to_ransack
|
27
|
+
|
28
|
+
q[:c][0].should have_attribute 'id'
|
29
|
+
q[:c][0].should have_predicate 'gt'
|
30
|
+
q[:c][0].should have_value '1234'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Greater or equals than' do
|
34
|
+
q = parser.parse('id >= 1234').to_ransack
|
35
|
+
|
36
|
+
q[:c][0].should have_attribute 'id'
|
37
|
+
q[:c][0].should have_predicate 'gteq'
|
38
|
+
q[:c][0].should have_value '1234'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'Less than' do
|
42
|
+
q = parser.parse('id < 1234').to_ransack
|
43
|
+
|
44
|
+
q[:c][0].should have_attribute 'id'
|
45
|
+
q[:c][0].should have_predicate 'lt'
|
46
|
+
q[:c][0].should have_value '1234'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'Less or equals than' do
|
50
|
+
q = parser.parse('id <= 1234').to_ransack
|
51
|
+
|
52
|
+
q[:c][0].should have_attribute 'id'
|
53
|
+
q[:c][0].should have_predicate 'lteq'
|
54
|
+
q[:c][0].should have_value '1234'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'Contains' do
|
58
|
+
q = parser.parse('id : 1234').to_ransack
|
59
|
+
|
60
|
+
q[:c][0].should have_attribute 'id'
|
61
|
+
q[:c][0].should have_predicate 'cont'
|
62
|
+
q[:c][0].should have_value '1234'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'Not contains' do
|
66
|
+
q = parser.parse('id !: 1234').to_ransack
|
67
|
+
|
68
|
+
q[:c][0].should have_attribute 'id'
|
69
|
+
q[:c][0].should have_predicate 'not_cont'
|
70
|
+
q[:c][0].should have_value '1234'
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'Matches' do
|
74
|
+
q = parser.parse('id ~ 1234').to_ransack
|
75
|
+
|
76
|
+
q[:c][0].should have_attribute 'id'
|
77
|
+
q[:c][0].should have_predicate 'matches'
|
78
|
+
q[:c][0].should have_value '1234'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'Model references' do
|
82
|
+
q = parser.parse('models.id = 1234').to_ransack
|
83
|
+
q[:c][0].should have_attribute 'models_id'
|
84
|
+
q[:c][0].should have_predicate 'eq'
|
85
|
+
q[:c][0].should have_value '1234'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'Single comparison with parentheses and spaces' do
|
89
|
+
q = parser.parse('( models.id = 1234 )').to_ransack
|
90
|
+
q[:c][0].should have_attribute 'models_id'
|
91
|
+
q[:c][0].should have_predicate 'eq'
|
92
|
+
q[:c][0].should have_value '1234'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'Single comparison with parentheses' do
|
96
|
+
q = parser.parse('(models.id = 1234)').to_ransack
|
97
|
+
q[:c][0].should have_attribute 'models_id'
|
98
|
+
q[:c][0].should have_predicate 'eq'
|
99
|
+
q[:c][0].should have_value '1234'
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'Coordinated comparisons' do
|
105
|
+
|
106
|
+
it 'And' do
|
107
|
+
q = parser.parse('id > 1234 & name = abcd').to_ransack
|
108
|
+
|
109
|
+
q[:g][0][:m].should eq 'and'
|
110
|
+
q[:g][0][:c][0].should have_attribute 'id'
|
111
|
+
q[:g][0][:c][0].should have_predicate 'gt'
|
112
|
+
q[:g][0][:c][0].should have_value '1234'
|
113
|
+
q[:g][0][:c][1].should have_attribute 'name'
|
114
|
+
q[:g][0][:c][1].should have_predicate 'eq'
|
115
|
+
q[:g][0][:c][1].should have_value 'abcd'
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'Or' do
|
119
|
+
q = parser.parse('id < 1234 | name : abcd').to_ransack
|
120
|
+
|
121
|
+
q[:g][0][:m].should eq 'or'
|
122
|
+
q[:g][0][:c][0].should have_attribute 'id'
|
123
|
+
q[:g][0][:c][0].should have_predicate 'lt'
|
124
|
+
q[:g][0][:c][0].should have_value '1234'
|
125
|
+
q[:g][0][:c][1].should have_attribute 'name'
|
126
|
+
q[:g][0][:c][1].should have_predicate 'cont'
|
127
|
+
q[:g][0][:c][1].should have_value 'abcd'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'And then Or' do
|
131
|
+
q = parser.parse('id > 1234 & name = abcd | name : efgh').to_ransack
|
132
|
+
|
133
|
+
q[:g][0][:m].should eq 'and'
|
134
|
+
q[:g][0][:c][0].should have_attribute 'id'
|
135
|
+
q[:g][0][:c][0].should have_predicate 'gt'
|
136
|
+
q[:g][0][:c][0].should have_value '1234'
|
137
|
+
q[:g][0][:g][0][:m].should eq 'or'
|
138
|
+
q[:g][0][:g][0][:c][0].should have_attribute 'name'
|
139
|
+
q[:g][0][:g][0][:c][0].should have_predicate 'eq'
|
140
|
+
q[:g][0][:g][0][:c][0].should have_value 'abcd'
|
141
|
+
q[:g][0][:g][0][:c][1].should have_attribute 'name'
|
142
|
+
q[:g][0][:g][0][:c][1].should have_predicate 'cont'
|
143
|
+
q[:g][0][:g][0][:c][1].should have_value 'efgh'
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'With parentheses' do
|
147
|
+
q = parser.parse('(id > 1234 & name = abcd) | name : efgh').to_ransack
|
148
|
+
|
149
|
+
q[:g][0][:g][0][:m].should eq 'and'
|
150
|
+
q[:g][0][:g][0][:c][0].should have_attribute 'id'
|
151
|
+
q[:g][0][:g][0][:c][0].should have_predicate 'gt'
|
152
|
+
q[:g][0][:g][0][:c][0].should have_value '1234'
|
153
|
+
q[:g][0][:g][0][:c][1].should have_attribute 'name'
|
154
|
+
q[:g][0][:g][0][:c][1].should have_predicate 'eq'
|
155
|
+
q[:g][0][:g][0][:c][1].should have_value 'abcd'
|
156
|
+
q[:g][0][:m].should eq 'or'
|
157
|
+
q[:g][0][:c][0].should have_attribute 'name'
|
158
|
+
q[:g][0][:c][0].should have_predicate 'cont'
|
159
|
+
q[:g][0][:c][0].should have_value 'efgh'
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
135
164
|
end
|