nql 0.0.3 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,45 +1,46 @@
1
- require 'nql'
2
-
3
- Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each {|f| require f}
4
-
5
- ActiveRecord::Migrator.migrations_path = "#{File.dirname(__FILE__)}/migrations"
6
-
7
- RSpec::Matchers.define :have_attribute do |expected|
8
- match do |actual|
9
- actual[:a]['0'][:name] == expected
10
- end
11
-
12
- failure_message_for_should do |actual|
13
- "expected: #{actual[:a]['0'][:name]}\n got: #{expected}"
14
- end
15
- end
16
-
17
- RSpec::Matchers.define :have_predicate do |expected|
18
- match do |actual|
19
- actual[:p] == expected
20
- end
21
-
22
- failure_message_for_should do |actual|
23
- "expected: #{actual[:p]}\n got: #{expected}"
24
- end
25
- end
26
-
27
- RSpec::Matchers.define :have_value do |expected|
28
- match do |actual|
29
- actual[:v]['0'][:value] == expected
30
- end
31
-
32
- failure_message_for_should do |actual|
33
- "expected: #{actual[:v]['0'][:value]}\n got: #{expected}"
34
- end
35
- end
36
-
37
- RSpec::Matchers.define :produce_sql do |expected|
38
- match do |actual|
39
- actual.to_sql.strip == expected
40
- end
41
-
42
- failure_message_for_should do |actual|
43
- "expected: #{actual.to_sql}\n got: #{expected}"
44
- end
1
+ require 'coverage_helper'
2
+ require 'nql'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each {|f| require f}
5
+
6
+ ActiveRecord::Migrator.migrations_path = "#{File.dirname(__FILE__)}/migrations"
7
+
8
+ RSpec::Matchers.define :have_attribute do |expected|
9
+ match do |actual|
10
+ actual[:a]['0'][:name] == expected
11
+ end
12
+
13
+ failure_message_for_should do |actual|
14
+ "expected: #{actual[:a]['0'][:name]}\n got: #{expected}"
15
+ end
16
+ end
17
+
18
+ RSpec::Matchers.define :have_predicate do |expected|
19
+ match do |actual|
20
+ actual[:p] == expected
21
+ end
22
+
23
+ failure_message_for_should do |actual|
24
+ "expected: #{actual[:p]}\n got: #{expected}"
25
+ end
26
+ end
27
+
28
+ RSpec::Matchers.define :have_value do |expected|
29
+ match do |actual|
30
+ actual[:v]['0'][:value] == expected
31
+ end
32
+
33
+ failure_message_for_should do |actual|
34
+ "expected: #{actual[:v]['0'][:value]}\n got: #{expected}"
35
+ end
36
+ end
37
+
38
+ RSpec::Matchers.define :produce_sql do |expected|
39
+ match do |actual|
40
+ actual.to_sql.strip == expected
41
+ end
42
+
43
+ failure_message_for_should do |actual|
44
+ "expected: #{actual.to_sql}\n got: #{expected}"
45
+ end
45
46
  end
@@ -1,117 +1,139 @@
1
- require 'spec_helper'
2
-
3
- describe 'SQL generation' 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
- context 'Single comparisons' do
12
-
13
- it 'Equals' do
14
- q = 'name = abcd'
15
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"name\" = 'abcd'"
16
- end
17
-
18
- it 'Not equals' do
19
- q = 'name != abcd'
20
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" != 'abcd')"
21
- end
22
-
23
- it 'Greater than' do
24
- q = 'name > abcd'
25
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" > 'abcd')"
26
- end
27
-
28
- it 'Greater or equals than' do
29
- q = 'name >= abcd'
30
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" >= 'abcd')"
31
- end
32
-
33
- it 'Less than' do
34
- q = 'name < abcd'
35
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" < 'abcd')"
36
- end
37
-
38
- it 'Less or equals than' do
39
- q = 'name <= abcd'
40
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" <= 'abcd')"
41
- end
42
-
43
- it 'Contains' do
44
- q = 'name : abcd'
45
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" LIKE '%abcd%')"
46
- end
47
-
48
- end
49
-
50
- context 'Coordinated comparisons' do
51
-
52
- it 'And' do
53
- q = 'id > 1234 & name = abcd'
54
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" > 1234 AND \"countries\".\"name\" = 'abcd'))"
55
- end
56
-
57
- it 'Or' do
58
- q = 'id < 1234 | name : abcd'
59
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" < 1234 OR \"countries\".\"name\" LIKE '%abcd%'))"
60
- end
61
-
62
- it 'And then Or' do
63
- q = 'id > 1234 & name = abcd | name : efgh'
64
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" > 1234 AND (\"countries\".\"name\" = 'abcd' OR \"countries\".\"name\" LIKE '%efgh%')))"
65
- end
66
-
67
- it 'With parentheses' do
68
- q = '(id > 1234 & name = abcd) | name : efgh'
69
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"name\" LIKE '%efgh%' OR (\"countries\".\"id\" > 1234 AND \"countries\".\"name\" = 'abcd')))"
70
- end
71
-
72
- end
73
-
74
- context 'Model joins' do
75
-
76
- it 'Parent join' do
77
- q = 'country.name : abcd'
78
- City.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"cities\".* FROM \"cities\" LEFT OUTER JOIN \"countries\" ON \"countries\".\"id\" = \"cities\".\"country_id\" WHERE (\"countries\".\"name\" LIKE '%abcd%')"
79
- end
80
-
81
- it 'Children join' do
82
- q = 'cities.name : abcd'
83
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\" LEFT OUTER JOIN \"cities\" ON \"cities\".\"country_id\" = \"countries\".\"id\" WHERE (\"cities\".\"name\" LIKE '%abcd%')"
84
- end
85
-
86
- it 'Children join distinct' do
87
- q = 'cities.name : abcd'
88
- Country.search(NQL.to_ransack(q)).result(distinct: true).should produce_sql "SELECT DISTINCT \"countries\".* FROM \"countries\" LEFT OUTER JOIN \"cities\" ON \"cities\".\"country_id\" = \"countries\".\"id\" WHERE (\"cities\".\"name\" LIKE '%abcd%')"
89
- end
90
-
91
- end
92
-
93
- context 'Invalid queries' do
94
-
95
- it 'Nil' do
96
- q = nil
97
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\""
98
- end
99
-
100
- it 'Empty' do
101
- q = ''
102
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\""
103
- end
104
-
105
- it 'Empty with spaces' do
106
- q = ' '
107
- Country.search(NQL.to_ransack(q)).result.should produce_sql "SELECT \"countries\".* FROM \"countries\""
108
- end
109
-
110
- it 'Partial expression' do
111
- q = 'id ='
112
- expect{Country.search(NQL.to_ransack(q)).result}.should raise_exception NQL::InvalidExpressionError
113
- end
114
-
115
- end
116
-
1
+ require 'spec_helper'
2
+
3
+ describe 'SQL generation' 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
+ context 'Single comparisons' do
12
+
13
+ it 'Equals' do
14
+ q = 'name = abcd'
15
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"name\" = 'abcd'"
16
+ end
17
+
18
+ it 'With parentheses' do
19
+ q = '(name = abcd)'
20
+ a = Country.nql(q)
21
+ a.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"name\" = 'abcd'"
22
+ end
23
+
24
+ it 'With parentheses and boolean value' do
25
+ q = '(landlocked = true)'
26
+ a = Country.nql(q)
27
+ a.should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE \"countries\".\"landlocked\" = 't'"
28
+ end
29
+
30
+ it 'Not equals' do
31
+ q = 'name != abcd'
32
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" != 'abcd')"
33
+ end
34
+
35
+ it 'Greater than' do
36
+ q = 'name > abcd'
37
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" > 'abcd')"
38
+ end
39
+
40
+ it 'Greater or equals than' do
41
+ q = 'name >= abcd'
42
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" >= 'abcd')"
43
+ end
44
+
45
+ it 'Less than' do
46
+ q = 'name < abcd'
47
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" < 'abcd')"
48
+ end
49
+
50
+ it 'Less or equals than' do
51
+ q = 'name <= abcd'
52
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" <= 'abcd')"
53
+ end
54
+
55
+ it 'Contains' do
56
+ q = 'name : abcd'
57
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" LIKE '%abcd%')"
58
+ end
59
+
60
+ it 'Not contains' do
61
+ q = 'name !: abcd'
62
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" NOT LIKE '%abcd%')"
63
+ end
64
+
65
+ it 'Matches' do
66
+ q = 'name ~ abcd'
67
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (\"countries\".\"name\" LIKE 'abcd')"
68
+ end
69
+
70
+ end
71
+
72
+ context 'Coordinated comparisons' do
73
+
74
+ it 'And' do
75
+ q = 'id > 1234 & name = abcd'
76
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" > 1234 AND \"countries\".\"name\" = 'abcd'))"
77
+ end
78
+
79
+ it 'Or' do
80
+ q = 'id < 1234 | name : abcd'
81
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" < 1234 OR \"countries\".\"name\" LIKE '%abcd%'))"
82
+ end
83
+
84
+ it 'And then Or' do
85
+ q = 'id > 1234 & name = abcd | name : efgh'
86
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"id\" > 1234 AND (\"countries\".\"name\" = 'abcd' OR \"countries\".\"name\" LIKE '%efgh%')))"
87
+ end
88
+
89
+ it 'With parentheses' do
90
+ q = '(id > 1234 & name = abcd) | name : efgh'
91
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE ((\"countries\".\"name\" LIKE '%efgh%' OR (\"countries\".\"id\" > 1234 AND \"countries\".\"name\" = 'abcd')))"
92
+ end
93
+
94
+ end
95
+
96
+ context 'Model joins' do
97
+
98
+ it 'Parent join' do
99
+ q = 'country.name : abcd'
100
+ City.nql(q).should produce_sql "SELECT \"cities\".* FROM \"cities\" LEFT OUTER JOIN \"countries\" ON \"countries\".\"id\" = \"cities\".\"country_id\" WHERE (\"countries\".\"name\" LIKE '%abcd%')"
101
+ end
102
+
103
+ it 'Children join' do
104
+ q = 'cities.name : abcd'
105
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" LEFT OUTER JOIN \"cities\" ON \"cities\".\"country_id\" = \"countries\".\"id\" WHERE (\"cities\".\"name\" LIKE '%abcd%')"
106
+ end
107
+
108
+ it 'Children join distinct' do
109
+ q = 'cities.name : abcd'
110
+ Country.nql(q, distinct: true).should produce_sql "SELECT DISTINCT \"countries\".* FROM \"countries\" LEFT OUTER JOIN \"cities\" ON \"cities\".\"country_id\" = \"countries\".\"id\" WHERE (\"cities\".\"name\" LIKE '%abcd%')"
111
+ end
112
+
113
+ end
114
+
115
+ context 'Invalid queries' do
116
+
117
+ it 'Nil' do
118
+ q = nil
119
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\""
120
+ end
121
+
122
+ it 'Empty' do
123
+ q = ''
124
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\""
125
+ end
126
+
127
+ it 'Empty with spaces' do
128
+ q = ' '
129
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\""
130
+ end
131
+
132
+ it 'Partial expression' do
133
+ q = 'id ='
134
+ Country.nql(q).should produce_sql "SELECT \"countries\".* FROM \"countries\" WHERE (1=2)"
135
+ end
136
+
137
+ end
138
+
117
139
  end
metadata CHANGED
@@ -1,82 +1,127 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Gabriel Naiman
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-11-09 00:00:00.000000000 Z
11
+ date: 2020-08-25 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: treetop
16
- requirement: &28678476 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: '0'
19
+ version: 1.4.0
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *28678476
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.0
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: activerecord
27
- requirement: &28678152 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - ! '>='
31
+ - - ">="
31
32
  - !ruby/object:Gem::Version
32
33
  version: 3.2.0
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *28678152
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: activesupport
38
- requirement: &28677828 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
- - - ! '>='
45
+ - - ">="
42
46
  - !ruby/object:Gem::Version
43
47
  version: 3.2.0
44
48
  type: :runtime
45
49
  prerelease: false
46
- version_requirements: *28677828
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.0
47
55
  - !ruby/object:Gem::Dependency
48
56
  name: ransack
49
- requirement: &28677600 !ruby/object:Gem::Requirement
50
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
51
58
  requirements:
52
- - - ! '>='
59
+ - - "~>"
53
60
  - !ruby/object:Gem::Version
54
- version: '0'
61
+ version: '0.7'
55
62
  type: :runtime
56
63
  prerelease: false
57
- version_requirements: *28677600
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.7'
58
69
  - !ruby/object:Gem::Dependency
59
- name: sqlite3
60
- requirement: &28677324 !ruby/object:Gem::Requirement
61
- none: false
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
62
79
  requirements:
63
- - - ! '>='
80
+ - - ">="
64
81
  - !ruby/object:Gem::Version
65
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.0
66
90
  type: :development
67
91
  prerelease: false
68
- version_requirements: *28677324
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.0
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rspec
71
- requirement: &28677060 !ruby/object:Gem::Requirement
72
- none: false
99
+ requirement: !ruby/object:Gem::Requirement
73
100
  requirements:
74
- - - ! '>='
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
75
116
  - !ruby/object:Gem::Version
76
117
  version: '0'
77
118
  type: :development
78
119
  prerelease: false
79
- version_requirements: *28677060
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
80
125
  description: Natural Query Language built on top of ActiveRecord and Ransack
81
126
  email:
82
127
  - gabynaiman@gmail.com
@@ -84,57 +129,67 @@ executables: []
84
129
  extensions: []
85
130
  extra_rdoc_files: []
86
131
  files:
87
- - .gitignore
132
+ - ".coveralls.yml"
133
+ - ".gitignore"
134
+ - ".ruby-gemset"
135
+ - ".ruby-version"
136
+ - ".travis.yml"
88
137
  - Gemfile
89
138
  - LICENSE
90
139
  - README.md
91
140
  - Rakefile
92
141
  - lib/nql.rb
142
+ - lib/nql/error.rb
143
+ - lib/nql/extension/active_record.rb
144
+ - lib/nql/extension/arel.rb
145
+ - lib/nql/extension/hash.rb
93
146
  - lib/nql/grammar.rb
94
147
  - lib/nql/grammar.treetop
95
- - lib/nql/invalid_expression_error.rb
148
+ - lib/nql/query.rb
96
149
  - lib/nql/version.rb
97
150
  - nql.gemspec
98
151
  - spec/comparison_parser_spec.rb
99
152
  - spec/coordination_parser_spec.rb
153
+ - spec/coverage_helper.rb
100
154
  - spec/migrations/20121108154439_create_countries.rb
101
155
  - spec/migrations/20121108154508_create_cities.rb
102
156
  - spec/models/city.rb
103
157
  - spec/models/country.rb
158
+ - spec/query_spec.rb
104
159
  - spec/ransack_spec.rb
105
160
  - spec/spec_helper.rb
106
161
  - spec/sql_spec.rb
107
162
  homepage: https://github.com/gabynaiman/nql
108
163
  licenses: []
164
+ metadata: {}
109
165
  post_install_message:
110
166
  rdoc_options: []
111
167
  require_paths:
112
168
  - lib
113
169
  required_ruby_version: !ruby/object:Gem::Requirement
114
- none: false
115
170
  requirements:
116
- - - ! '>='
171
+ - - ">="
117
172
  - !ruby/object:Gem::Version
118
173
  version: '0'
119
174
  required_rubygems_version: !ruby/object:Gem::Requirement
120
- none: false
121
175
  requirements:
122
- - - ! '>='
176
+ - - ">="
123
177
  - !ruby/object:Gem::Version
124
178
  version: '0'
125
179
  requirements: []
126
- rubyforge_project:
127
- rubygems_version: 1.8.16
180
+ rubygems_version: 3.0.6
128
181
  signing_key:
129
- specification_version: 3
182
+ specification_version: 4
130
183
  summary: Natural Query Language built on top of ActiveRecord and Ransack
131
184
  test_files:
132
185
  - spec/comparison_parser_spec.rb
133
186
  - spec/coordination_parser_spec.rb
187
+ - spec/coverage_helper.rb
134
188
  - spec/migrations/20121108154439_create_countries.rb
135
189
  - spec/migrations/20121108154508_create_cities.rb
136
190
  - spec/models/city.rb
137
191
  - spec/models/country.rb
192
+ - spec/query_spec.rb
138
193
  - spec/ransack_spec.rb
139
194
  - spec/spec_helper.rb
140
195
  - spec/sql_spec.rb