sobjectmodel 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 593a275d93ff933f9e7d9be260194f2da06f4b55285424589b739db5b80e4b8d
4
+ data.tar.gz: d8723a40b412a472ba84d56658046cde7b320ed617e22c92264d93b8649d5aaa
5
+ SHA512:
6
+ metadata.gz: 6dda884fe46731a0c8e25b372792908b3e4e43eb1d1a7693e97658cba06eeb6dadbff42ceedc7e04525072fe67b7027de372172ce637e567547b90b879bc799b
7
+ data.tar.gz: 0cb40fb055300eaef7f198878c12f03652a7d79c70a0bef09ddb9d4b9ac949a92e231912be6ac0ee18d0142e446bbc8a713266d6ef9eaf817af4d2fdaf40815c
@@ -0,0 +1,34 @@
1
+ module SObjectModel
2
+ module Adapter
3
+ class Base
4
+ def exec_query(soql, model_class: nil)
5
+ raise 'this method is not implemented'
6
+ end
7
+
8
+ def find(object_type, id, klass)
9
+ raise 'this method is not implemented'
10
+ end
11
+
12
+ def create(object_type, values, klass = nil)
13
+ raise 'this method is not implemented'
14
+ end
15
+
16
+ def update(object_type, id, values)
17
+ raise 'this method is not implemented'
18
+ end
19
+
20
+ def delete(object_type, id)
21
+ raise 'this method is not implemented'
22
+ end
23
+
24
+ def query(soql, klass)
25
+ raise 'this method is not implemented'
26
+ end
27
+
28
+ def describe(object_type)
29
+ raise 'this method is not implemented'
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,53 @@
1
+ require_relative 'base'
2
+ require_relative '../rest/client'
3
+
4
+ module SObjectModel
5
+ module Adapter
6
+ class Rest < Base
7
+ def initialize(rest_client)
8
+ @client = rest_client
9
+ end
10
+
11
+ def exec_query(soql, model_class: nil)
12
+ result = client.query(soql)
13
+ result.to_records(model_class: model_class)
14
+ end
15
+
16
+ def describe(object_type)
17
+ client.describe(object_type)
18
+ end
19
+
20
+ def find(object_type, id, klass)
21
+ attributes = client.find(object_type, id)
22
+ klass.new(**attributes)
23
+ rescue ::SObjectModel::Rest::RecordNotFoundError
24
+ nil
25
+ end
26
+
27
+ def create(object_type, values, klass = nil)
28
+ id = client.create(object_type, values)
29
+ return id if klass.nil?
30
+
31
+ find(object_type, id, klass)
32
+ end
33
+
34
+ def update(object_type, id, values)
35
+ client.update(object_type, id, values)
36
+ end
37
+
38
+ def delete(object_type, id)
39
+ client.delete(object_type, id)
40
+ end
41
+
42
+ def query(soql, klass)
43
+ exec_query(soql, model_class: klass)
44
+ end
45
+
46
+ private
47
+
48
+ def client
49
+ @client
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'base'
2
+
3
+ module SObjectModel
4
+ module Adapter
5
+ class Sf < Base
6
+ attr_reader :target_org
7
+
8
+ def initialize(sf_main, target_org:)
9
+ @sf = sf_main
10
+ @target_org = target_org
11
+ end
12
+
13
+ def exec_query(soql, format: nil, bulk: false, wait: nil, model_class: nil)
14
+ sf.data.query(soql, target_org: target_org, format: format, bulk: bulk, wait: wait, model_class: model_class)
15
+ end
16
+
17
+ def find(object_type, id, klass)
18
+ sf.data.get_record object_type, record_id: id, target_org: target_org, model_class: klass
19
+ end
20
+
21
+ def create(object_type, values, klass = nil)
22
+ id = sf.data.create_record object_type, values: values, target_org: target_org
23
+ return id if klass.nil?
24
+
25
+ find(object_type, id, klass)
26
+ end
27
+
28
+ def update(object_type, id, values)
29
+ sf.data.update_record object_type, record_id: id, where: nil, values: values, target_org: target_org
30
+ end
31
+
32
+ def delete(object_type, id)
33
+ sf.data.delete_record object_type, record_id: id, where: nil, target_org: target_org
34
+ end
35
+
36
+ def query(soql, klass, format = nil)
37
+ sf.data.query soql, target_org: target_org, format: format, model_class: klass
38
+ end
39
+
40
+ def describe(object_type)
41
+ schema = sf.sobject.describe(object_type, target_org: target_org)
42
+ schema.to_h # convert to raw command response
43
+ end
44
+
45
+ private
46
+
47
+ def sf
48
+ @sf
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,57 @@
1
+ module SObjectModel
2
+ module BaseMethods
3
+ def self.included(c)
4
+ c.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def connection
9
+ @connection
10
+ end
11
+
12
+ def connection=(conn)
13
+ @connection = conn
14
+ end
15
+
16
+ def describe
17
+ Schema.new(connection.describe(name.to_sym))
18
+ end
19
+ end
20
+
21
+ def initialize(attributes = {})
22
+ @original_attributes = {}
23
+ @current_attributes = {}
24
+ @updated_attributes = {}
25
+
26
+ attributes.each do |k, v|
27
+ field_name = k.to_sym
28
+ if self.class.field_names.include?(field_name)
29
+ @original_attributes[field_name] = v
30
+ __send__ (field_name.to_s + '='), v
31
+ elsif self.class.parent_relations.find{|r| r[:name] == field_name}
32
+ __send__ (field_name.to_s + '='), v
33
+ elsif self.class.child_relations.find{|r| r[:name] == field_name}
34
+ __send__ (field_name.to_s + '='), (v.nil? ? [] : v)
35
+ end
36
+ end
37
+ end
38
+
39
+ def to_h(keys: nil)
40
+ self.class.field_names.each_with_object({}) do |name, hash|
41
+ if keys&.instance_of?(Array)
42
+ hash[name] = __send__(name) if keys.include?(name)
43
+ else
44
+ hash[name] = __send__(name)
45
+ end
46
+ end
47
+ end
48
+
49
+ def new_record?
50
+ self.Id.nil?
51
+ end
52
+
53
+ def persisted?
54
+ new_record? == false
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,100 @@
1
+ require_relative './schema'
2
+ require_relative './base_methods'
3
+ require_relative './dml_methods'
4
+ require_relative './query_methods'
5
+
6
+ module SObjectModel
7
+ class ClassDefinition
8
+ attr_reader :schema
9
+
10
+ def initialize(schema)
11
+ @schema = Schema.new(schema)
12
+ end
13
+
14
+ def to_s
15
+ <<~Klass
16
+ Class.new do
17
+ include ::SObjectModel::BaseMethods
18
+ include ::SObjectModel::DmlMethods
19
+ include ::SObjectModel::QueryMethods
20
+
21
+ attr_reader :original_attributes, :current_attributes, :updated_attributes
22
+
23
+ #{ class_methods }
24
+
25
+ #{ field_attribute_methods }
26
+ #{ parent_relation_methods }
27
+ #{ child_relation_methods }
28
+ end
29
+ Klass
30
+ end
31
+
32
+ def class_methods
33
+ <<~EOS
34
+ class << self
35
+ def field_names
36
+ @field_names ||= #{ schema.field_names }
37
+ end
38
+
39
+ def parent_relations
40
+ @parent_relations ||= #{ schema.parent_relations }
41
+ end
42
+
43
+ def child_relations
44
+ @child_relations ||= #{ schema.child_relations }
45
+ end
46
+ end
47
+ EOS
48
+ end
49
+
50
+ def field_attribute_methods
51
+ schema.field_names.each_with_object('') do |name, s|
52
+ s << <<~EOS
53
+ def #{name}
54
+ @#{name}
55
+ end
56
+
57
+ def #{name}=(value)
58
+ @#{name} = value
59
+ return if %i[Id LastModifiedDate IsDeleted SystemModstamp CreatedById CreatedDate LastModifiedById].include?(:#{name})
60
+
61
+ current_attributes[:#{name}] = value
62
+ if current_attributes[:#{name}] == original_attributes[:#{name}]
63
+ updated_attributes[:#{name}] = nil
64
+ else
65
+ updated_attributes[:#{name}] = (value.nil? ? :null : value)
66
+ end
67
+ end
68
+ EOS
69
+ end
70
+ end
71
+
72
+ def parent_relation_methods
73
+ schema.parent_relations.each_with_object('') do |r, s|
74
+ s << <<~EOS
75
+ def #{r[:name]}
76
+ @#{r[:name]}
77
+ end
78
+
79
+ def #{r[:name]}=(attributes)
80
+ @#{r[:name]} = attributes.nil? ? nil : #{r[:class_name]}.new(attributes)
81
+ end
82
+ EOS
83
+ end
84
+ end
85
+
86
+ def child_relation_methods
87
+ schema.child_relations.each_with_object('') do |r, s|
88
+ s << <<~EOS
89
+ def #{r[:name]}
90
+ @#{r[:name]}
91
+ end
92
+
93
+ def #{r[:name]}=(records)
94
+ @#{r[:name]} = records.map{|attributes| #{r[:class_name]}.new(attributes)}
95
+ end
96
+ EOS
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,37 @@
1
+ module SObjectModel
2
+ module DmlMethods
3
+ def self.included(c)
4
+ c.extend ClassMethods
5
+ end
6
+
7
+ def save
8
+ if new_record?
9
+ self.Id = self.class.connection.create(self.class.name.to_sym, current_attributes.reject{|_,v| v.nil?})
10
+ else
11
+ _updated_attributes =
12
+ updated_attributes
13
+ .reject{|_,v| v.nil?}
14
+ .each_with_object({}){ |(k,v),h| h[k] = (v == :null) ? nil : v }
15
+
16
+ self.class.connection.update(self.class.name.to_sym, self.Id, _updated_attributes)
17
+ end
18
+
19
+ @original_attributes = current_attributes.dup
20
+ @updated_attributes = {}
21
+
22
+ self.Id
23
+ end
24
+
25
+ def delete
26
+ return if self.Id.nil?
27
+
28
+ self.class.connection.delete(self.class.name.to_sym, self.Id)
29
+ end
30
+
31
+ module ClassMethods
32
+ def create(values = {})
33
+ connection.create(name.to_sym, values, Object.const_get(name.to_sym))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ require_relative './class_definition'
2
+
3
+ module SObjectModel
4
+ class Generator
5
+ attr_reader :connection
6
+
7
+ def initialize(connection)
8
+ @connection = connection
9
+ end
10
+
11
+ def connected?
12
+ connection.nil? == false
13
+ end
14
+
15
+ def generate(*object_types)
16
+ generated_types = []
17
+ object_types.each do |object_type|
18
+ next if generated? object_type
19
+
20
+ schema = describe(object_type)
21
+ class_definition = ClassDefinition.new(schema)
22
+
23
+ instance_eval "::#{object_type} = #{class_definition}"
24
+ klass = Object.const_get object_type.to_sym
25
+ klass.connection = connection
26
+ generated_types << object_type
27
+ end
28
+
29
+ generated_types
30
+ end
31
+
32
+ private
33
+
34
+ def describe(object_type)
35
+ connection.describe object_type
36
+ end
37
+
38
+ def generated?(object_type)
39
+ Object.const_get object_type.to_sym
40
+ true
41
+ rescue NameError
42
+ false
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,189 @@
1
+ require 'date'
2
+
3
+ module SObjectModel
4
+ module QueryMethods
5
+ class QueryCondition
6
+ attr_reader :connection,
7
+ :object_name,
8
+ :all_field_names,
9
+ :fields,
10
+ :conditions,
11
+ :limit_num,
12
+ :row_order,
13
+ :count_select,
14
+ :max_select_field,
15
+ :min_select_field
16
+
17
+ def initialize(connection, object_name, field_names)
18
+ @object_name = object_name
19
+ @all_field_names = field_names
20
+ @connection = connection
21
+ @fields = []
22
+ @conditions = []
23
+ @limit_num = nil
24
+ @row_order = nil
25
+ @count_select = false
26
+ @max_select_field = nil
27
+ @min_select_field = nil
28
+ end
29
+
30
+ def where(*expr)
31
+ return self unless valid_expr?(expr)
32
+
33
+ conditions.append to_string_expr(expr)
34
+ self
35
+ end
36
+
37
+ def not(*expr)
38
+ return self unless valid_expr?(expr)
39
+
40
+ conditions.append %|(NOT(#{to_string_expr(expr)}))|
41
+ self
42
+ end
43
+
44
+ def select(*expr)
45
+ return self if expr&.empty?
46
+
47
+ if expr.size > 1
48
+ @fields = self.fields + expr
49
+ else
50
+ self.fields << expr.first
51
+ end
52
+ return self
53
+ end
54
+
55
+ def limit(num)
56
+ @limit_num = num
57
+ return self
58
+ end
59
+
60
+ def order(*_fields)
61
+ return self if _fields&.empty?
62
+
63
+ @row_order = _fields
64
+ return self
65
+ end
66
+
67
+ def to_soql
68
+ base = 'SELECT %{select} FROM %{object}' % {select: select_fields, object: object_name}
69
+ where = conditions.size.zero? ? nil : 'WHERE %{where}' % {where: conditions.flatten.join(' AND ')}
70
+ _order = row_order.nil? ? nil : 'ORDER BY %{order}' % {order: row_order.join(', ')}
71
+ limit = limit_num.nil? ? nil : 'LIMIT %{limit}' % {limit: limit_num}
72
+
73
+ [base, where, _order, limit].compact.join(' ')
74
+ end
75
+
76
+ def all
77
+ connection.query(to_soql, Object.const_get(object_name.to_sym))
78
+ end
79
+
80
+ def pluck(field_name)
81
+ connection.query(to_soql, nil).map{|record| record[field_name.to_s]}
82
+ end
83
+
84
+ def take
85
+ limit(1).all.first
86
+ end
87
+
88
+ def count
89
+ @count_select = true
90
+ connection.query(to_soql, nil).first['expr0']
91
+ end
92
+
93
+ def max(field_name)
94
+ @max_select_field = field_name
95
+ connection.query(to_soql, nil).first['expr0']
96
+ end
97
+
98
+ def min(field_name)
99
+ @min_select_field = field_name
100
+ connection.query(to_soql, nil).first['expr0']
101
+ end
102
+
103
+ private
104
+
105
+ def select_fields
106
+ return 'COUNT(Id)' if count_select
107
+ return "MAX(#{max_select_field})" if max_select_field
108
+ return "MIN(#{min_select_field})" if min_select_field
109
+
110
+ (fields.empty? ? all_field_names : fields.push(:Id).uniq).join(', ')
111
+ end
112
+
113
+ def to_string_expr(expr)
114
+ return str_by_ternary_expr(expr) if expr.size > 1
115
+ return expr[0] if expr[0].instance_of? String
116
+
117
+ strs_by_hash_expr(expr)
118
+ end
119
+
120
+ def str_by_ternary_expr(expr)
121
+ return self if expr.size < 3
122
+
123
+ value = case expr[2].class.name.to_sym
124
+ when :String
125
+ %|'#{expr[2]}'|
126
+ when :Time
127
+ expr[2].to_datetime
128
+ when :NilClass
129
+ :null
130
+ when :Array
131
+ candidates = expr[2].map do |o|
132
+ case o.class.name.to_sym
133
+ when :String
134
+ %|'#{o}'|
135
+ when :Time
136
+ o.to_datetime
137
+ when :NilClass
138
+ :null
139
+ else
140
+ o
141
+ end
142
+ end
143
+ %|(#{candidates.join(', ')})|
144
+ else
145
+ expr[2]
146
+ end
147
+ %|#{expr[0]} #{expr[1]} #{value}|
148
+ end
149
+
150
+ def valid_expr?(expr)
151
+ return false if expr&.empty?
152
+ return false if expr.map{|o| (o == '' || o == {} || o == []) ? nil : o}.compact.size.zero?
153
+ return false unless [Hash, Symbol, String].any?{|klass| expr.first.instance_of? klass}
154
+
155
+ true
156
+ end
157
+
158
+ def strs_by_hash_expr(expr)
159
+ expr[0].map do |k,v|
160
+ case v.class.name.to_sym
161
+ when :String
162
+ %|#{k} = '#{v}'|
163
+ when :Time
164
+ %|#{k} = #{v.to_datetime}|
165
+ when :NilClass
166
+ %|#{k} = null|
167
+ when :Array
168
+ candidates = v.map do |o|
169
+ case o.class.name.to_sym
170
+ when :String
171
+ %|'#{o}'|
172
+ when :Time
173
+ %|#{o.to_datetime}|
174
+ when :NilClass
175
+ :null
176
+ else
177
+ o
178
+ end
179
+ end
180
+ %|#{k} IN (#{candidates.join(', ')})|
181
+ else
182
+ "#{k} = #{v}"
183
+ end
184
+ end
185
+ .join(' AND ')
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,78 @@
1
+ require_relative './query_condition'
2
+
3
+ module SObjectModel
4
+ module QueryMethods
5
+ def self.included(c)
6
+ c.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def where(*expr)
11
+ qc = QueryCondition.new(connection, self.name, self.field_names)
12
+ qc.where(*expr)
13
+ return qc
14
+ end
15
+
16
+ def select(*fields)
17
+ qc = QueryCondition.new(connection, self.name, self.field_names)
18
+ qc.select(*fields)
19
+ return qc
20
+ end
21
+
22
+ def limit(num)
23
+ qc = QueryCondition.new(connection, self.name, self.field_names)
24
+ qc.limit(num)
25
+ qc
26
+ end
27
+
28
+ def order(*field_names)
29
+ qc = QueryCondition.new(connection, self.name, self.field_names)
30
+ qc.order(*field_names)
31
+ qc
32
+ end
33
+ def find(id)
34
+ connection.find(name.to_sym, id, Object.const_get(name.to_sym))
35
+ end
36
+
37
+ def find_by(*find_condition)
38
+ qc = QueryCondition.new(connection, self.name, self.field_names)
39
+ qc.where(*find_condition).take
40
+ end
41
+
42
+ def all
43
+ qc = QueryCondition.new(connection, self.name, self.field_names)
44
+ qc.all
45
+ end
46
+
47
+ def to_csv
48
+ qc = QueryCondition.new(connection, self.name, self.field_names)
49
+ qc.to_csv
50
+ end
51
+
52
+ def pluck(field_name)
53
+ qc = QueryCondition.new(connection, self.name, self.field_names)
54
+ qc.pluck(field_name)
55
+ end
56
+
57
+ def take
58
+ qc = QueryCondition.new(connection, self.name, self.field_names)
59
+ qc.take
60
+ end
61
+
62
+ def count
63
+ qc = QueryCondition.new(connection, self.name, self.field_names)
64
+ qc.count
65
+ end
66
+
67
+ def max(field_name)
68
+ qc = QueryCondition.new(connection, self.name, self.field_names)
69
+ qc.max(field_name)
70
+ end
71
+
72
+ def min(field_name)
73
+ qc = QueryCondition.new(connection, self.name, self.field_names)
74
+ qc.min(field_name)
75
+ end
76
+ end
77
+ end
78
+ end