dbee-active_record 2.0.1 → 2.1.0.pre.alpha.1

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.
@@ -10,7 +10,7 @@
10
10
  module Dbee
11
11
  module Providers
12
12
  class ActiveRecordProvider
13
- VERSION = '2.0.1'
13
+ VERSION = '2.1.0-alpha.1'
14
14
  end
15
15
  end
16
16
  end
@@ -10,6 +10,27 @@
10
10
  # Enable logging using something like:
11
11
  # ActiveRecord::Base.logger = Logger.new(STDERR)
12
12
 
13
+ class Field < ActiveRecord::Base
14
+ has_many :patient_field_values
15
+ end
16
+
17
+ class Patient < ActiveRecord::Base
18
+ has_many :patient_field_values
19
+ has_many :patient_payments
20
+
21
+ accepts_nested_attributes_for :patient_field_values
22
+ accepts_nested_attributes_for :patient_payments
23
+ end
24
+
25
+ class PatientFieldValue < ActiveRecord::Base
26
+ belongs_to :patient
27
+ belongs_to :field
28
+ end
29
+
30
+ class PatientPayment < ActiveRecord::Base
31
+ belongs_to :patient
32
+ end
33
+
13
34
  def connect_to_db(name)
14
35
  config = yaml_file_read('spec', 'config', 'database.yaml')[name.to_s]
15
36
  ActiveRecord::Base.establish_connection(config)
@@ -17,39 +38,40 @@ end
17
38
 
18
39
  def load_schema
19
40
  ActiveRecord::Schema.define do
41
+ # Movie Theater Schema
20
42
  create_table :theaters do |t|
21
- t.column :name, :string
43
+ t.column :name, :string
22
44
  t.column :partition, :string
23
- t.column :active, :boolean
45
+ t.column :active, :boolean
24
46
  t.column :inspected, :boolean
25
47
  t.timestamps
26
48
  end
27
49
 
28
50
  create_table :members do |t|
29
- t.column :tid, :integer
51
+ t.column :tid, :integer
30
52
  t.column :account_number, :string
31
- t.column :partition, :string
53
+ t.column :partition, :string
32
54
  t.timestamps
33
55
  end
34
56
 
35
57
  create_table :demographics do |t|
36
58
  t.column :member_id, :integer
37
- t.column :name, :string
59
+ t.column :name, :string
38
60
  t.timestamps
39
61
  end
40
62
 
41
63
  create_table :phone_numbers do |t|
42
64
  t.column :demographic_id, :integer
43
- t.column :phone_type, :string
44
- t.column :phone_number, :string
65
+ t.column :phone_type, :string
66
+ t.column :phone_number, :string
45
67
  t.timestamps
46
68
  end
47
69
 
48
70
  create_table :movies do |t|
49
71
  t.column :member_id, :integer
50
- t.column :name, :string
51
- t.column :genre, :string
52
- t.column :favorite, :boolean, default: false, null: false
72
+ t.column :name, :string
73
+ t.column :genre, :string
74
+ t.column :favorite, :boolean, default: false, null: false
53
75
  t.timestamps
54
76
  end
55
77
 
@@ -60,10 +82,10 @@ def load_schema
60
82
 
61
83
  create_table :animals do |t|
62
84
  t.column :owner_id, :integer
63
- t.column :toy_id, :integer
64
- t.column :type, :string
65
- t.column :name, :string
66
- t.column :deleted, :boolean
85
+ t.column :toy_id, :integer
86
+ t.column :type, :string
87
+ t.column :name, :string
88
+ t.column :deleted, :boolean
67
89
  t.timestamps
68
90
  end
69
91
 
@@ -76,5 +98,99 @@ def load_schema
76
98
  t.column :laser, :boolean
77
99
  t.timestamps
78
100
  end
101
+
102
+ # Patient Schema
103
+ create_table :fields do |t|
104
+ t.column :section, :string
105
+ t.column :key, :string
106
+ t.timestamps
107
+ end
108
+
109
+ create_table :patients do |t|
110
+ t.column :first, :string
111
+ t.column :middle, :string
112
+ t.column :last, :string
113
+ t.timestamps
114
+ end
115
+
116
+ create_table :patient_field_values do |t|
117
+ t.column :patient_id, :integer
118
+ t.column :field_id, :integer
119
+ t.column :value, :string
120
+ t.timestamps
121
+ end
122
+
123
+ create_table :patient_payments do |t|
124
+ t.column :patient_id, :integer
125
+ t.column :amount, :decimal
126
+ t.timestamps
127
+ end
79
128
  end
80
129
  end
130
+
131
+ def load_data
132
+ demo_dob_field = Field.create!(section: 'demographics', key: 'dob')
133
+ demo_drivers_license_field = Field.create!(section: 'demographics', key: 'drivers_license')
134
+ demo_notes_field = Field.create!(section: 'demographics', key: 'notes')
135
+
136
+ contact_phone_number_field = Field.create!(section: 'contact', key: 'phone_number')
137
+ contact_notes_field = Field.create!(section: 'contact', key: 'notes')
138
+
139
+ Patient.create!(
140
+ first: 'Bozo',
141
+ middle: 'The',
142
+ last: 'Clown',
143
+ patient_field_values_attributes: [
144
+ {
145
+ field: demo_dob_field,
146
+ value: '1904-04-04'
147
+ },
148
+ {
149
+ field: demo_notes_field,
150
+ value: 'The patient is funny!'
151
+ },
152
+ {
153
+ field: demo_drivers_license_field,
154
+ value: '82-54-hut-hut-hike!'
155
+ },
156
+ {
157
+ field: contact_phone_number_field,
158
+ value: '555-555-5555'
159
+ },
160
+ {
161
+ field: contact_notes_field,
162
+ value: 'Do not call this patient at night!'
163
+ }
164
+ ],
165
+ patient_payments_attributes: [
166
+ { amount: 5 },
167
+ { amount: 10 },
168
+ { amount: 15 }
169
+ ]
170
+ )
171
+
172
+ Patient.create!(
173
+ first: 'Frank',
174
+ last: 'Rizzo',
175
+ patient_payments_attributes: [
176
+ { amount: 50 },
177
+ { amount: 150 }
178
+ ]
179
+ )
180
+
181
+ Patient.create!(
182
+ first: 'Bugs',
183
+ middle: 'The',
184
+ last: 'Bunny',
185
+ patient_field_values_attributes: [
186
+ {
187
+ field: demo_dob_field,
188
+ value: '2040-01-01'
189
+ },
190
+ {
191
+ field: contact_notes_field,
192
+ value: 'Call anytime!!'
193
+ }
194
+ ]
195
+ )
196
+ end
@@ -79,7 +79,7 @@ describe Dbee::Providers::ActiveRecordProvider do
79
79
  end
80
80
  end
81
81
 
82
- context 'Executing SQL' do
82
+ context 'Shallow SQL Execution' do
83
83
  %w[sqlite].each do |dbms|
84
84
  context dbms do
85
85
  before(:all) do
@@ -108,4 +108,105 @@ describe Dbee::Providers::ActiveRecordProvider do
108
108
  end
109
109
  end
110
110
  end
111
+
112
+ describe 'Deep SQL execution' do
113
+ before(:all) do
114
+ connect_to_db(:sqlite)
115
+ load_schema
116
+ load_data
117
+ end
118
+
119
+ describe 'pivoting' do
120
+ let(:snapshot_path) do
121
+ %w[
122
+ spec
123
+ fixtures
124
+ active_record_snapshots
125
+ two_table_query_with_pivoting.yaml
126
+ ]
127
+ end
128
+
129
+ let(:snapshot) { yaml_file_read(*snapshot_path) }
130
+ let(:query) { Dbee::Query.make(snapshot['query']) }
131
+ let(:model) { Dbee::Model.make(models['Patients']) }
132
+
133
+ it 'pivots table rows into columns' do
134
+ sql = described_class.new.sql(model, query)
135
+
136
+ results = ActiveRecord::Base.connection.execute(sql)
137
+
138
+ expect(results[0]).to include(
139
+ 'First Name' => 'Bozo',
140
+ 'Date of Birth' => '1904-04-04',
141
+ 'Drivers License #' => '82-54-hut-hut-hike!',
142
+ 'Demographic Notes' => 'The patient is funny!',
143
+ 'Contact Notes' => 'Do not call this patient at night!'
144
+ )
145
+
146
+ expect(results[1]).to include(
147
+ 'First Name' => 'Frank',
148
+ 'Date of Birth' => nil,
149
+ 'Drivers License #' => nil,
150
+ 'Demographic Notes' => nil,
151
+ 'Contact Notes' => nil
152
+ )
153
+
154
+ expect(results[2]).to include(
155
+ 'First Name' => 'Bugs',
156
+ 'Date of Birth' => '2040-01-01',
157
+ 'Drivers License #' => nil,
158
+ 'Demographic Notes' => nil,
159
+ 'Contact Notes' => 'Call anytime!!'
160
+ )
161
+ end
162
+ end
163
+
164
+ describe 'aggregation' do
165
+ let(:snapshot_path) do
166
+ %w[
167
+ spec
168
+ fixtures
169
+ active_record_snapshots
170
+ two_table_query_with_aggregation.yaml
171
+ ]
172
+ end
173
+
174
+ let(:snapshot) { yaml_file_read(*snapshot_path) }
175
+ let(:query) { Dbee::Query.make(snapshot['query']) }
176
+ let(:model) { Dbee::Model.make(models['Patients']) }
177
+
178
+ it 'executes correct SQL aggregate functions' do
179
+ sql = described_class.new.sql(model, query)
180
+
181
+ results = ActiveRecord::Base.connection.execute(sql)
182
+
183
+ expect(results[0]).to include(
184
+ 'First Name' => 'Bozo',
185
+ 'Ave Payment' => 10,
186
+ 'Number of Payments' => 3,
187
+ 'Max Payment' => 15,
188
+ 'Min Payment' => 5,
189
+ 'Total Paid' => 30
190
+ )
191
+
192
+ expect(results[1]).to include(
193
+ 'First Name' => 'Frank',
194
+ 'Ave Payment' => 100,
195
+ 'Number of Payments' => 2,
196
+ 'Max Payment' => 150,
197
+ 'Min Payment' => 50,
198
+ 'Total Paid' => 200
199
+ )
200
+
201
+ expect(results[2]).to include(
202
+ 'First Name' => 'Bugs',
203
+ 'Ave Payment' => nil,
204
+ 'Number of Payments' => 0,
205
+ 'Max Payment' => nil,
206
+ 'Min Payment' => nil,
207
+ 'Total Paid' => nil
208
+ )
209
+ end
210
+ end
211
+ end
111
212
  end
@@ -45,6 +45,10 @@ query:
45
45
  - 'Netflix'
46
46
  - 'Hulu'
47
47
  key_path: name
48
+ - type: not_equals
49
+ value:
50
+ - 'YouTube Super Video'
51
+ key_path: name
48
52
  - type: not_contain
49
53
  value:
50
54
  - 'tfli'
@@ -59,67 +63,71 @@ sqlite_readable: |+
59
63
  SELECT "theaters"."id" AS 'ID #',
60
64
  "theaters"."name" AS 'name'
61
65
  FROM "theaters" "theaters"
62
- WHERE ("theaters"."name" = 'AMC' OR "theaters"."name" = 'Regal') AND
66
+ WHERE "theaters"."name" IN ('AMC', 'Regal') AND
63
67
  "theaters"."name" LIKE 'A%' AND
64
68
  "theaters"."name" LIKE '%m%' AND
65
69
  "theaters"."active" = 'false' AND
66
- (("theaters"."active" = 't' OR "theaters"."active" = 'f') OR "theaters"."active" IS NULL) AND
67
- (("theaters"."inspected" = 'f' OR "theaters"."inspected" = 't') OR "theaters"."inspected" IS NULL) AND
70
+ ("theaters"."active" IS NULL OR "theaters"."active" IN ('t', 'f')) AND
71
+ ("theaters"."inspected" IS NULL OR "theaters"."inspected" IN ('f', 't')) AND
68
72
  "theaters"."created_at" <= '2019-03-04' AND
69
73
  "theaters"."created_at" < '2018-03-04' AND
70
74
  "theaters"."created_at" >= '2001-03-04' AND
71
75
  "theaters"."created_at" > '2002-03-04' AND
72
- ("theaters"."name" != 'Netflix' OR "theaters"."name" != 'Hulu') AND
76
+ "theaters"."name" NOT IN ('Netflix', 'Hulu') AND
77
+ "theaters"."name" != 'YouTube Super Video' AND
73
78
  ("theaters"."name" NOT LIKE '%tfli%' OR "theaters"."name" NOT LIKE '%ul%') AND
74
79
  ("theaters"."name" NOT LIKE 'netf%' OR "theaters"."name" NOT LIKE 'hu%')
75
80
  sqlite_not_readable: |+
76
81
  SELECT "t0"."id" AS 'c0',
77
82
  "t0"."name" AS 'c1'
78
83
  FROM "theaters" "t0"
79
- WHERE ("t0"."name" = 'AMC' OR "t0"."name" = 'Regal') AND
84
+ WHERE "t0"."name" IN ('AMC', 'Regal') AND
80
85
  "t0"."name" LIKE 'A%' AND
81
86
  "t0"."name" LIKE '%m%' AND
82
87
  "t0"."active" = 'false' AND
83
- (("t0"."active" = 't' OR "t0"."active" = 'f') OR "t0"."active" IS NULL) AND
84
- (("t0"."inspected" = 'f' OR "t0"."inspected" = 't') OR "t0"."inspected" IS NULL) AND
88
+ ("t0"."active" IS NULL OR "t0"."active" IN ('t', 'f')) AND
89
+ ("t0"."inspected" IS NULL OR "t0"."inspected" IN ('f', 't')) AND
85
90
  "t0"."created_at" <= '2019-03-04' AND
86
91
  "t0"."created_at" < '2018-03-04' AND
87
92
  "t0"."created_at" >= '2001-03-04' AND
88
93
  "t0"."created_at" > '2002-03-04' AND
89
- ("t0"."name" != 'Netflix' OR "t0"."name" != 'Hulu') AND
94
+ "t0"."name" NOT IN ('Netflix', 'Hulu') AND
95
+ "t0"."name" != 'YouTube Super Video' AND
90
96
  ("t0"."name" NOT LIKE '%tfli%' OR "t0"."name" NOT LIKE '%ul%') AND
91
97
  ("t0"."name" NOT LIKE 'netf%' OR "t0"."name" NOT LIKE 'hu%')
92
98
  mysql_readable: |+
93
99
  SELECT `theaters`.`id` AS 'ID #',
94
100
  `theaters`.`name` AS 'name'
95
101
  FROM `theaters` `theaters`
96
- WHERE (`theaters`.`name` = 'AMC' OR `theaters`.`name` = 'Regal') AND
102
+ WHERE `theaters`.`name` IN ('AMC', 'Regal') AND
97
103
  `theaters`.`name` LIKE 'A%' AND
98
104
  `theaters`.`name` LIKE '%m%' AND
99
105
  `theaters`.`active` = 'false' AND
100
- ((`theaters`.`active` = TRUE OR `theaters`.`active` = FALSE) OR `theaters`.`active` IS NULL) AND
101
- ((`theaters`.`inspected` = FALSE OR `theaters`.`inspected` = TRUE) OR `theaters`.`inspected` IS NULL) AND
106
+ (`theaters`.`active` IS NULL OR `theaters`.`active` IN (TRUE, FALSE)) AND
107
+ (`theaters`.`inspected` IS NULL OR `theaters`.`inspected` IN (FALSE, TRUE)) AND
102
108
  `theaters`.`created_at` <= '2019-03-04' AND
103
109
  `theaters`.`created_at` < '2018-03-04' AND
104
110
  `theaters`.`created_at` >= '2001-03-04' AND
105
111
  `theaters`.`created_at` > '2002-03-04' AND
106
- (`theaters`.`name` != 'Netflix' OR `theaters`.`name` != 'Hulu') AND
112
+ `theaters`.`name` NOT IN ('Netflix', 'Hulu') AND
113
+ `theaters`.`name` != 'YouTube Super Video' AND
107
114
  (`theaters`.`name` NOT LIKE '%tfli%' OR `theaters`.`name` NOT LIKE '%ul%') AND
108
115
  (`theaters`.`name` NOT LIKE 'netf%' OR `theaters`.`name` NOT LIKE 'hu%')
109
116
  mysql_not_readable: |+
110
117
  SELECT `t0`.`id` AS 'c0',
111
118
  `t0`.`name` AS 'c1'
112
119
  FROM `theaters` `t0`
113
- WHERE (`t0`.`name` = 'AMC' OR `t0`.`name` = 'Regal') AND
120
+ WHERE `t0`.`name` IN ('AMC', 'Regal') AND
114
121
  `t0`.`name` LIKE 'A%' AND
115
122
  `t0`.`name` LIKE '%m%' AND
116
123
  `t0`.`active` = 'false' AND
117
- ((`t0`.`active` = TRUE OR `t0`.`active` = FALSE) OR `t0`.`active` IS NULL) AND
118
- ((`t0`.`inspected` = FALSE OR `t0`.`inspected` = TRUE) OR `t0`.`inspected` IS NULL) AND
124
+ (`t0`.`active` IS NULL OR `t0`.`active` IN (TRUE, FALSE)) AND
125
+ (`t0`.`inspected` IS NULL OR `t0`.`inspected` IN (FALSE, TRUE)) AND
119
126
  `t0`.`created_at` <= '2019-03-04' AND
120
127
  `t0`.`created_at` < '2018-03-04' AND
121
128
  `t0`.`created_at` >= '2001-03-04' AND
122
129
  `t0`.`created_at` > '2002-03-04' AND
123
- (`t0`.`name` != 'Netflix' OR `t0`.`name` != 'Hulu') AND
130
+ `t0`.`name` NOT IN ('Netflix', 'Hulu') AND
131
+ `t0`.`name` != 'YouTube Super Video' AND
124
132
  (`t0`.`name` NOT LIKE '%tfli%' OR `t0`.`name` NOT LIKE '%ul%') AND
125
133
  (`t0`.`name` NOT LIKE 'netf%' OR `t0`.`name` NOT LIKE 'hu%')
@@ -0,0 +1,71 @@
1
+ model_name: Patients
2
+ query:
3
+ fields:
4
+ - key_path: id
5
+ display: 'ID #'
6
+ - key_path: first
7
+ display: First Name
8
+ - key_path: patient_payments.amount
9
+ display: Ave Payment
10
+ aggregator: ave
11
+ - key_path: patient_payments.amount
12
+ display: Number of Payments
13
+ aggregator: count
14
+ - key_path: patient_payments.amount
15
+ display: Max Payment
16
+ aggregator: max
17
+ - key_path: patient_payments.amount
18
+ display: Min Payment
19
+ aggregator: min
20
+ - key_path: patient_payments.amount
21
+ display: Total Paid
22
+ aggregator: sum
23
+
24
+ sqlite_readable: |+
25
+ SELECT
26
+ "patients"."id" AS 'ID #',
27
+ "patients"."first" AS 'First Name',
28
+ AVG("patient_payments"."amount") AS 'Ave Payment',
29
+ COUNT("patient_payments"."amount") AS 'Number of Payments',
30
+ MAX("patient_payments"."amount") AS 'Max Payment',
31
+ MIN("patient_payments"."amount") AS 'Min Payment',
32
+ SUM("patient_payments"."amount") AS 'Total Paid'
33
+ FROM "patients" "patients"
34
+ LEFT OUTER JOIN "patient_payments" "patient_payments" ON "patient_payments"."patient_id" = "patients"."id"
35
+ GROUP BY "patients"."id", "patients"."first"
36
+ sqlite_not_readable: |+
37
+ SELECT
38
+ "t0"."id" AS 'c0',
39
+ "t0"."first" AS 'c1',
40
+ AVG("t1"."amount") AS 'c2',
41
+ COUNT("t1"."amount") AS 'c3',
42
+ MAX("t1"."amount") AS 'c4',
43
+ MIN("t1"."amount") AS 'c5',
44
+ SUM("t1"."amount") AS 'c6'
45
+ FROM "patients" "t0"
46
+ LEFT OUTER JOIN "patient_payments" "t1" ON "t1"."patient_id" = "t0"."id"
47
+ GROUP BY "t0"."id", "t0"."first"
48
+ mysql_readable: |+
49
+ SELECT
50
+ `patients`.`id` AS 'ID #',
51
+ `patients`.`first` AS 'First Name',
52
+ AVG(`patient_payments`.`amount`) AS 'Ave Payment',
53
+ COUNT(`patient_payments`.`amount`) AS 'Number of Payments',
54
+ MAX(`patient_payments`.`amount`) AS 'Max Payment',
55
+ MIN(`patient_payments`.`amount`) AS 'Min Payment',
56
+ SUM(`patient_payments`.`amount`) AS 'Total Paid'
57
+ FROM `patients` `patients`
58
+ LEFT OUTER JOIN `patient_payments` `patient_payments` ON `patient_payments`.`patient_id` = `patients`.`id`
59
+ GROUP BY `patients`.`id`, `patients`.`first`
60
+ mysql_not_readable: |+
61
+ SELECT
62
+ `t0`.`id` AS 'c0',
63
+ `t0`.`first` AS 'c1',
64
+ AVG(`t1`.`amount`) AS 'c2',
65
+ COUNT(`t1`.`amount`) AS 'c3',
66
+ MAX(`t1`.`amount`) AS 'c4',
67
+ MIN(`t1`.`amount`) AS 'c5',
68
+ SUM(`t1`.`amount`) AS 'c6'
69
+ FROM `patients` `t0`
70
+ LEFT OUTER JOIN `patient_payments` `t1` ON `t1`.`patient_id` = `t0`.`id`
71
+ GROUP BY `t0`.`id`, `t0`.`first`