to_wa 0.2.0 → 0.3.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: ff02d9d70a9c571bba011ff3f651e559498cd6cd
4
- data.tar.gz: 92bba9fd29b6adfd9035a16e512729bc54b6b092
3
+ metadata.gz: 64fb0e5b6b7555f477aad99eeedfe5d18d083eda
4
+ data.tar.gz: a0647318b3e575773c68193c4c747a67280509fe
5
5
  SHA512:
6
- metadata.gz: 75b7af23c3c6cf5cfb46ebd4533c9bb9bec6705e7b480b059071125bb2993fc3e7b017f1a25e5d2446ce52dd74b5916fe4bbabd3444b7f5e0eb2d2e9b17473ad
7
- data.tar.gz: bea440ef0ff39602c6f265b8046ef359323f876b6b6b12a76beaabe5b8ae055953ca6b7306eca6cb27aa795845a60d6eae404de339bab05979a65e7f70fe8422
6
+ metadata.gz: a1648c143778d42f291f87aa8b2d56b5f9e8001a9fa564ad6e41422c2d15e4643ef8971398d68571e5234874ce9c817e2f25b2b84aa8ce26c9d01b1a93cabd26
7
+ data.tar.gz: d02ec68c2568671103d721ce2fb291660b569ea4fde6bd71f11d08d6495e063811543b0a3275b7e4a339bdef57f95f785606163820796373121258d969a995f1
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- to_wa (0.2.0)
4
+ to_wa (0.3.0)
5
5
  activerecord
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -7,16 +7,38 @@
7
7
  # Installation
8
8
 
9
9
  ```ruby
10
- gem 'ToWa'
10
+ gem 'to_wa'
11
11
  ```
12
12
 
13
13
  ```console
14
14
  bundle install
15
15
  ```
16
16
 
17
+ ## Use all columns and operators
18
+
19
+ ```ruby
20
+ class TestRecord < ActiveRecord::Base
21
+ extend ToWa
22
+
23
+ permit_all_to_wa_operators!
24
+ permit_all_to_wa_columns!
25
+
26
+ # read section: Use specific table columns as left and right
27
+ permit_all_to_wa_specified_columns!
28
+ end
29
+ ```
30
+
31
+ ## Or specify columns and operators
32
+
17
33
  ```ruby
18
34
  class TestRecord < ActiveRecord::Base
19
35
  extend ToWa
36
+
37
+ permit_to_wa_columns :a, :b, :x
38
+ permit_to_wa_operators :eq, :ne
39
+
40
+ # read section: Use specific table columns as left and right
41
+ permit_to_wa_specified_columns foo_records: [:a], bar_records: [:b]
20
42
  end
21
43
  ```
22
44
 
@@ -96,7 +118,6 @@ and|and|
96
118
  or|or|
97
119
  not|not|It must receive Array that includes only one data. `{ "not": [{ "name": "ToWa" }] }`
98
120
 
99
-
100
121
  # Usage
101
122
 
102
123
  (Ofcourse, `ActiveRecord::Relation` will be provided after `to_wa` without `to_sql`.)
@@ -135,6 +156,28 @@ TestRecord.to_wa(
135
156
  #=> "SELECT `test_records`.* FROM `test_records` WHERE ((`test_records`.`name` = 'ToWa' OR `test_records`.`name` = 'to_wa') AND `test_records`.`gender` = 'male')"
136
157
  ```
137
158
 
159
+ ## Use specific table columns as left and right
160
+
161
+ ```ruby
162
+ class TestRecord < ActiveRecord::Base
163
+ extend ToWa
164
+ has_many :users
165
+ end
166
+
167
+ class User < ActiveRecord::Base
168
+ end
169
+
170
+ TestRecord.left_outer_joins(:users).to_wa(
171
+ {
172
+ '>': [
173
+ { 'col': ['users', 'left_arm_length'] },
174
+ { 'col': ['users', 'right_arm_length'] }
175
+ ]
176
+ }
177
+ ).to_sql
178
+ #=> "SELECT `test_records`.* FROM `test_records` LEFT OUTER JOIN `users` ON `users`.`test_record_id` = `test_records`.`id` WHERE (`users`.`left_arm_length` > `users`.`right_arm_length`)"
179
+ ```
180
+
138
181
  ## Working with other query
139
182
 
140
183
  ```ruby
@@ -153,4 +196,4 @@ a.to_sql
153
196
  #=> "`test_records`.`name` = 'ToWa'"
154
197
  TestRecord.where(a).to_sql
155
198
  #=> "SELECT `test_records`.* FROM `test_records` WHERE `test_records`.`name` = 'ToWa'"
156
- ```
199
+ ```
@@ -1,6 +1,12 @@
1
1
  class ToWaTestMigration < ActiveRecord::Migration[5.1]
2
2
  class << self
3
3
  def up
4
+ create_table(:users) do |t|
5
+ t.integer :left_arm_length
6
+ t.integer :right_arm_length
7
+ t.references :test_record
8
+ end
9
+
4
10
  create_table(:test_records) do |t|
5
11
  t.string :a
6
12
  t.string :b
@@ -8,15 +14,23 @@ class ToWaTestMigration < ActiveRecord::Migration[5.1]
8
14
  t.integer :x
9
15
  t.integer :y
10
16
  t.integer :z
17
+ t.string :denied_column
11
18
  end
12
19
  rescue
13
20
  nil
14
21
  end
15
22
 
16
23
  def down
17
- drop_table(:test_records)
18
- rescue
19
- nil
24
+ begin
25
+ drop_table(:users)
26
+ rescue
27
+ nil
28
+ end
29
+ begin
30
+ drop_table(:test_records)
31
+ rescue
32
+ nil
33
+ end
20
34
  end
21
35
  end
22
36
  end
@@ -11,4 +11,5 @@ rescue ActiveRecord::StatementInvalid => e
11
11
  end
12
12
 
13
13
  ActiveRecord::Base.establish_connection(ToWaTestConfiguration::FULL_SET)
14
+ ToWaTestMigration.down
14
15
  ToWaTestMigration.up
@@ -1,21 +1,29 @@
1
1
  require 'to_wa/version'
2
- require 'active_record'
2
+ require 'to_wa/exceptions'
3
+ require 'to_wa/configuration'
4
+ require 'to_wa/easy_hash_access'
5
+ require 'to_wa/builder'
6
+ require 'to_wa/core'
3
7
 
4
8
  module ToWa
5
- require 'to_wa/builder'
6
- require 'to_wa/core'
9
+ include ::ToWa::Configuration
7
10
 
8
- def to_wa(o)
9
- ::ToWa::Core.to_wa(self, o)
10
- end
11
-
12
- def to_wa_raw(arel_table, o)
13
- ToWa(arel_table, o)
11
+ def to_wa(ex)
12
+ where(
13
+ ::ToWa::Builder.new(
14
+ arel_table: arel_table,
15
+ restricted: true,
16
+ permitted_columns: permitted_to_wa_columns,
17
+ permitted_operators: permitted_to_wa_operators,
18
+ permitted_specified_columns: permitted_to_wa_specified_columns,
19
+ ex: ex,
20
+ ).execute!,
21
+ )
14
22
  end
15
23
  end
16
24
 
17
25
  module Kernel
18
- def ToWa(arel_table, o) # rubocop:disable Naming/MethodName
19
- ::ToWa::Core.to_wa_raw(arel_table, o)
26
+ def ToWa(arel_table, ex) # rubocop:disable Naming/MethodName
27
+ ::ToWa::Builder.new(restricted: false, arel_table: arel_table, ex: ex).execute!
20
28
  end
21
29
  end
@@ -1,56 +1,134 @@
1
1
  require 'json'
2
+ require 'active_record'
2
3
 
4
+ # rubocop:disable Metrics/ClassLength
3
5
  module ToWa
4
- module Builder
6
+ class Builder
7
+ using ::ToWa::EasyHashAccess
5
8
  include ActiveRecord::Sanitization
6
9
 
7
- def to_wa(ar, ex)
8
- ar.where(to_wa_raw(ar.arel_table, ex))
10
+ # rubocop:disable Metrics/ParameterLists
11
+ def initialize(
12
+ restricted:,
13
+ ex:,
14
+ arel_table:,
15
+ permitted_columns: Set.new,
16
+ permitted_operators: Set.new,
17
+ permitted_specified_columns: {}
18
+ )
19
+ @restricted = restricted
20
+ @arel_table = arel_table
21
+ @initial_ex = ex.is_a?(String) ? JSON.parse(ex) : ex
22
+ @permitted_columns = permitted_columns
23
+ @permitted_operators = permitted_operators
24
+ @permitted_specified_columns = permitted_specified_columns
9
25
  end
26
+ # rubocop:enable Metrics/ParameterLists
10
27
 
11
- def to_wa_raw(arel_table = nil, ex)
12
- decide(arel_table, ex.is_a?(String) ? JSON.parse(ex) : ex)
28
+ def self.like(v)
29
+ sanitize_sql_like(v)
30
+ end
31
+
32
+ def execute!
33
+ decide(@initial_ex)
34
+ end
35
+
36
+ private
37
+
38
+ def restricted?
39
+ @restricted
40
+ end
41
+
42
+ def permitted_columns?(v)
43
+ return true unless restricted?
44
+ @permitted_columns.include?(v)
45
+ end
46
+
47
+ def permitted_operators?(v)
48
+ return true unless restricted?
49
+ @permitted_operators.include?(v)
13
50
  end
14
51
 
15
- def decide(arel_table, ex)
16
- op = ::ToWa::Core::OPERATORS[ex.keys.first.to_s]
17
- values = ex.values.first
52
+ def decide(ex)
53
+ op = detect_op!(ex.k)
54
+ lr_or_exes = ex.v
18
55
 
19
56
  case op
20
57
  when 'not'
21
- decide(arel_table, values.first).not
58
+ decide(lr_or_exes.first).not
22
59
  when 'and', 'or'
23
- add_logic(arel_table, op, values)
60
+ add_logic(op, lr_or_exes)
61
+ when 'between'
62
+ between_to_gteq_and_lteq(*lr_or_exes)
24
63
  else
25
- add_comparison(arel_table, op, *values)
64
+ add_comparison(op, *lr_or_exes)
26
65
  end
27
66
  end
28
67
 
29
- def add_comparison(arel_table, op, table, value)
30
- normalized =
31
- case op
32
- when 'between'
33
- value.first..value.second
34
- when 'matches'
35
- "%#{ToWa::Builder.like(value)}%"
36
- else
37
- value
38
- end
68
+ def detect_op!(op)
69
+ sop = op.to_s
70
+ raise ::ToWa::DeniedOperator, sop unless permitted_operators?(sop)
71
+ ::ToWa::Core::OPERATORS[sop]
72
+ end
39
73
 
40
- arel_table[table].send(op, normalized)
74
+ def add_comparison(op, left, right)
75
+ normalize_value(left) { normalize_left_table(left) }.send(op, normalize_right(op, right))
41
76
  end
42
77
 
43
- def add_logic(arel_table, op, exes)
44
- logics =
45
- exes.inject(nil) do |a, ex|
46
- a.nil? ? decide(arel_table, ex) : a.send(op, decide(arel_table, ex))
47
- end
78
+ def between_to_gteq_and_lteq(left, right)
79
+ decide({
80
+ 'and' => [
81
+ { '>=' => [left, right.first] },
82
+ { '<=' => [left, right.second] },
83
+ ],
84
+ })
85
+ end
48
86
 
49
- (exes.size == 1) ? logics : Arel::Nodes::Grouping.new(logics)
87
+ def normalize_left_table(left)
88
+ raise ::ToWa::DeniedColumn, left unless permitted_columns?(left)
89
+ @arel_table[left]
50
90
  end
51
91
 
52
- def self.like(v)
53
- sanitize_sql_like(v)
92
+ def normalize_value(o, &block)
93
+ case
94
+ when o.is_a?(Hash) && o.k.to_s == 'col'
95
+ specify_columns(*o.v.map(&:to_s))
96
+ when block_given?
97
+ block&.call
98
+ else
99
+ o
100
+ end
101
+ end
102
+
103
+ def all_specified_columns_allowed?
104
+ @permitted_specified_columns == ::ToWa::AllSpecifiedColumnsAllowance
105
+ end
106
+
107
+ def permitted_specified_columns?(table, column)
108
+ return true unless restricted?
109
+ return true if all_specified_columns_allowed?
110
+
111
+ @permitted_specified_columns[table]&.include?(column)
112
+ end
113
+
114
+ def specify_columns(table, column)
115
+ raise ::ToWa::DeniedColumn, [table, column] unless permitted_specified_columns?(table, column)
116
+
117
+ Arel::Table.new(table)[column]
118
+ end
119
+
120
+ def normalize_right(op, right)
121
+ if op == 'matches'
122
+ "%#{ToWa::Builder.like(right)}%"
123
+ else
124
+ normalize_value(right)
125
+ end
126
+ end
127
+
128
+ def add_logic(op, exes)
129
+ logic = exes[1..-1].inject(decide(exes.first)) { |a, ex| a.send(op, decide(ex)) }
130
+ Arel::Nodes::Grouping.new(logic)
54
131
  end
55
132
  end
56
133
  end
134
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,45 @@
1
+ require 'json'
2
+
3
+ module ToWa
4
+ module Configuration
5
+ def permit_to_wa_columns(*columns)
6
+ @permitted_to_wa_columns = Set.new(columns.map(&:to_s))
7
+ end
8
+
9
+ def permit_all_to_wa_columns!
10
+ @permitted_to_wa_columns = Set.new(self.column_names)
11
+ end
12
+
13
+ def permit_to_wa_specified_columns(hash)
14
+ @permitted_to_wa_specified_columns =
15
+ JSON.parse(hash.to_json).inject({}) { |a, (k, v)| a.merge!(k => Set.new(Array(v))) }
16
+ end
17
+
18
+ def permit_all_to_wa_specified_columns!
19
+ @permitted_to_wa_specified_columns = ::ToWa::AllSpecifiedColumnsAllowance
20
+ end
21
+
22
+ def permit_to_wa_operators(*operators)
23
+ @permitted_to_wa_operators = Set.new(operators.map(&:to_s))
24
+ end
25
+
26
+ def permit_all_to_wa_operators!
27
+ @permitted_to_wa_operators = ToWa::Core::OPERATORS
28
+ end
29
+
30
+ def permitted_to_wa_columns
31
+ @permitted_to_wa_columns ||= Set.new
32
+ end
33
+
34
+ def permitted_to_wa_specified_columns
35
+ @permitted_to_wa_specified_columns ||= {}
36
+ end
37
+
38
+ def permitted_to_wa_operators
39
+ @permitted_to_wa_operators ||= Set.new
40
+ end
41
+ end
42
+
43
+ module AllSpecifiedColumnsAllowance
44
+ end
45
+ end
@@ -1,7 +1,5 @@
1
1
  module ToWa
2
2
  module Core
3
- extend ::ToWa::Builder
4
-
5
3
  COMPARISON = {
6
4
  '==' => 'eq',
7
5
  '=' => 'eq',
@@ -0,0 +1,13 @@
1
+ module ToWa
2
+ module EasyHashAccess
3
+ refine Hash do
4
+ def k
5
+ keys.first
6
+ end
7
+
8
+ def v
9
+ values.first
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module ToWa
2
+ class DeniedOperator < StandardError
3
+ end
4
+
5
+ class DeniedColumn < StandardError
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module ToWa
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: to_wa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mmmpa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-20 00:00:00.000000000 Z
11
+ date: 2018-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -131,7 +131,10 @@ files:
131
131
  - db/preparation.rb
132
132
  - lib/to_wa.rb
133
133
  - lib/to_wa/builder.rb
134
+ - lib/to_wa/configuration.rb
134
135
  - lib/to_wa/core.rb
136
+ - lib/to_wa/easy_hash_access.rb
137
+ - lib/to_wa/exceptions.rb
135
138
  - lib/to_wa/version.rb
136
139
  - to_wa.gemspec
137
140
  homepage: http://mmmpa.net