sf_cli 0.0.3 → 0.0.5

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.rdoc +30 -8
  4. data/lib/sf_cli/sf/core/base.rb +80 -0
  5. data/lib/sf_cli/sf/data/bulk_result_v2.rb +77 -0
  6. data/lib/sf_cli/sf/data/core.rb +39 -0
  7. data/lib/sf_cli/sf/data/create_record.rb +30 -0
  8. data/lib/sf_cli/sf/data/delete_bulk.rb +47 -0
  9. data/lib/sf_cli/sf/data/delete_record.rb +33 -0
  10. data/lib/sf_cli/sf/data/delete_resume.rb +47 -0
  11. data/lib/sf_cli/sf/data/get_record.rb +42 -0
  12. data/lib/sf_cli/sf/data/helper_methods.rb +47 -0
  13. data/lib/sf_cli/sf/data/query.rb +120 -0
  14. data/lib/sf_cli/sf/data/query_helper.rb +57 -0
  15. data/lib/sf_cli/sf/data/resume.rb +87 -0
  16. data/lib/sf_cli/sf/data/search.rb +50 -0
  17. data/lib/sf_cli/sf/data/update_record.rb +38 -0
  18. data/lib/sf_cli/sf/data/upsert_bulk.rb +50 -0
  19. data/lib/sf_cli/sf/data/upsert_resume.rb +47 -0
  20. data/lib/sf_cli/sf/main.rb +31 -0
  21. data/lib/sf_cli/sf/model/class_definition.rb +112 -0
  22. data/lib/sf_cli/sf/model/generator.rb +27 -0
  23. data/lib/sf_cli/sf/model/schema.rb +33 -0
  24. data/lib/sf_cli/sf/model.rb +21 -0
  25. data/lib/sf_cli/sf/org/core.rb +23 -0
  26. data/lib/sf_cli/sf/org/display.rb +41 -0
  27. data/lib/sf_cli/sf/org/list.rb +67 -0
  28. data/lib/sf_cli/sf/org/login.rb +64 -0
  29. data/lib/sf_cli/sf/project/core.rb +83 -0
  30. data/lib/sf_cli/sf/sobject/core.rb +50 -0
  31. data/lib/sf_cli.rb +19 -1
  32. metadata +38 -15
  33. data/lib/sf_cli/sf/base.rb +0 -34
  34. data/lib/sf_cli/sf/data.rb +0 -148
  35. data/lib/sf_cli/sf/org.rb +0 -49
  36. data/lib/sf_cli/sf/project.rb +0 -68
  37. data/lib/sf_cli/sf/sobject.rb +0 -41
  38. data/lib/sf_cli/sf.rb +0 -87
@@ -0,0 +1,120 @@
1
+ require_relative './helper_methods'
2
+ require_relative './query_helper'
3
+
4
+ module SfCli::Sf::Data
5
+ module Query
6
+ # get object records using SOQL.
7
+ #
8
+ # *soql* --- SOQL<br>
9
+ #
10
+ # *target_org* --- an alias of paticular org, or username can be used<br>
11
+ #
12
+ # *format* --- get the command's raw output. human, csv, json can be available<br>
13
+ #
14
+ # *model_class* --- the object model class<br>
15
+ #
16
+ # *bulk* --- use Bulk API<br>
17
+ #
18
+ # *timeout* --- max minutes to wait for the job complete in Bulk API mode<br>
19
+ #
20
+ # ======
21
+ # sf.data.query 'SELECT Id, Name FROM Account LIMIT 1' # => [{Id: "abc", Name: "account name"}]
22
+ #
23
+ # Account = Struct.new(:Id, :Name)
24
+ # sf.data.query('SELECT Id, Name From Account Limit 3', model_class: Account) # returns an array of Account struct object
25
+ #
26
+ # # child-parent relationship is supported
27
+ # sf.data.query 'SELECT Id, Name, Account.Name From Contact Limit 1' # [{Id: "abc", Name: "contact name", Account: {Name: "account name"}}]
28
+ #
29
+ # # parent-children relationship is supported
30
+ # sf.data.query 'SELECT Id, Name, (SELECT Name From Contacts) FROM Account Limit 1' # [{Id: "abc", Name: "account name", Contacts: [{Name: "contact name"}]}]
31
+ #
32
+ # When using Bulk API, you get the records when the query job completes within time limit.
33
+ # The method returns a tapple(an array), which changes its contents according to the job result.
34
+ #
35
+ # ======
36
+ # done, result = sf.data.query 'SELECT Id, Name FROM Account', bulk: true # max wait 1 min to get result (default)
37
+ # done, result = sf.data.query 'SELECT Id, Name FROM Account', bulk: true, timeout: 5 # max wait 5 min to get result
38
+ # done, result = sf.data.query 'SELECT Id, Name FROM Account', bulk: true, timeout: 0 # returns immediately
39
+ #
40
+ # rows = result if done # if job is completed, the result is an array of record
41
+ # job_id = result unless done # if job hasn't completed or it has aborted, the result is the job ID
42
+ #
43
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_query_unified]
44
+ #
45
+ # About querying with auto generated object model, see the section {"Object Model support"}[link://files/README_rdoc.html#label-Object+Model+support+-28experimental-2C+since+0.0.4-29] in README.
46
+ #
47
+ def query(soql, target_org: nil, format: nil, bulk: false, timeout: nil, model_class: nil)
48
+ flags = {
49
+ :"query" => %("#{soql}"),
50
+ :"target-org" => target_org,
51
+ :"result-format" => format,
52
+ :"wait" => (bulk ? timeout : nil),
53
+ }
54
+ switches = {
55
+ bulk: bulk,
56
+ }
57
+ raw_output = format ? true : false
58
+ format = format&.to_sym || :json
59
+
60
+ result = exec(__method__, flags: flags, switches: switches, redirection: :null_stderr, raw_output: raw_output, format: format)
61
+
62
+ return_result(result, raw_output, bulk, model_class)
63
+ end
64
+
65
+ # resume a bulk query job, which you previously started, and get records
66
+ #
67
+ # *job_id* --- job ID you want to resume<br>
68
+ #
69
+ # *target_org* --- an alias of paticular org, or username can be used<br>
70
+ #
71
+ # *format* --- get the command's raw output. human, csv, json can be available<br>
72
+ #
73
+ # *model_class* --- the object model class<br>
74
+ #
75
+ # ======
76
+ # # start a query job
77
+ # result1 = sf.data.query 'SELECT Id, Name FROM Account', bulk: true, timeout: 0 # returns immediately
78
+ #
79
+ # result1 # => [false, "<job id>"]
80
+ #
81
+ # # the job has already started asynchronously.
82
+ # # So you should check and get the result.
83
+ # done, result2 = sf.data.query_resume job_id: result1.last
84
+ #
85
+ # puts 'get the records!' if done # the job has completed
86
+ #
87
+ # result2 # => if done is true, this is an array of record. Otherwise it should be the Job ID.
88
+ #
89
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_query_resume_unified]
90
+ def query_resume(job_id:, target_org: nil, format: nil, model_class: nil)
91
+ flags = {
92
+ :"bulk-query-id" => job_id,
93
+ :"target-org" => target_org,
94
+ :"result-format" => format,
95
+ }
96
+ raw_output = format ? true : false
97
+ format = format || :json
98
+
99
+ action = __method__.to_s.tr('_', ' ')
100
+ result = exec(action, flags: flags, redirection: :null_stderr, raw_output: raw_output, format: format)
101
+
102
+ return_result(result, raw_output, true, model_class)
103
+ end
104
+
105
+ def return_result(result, raw_output, bulk, model_class)
106
+ result_adjuster =
107
+ if raw_output
108
+ RawOutputResultAdjuster.new(result)
109
+ elsif bulk
110
+ BulkResultAdjuster.new(result, model_class)
111
+ else
112
+ RegularResultAdjuster.new(result, model_class)
113
+ end
114
+
115
+ result_adjuster.get_return_value
116
+ end
117
+
118
+ private :return_result
119
+ end
120
+ end
@@ -0,0 +1,57 @@
1
+ require_relative './helper_methods'
2
+
3
+ module SfCli::Sf::Data
4
+ module Query
5
+ class RegularResultAdjuster # :nodoc: all
6
+ include ::SfCli::Sf::Data::HelperMethods
7
+
8
+ attr_reader :result, :model_class
9
+
10
+ def initialize(result, model_class)
11
+ @result = result
12
+ @model_class = model_class
13
+ end
14
+
15
+ def get_return_value
16
+ result['result']['records'].each_with_object([]) do |h, a|
17
+ record = prepare_record(h)
18
+ a << (model_class ? model_class.new(**record) : record)
19
+ end
20
+ end
21
+ end
22
+
23
+ class BulkResultAdjuster # :nodoc: all
24
+ include ::SfCli::Sf::Data::HelperMethods
25
+
26
+ attr_reader :result, :model_class
27
+
28
+ def initialize(result, model_class)
29
+ @result = result
30
+ @model_class = model_class
31
+ end
32
+
33
+ def get_return_value
34
+ done = result['result']['done']
35
+ id = result['result']['id']
36
+ rows = result['result']['records'].each_with_object([]) do |h, a|
37
+ record = prepare_record_in_bulk_mode(h)
38
+ a << (model_class ? model_class.new(**record) : record)
39
+ end
40
+
41
+ done ? [done, rows] : [done, id]
42
+ end
43
+ end
44
+
45
+ class RawOutputResultAdjuster # :nodoc: all
46
+ attr_reader :result
47
+
48
+ def initialize(result)
49
+ @result = result
50
+ end
51
+
52
+ def get_return_value
53
+ result
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,87 @@
1
+ module SfCli::Sf::Data
2
+ module Resume
3
+ JobInfo = Struct.new(
4
+ :id,
5
+ :operation,
6
+ :object,
7
+ :createdById,
8
+ :createdDate,
9
+ :systemModstamp,
10
+ :state,
11
+ :concurrencyMode,
12
+ :contentType,
13
+ :numberBatchesQueued,
14
+ :numberBatchesInProgress,
15
+ :numberBatchesCompleted,
16
+ :numberBatchesFailed,
17
+ :numberBatchesTotal,
18
+ :numberRecordsProcessed,
19
+ :numberRetries,
20
+ :apiVersion,
21
+ :numberRecordsFailed,
22
+ :totalProcessingTime,
23
+ :apiActiveProcessingTime,
24
+ :apexProcessingTime
25
+ ) do
26
+ def opened?
27
+ state == 'Open'
28
+ end
29
+
30
+ def upload_completed?
31
+ state == 'UploadComplete'
32
+ end
33
+
34
+ def in_progress?
35
+ state == 'InProgress'
36
+ end
37
+
38
+ def completed?
39
+ state == 'JobComplete'
40
+ end
41
+
42
+ def failed?
43
+ state == 'Failed'
44
+ end
45
+
46
+ def aborted?
47
+ state == 'Aborted'
48
+ end
49
+ end
50
+
51
+ # View the status of a bulk job.
52
+ #
53
+ # *job_id* --- job ID you want to resume<br>
54
+ #
55
+ # *target_org* --- an alias of paticular org, or username can be used<br>
56
+ #
57
+ # ======
58
+ # # start a delete job
59
+ # jobinfo = sf.data.delete_bulk sobject: :TestCustomObject__c, file: 'delete.csv' # this returns immediately
60
+ # jobinfo.id # => "750J4000003g1OaIAI" it's job ID
61
+ #
62
+ # jobinfo2 = sf.data.resume job_id: jobinfo.id
63
+ #
64
+ # puts 'yey!' if job_info2.completed? # the job has completed
65
+ #
66
+ # The job info object has methods to check the job status:
67
+ # - opened?
68
+ # - upload_completed?
69
+ # - in_progress?
70
+ # - completed?
71
+ #
72
+ # To know job status more, take a look at {the bulk API developer guide}[https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/bulk_api_2_job_states.htm]
73
+ #
74
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_resume_unified]
75
+ #
76
+ def resume(job_id:, target_org: nil)
77
+ flags = {
78
+ :"job-id" => job_id,
79
+ :"target-org" => target_org,
80
+ }
81
+ json = exec(__method__, flags: flags, redirection: :null_stderr)
82
+
83
+ json['result'].delete '$'
84
+ JobInfo.new(**json['result'])
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,50 @@
1
+ module SfCli::Sf::Data
2
+ module Search
3
+ # search objects using SOSL.
4
+ #
5
+ # *sosl* --- SOSL<br>
6
+ #
7
+ # *target_org* --- an alias of paticular org, or username can be used<br>
8
+ #
9
+ # *format* --- get the command's raw output. human, csv, json can be available. *NOTE*: if you choose csv, csv files are downloaded in current directory<br>
10
+ #
11
+ # ======
12
+ # # example (in irb):
13
+ #
14
+ # > sf.data.search "FIND {TIM OR YOUNG OR OIL} IN Name Fields"
15
+ # =>
16
+ # {"Lead"=>["00Q5j00000WgEuDEAV"],
17
+ # "Account"=>["0015j00001U2XvNAAV", "0015j00001U2XvMAAV", "0015j00001U2XvJAAV"],
18
+ # "Contact"=>
19
+ # ["0035j00001HB84BAAT",
20
+ # "0035j00001HB84DAAT"],
21
+ # "Opportunity"=>
22
+ # ["0065j00001XHJLjAAP",
23
+ # "0065j00001XHJLTAA5",
24
+ # "0065j00001XHJLJAA5"],
25
+ # "User"=>["0055j00000CcL2bAAF", "0055j00000CcL1YAAV"]}
26
+ #
27
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_search_unified]
28
+ #
29
+ def search(sosl, target_org: nil, format: nil)
30
+ flags = {
31
+ :"query" => %|"#{sosl}"|,
32
+ :"target-org" => target_org,
33
+ :"result-format" => format,
34
+ }
35
+ raw_output = format ? true : false
36
+ format = format&.to_sym || :json
37
+
38
+ result = exec(__method__, flags: flags, redirection: :null_stderr, raw_output: raw_output, format: format)
39
+
40
+ return if format == :csv
41
+ return result if format == :human
42
+
43
+ result['result']['searchRecords']
44
+ .group_by{|r| r['attributes']['type']}
45
+ .each_with_object({}) do |(object_type, records), result|
46
+ result[object_type] = records.map{|r| r['Id']}
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,38 @@
1
+ module SfCli::Sf::Data
2
+ module UpdateRecord
3
+
4
+ # update a object record.
5
+ #
6
+ # *object_type* --- \Object Type (ex. Account)<br>
7
+ #
8
+ # *record_id* --- id of the object<br>
9
+ #
10
+ # *where* --- field values that is used to identify a record<br>
11
+ #
12
+ # *values* --- field values for update<br>
13
+ #
14
+ # *target_org* --- an alias of paticular org, or username can be used<br>
15
+ #
16
+ # ==== examples
17
+ # sf.data.update_record :Account, record_id: 'xxxxxxx', values: {Name: 'New Account Name'}
18
+ # sf.data.update_record :Hoge__c, where: {Name: 'Jonny B.Good', Country: 'USA'}, values: {Phone: 'xxxxx', bar: 2000}
19
+ #
20
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_update_record_unified]
21
+ #
22
+ def update_record(object_type, record_id: nil, where: nil, values: nil, target_org: nil)
23
+ where_conditions = field_value_pairs(where)
24
+ field_values = field_value_pairs(values)
25
+ flags = {
26
+ :"sobject" => object_type,
27
+ :"record-id" => record_id,
28
+ :"where" => (where_conditions.nil? ? nil : %|"#{where_conditions}"|),
29
+ :"values" => (field_values.nil? ? nil : %|"#{field_values}"|),
30
+ :"target-org" => target_org,
31
+ }
32
+ action = __method__.to_s.tr('_', ' ')
33
+ json = exec(action, flags: flags, redirection: :null_stderr)
34
+
35
+ json['result']['id']
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ require_relative './bulk_result_v2'
2
+
3
+ module SfCli::Sf::Data
4
+ module UpsertBulk
5
+ # update records using Bulk API 2.0
6
+ #
7
+ # *file* --- a \CSV file for update records<br>
8
+ #
9
+ # *sobject* --- \Object Type (ex. Account)<br>
10
+ #
11
+ # *external_id* --- Name of the external ID field, or the Id field<br>
12
+ #
13
+ # *timeout* --- max minutes to wait for the job complete the task.<br>
14
+ #
15
+ # *target_org* --- an alias of paticular org, or username can be used<br>
16
+ #
17
+ # ======
18
+ # # start a upsert job
19
+ # jobinfo = sf.data.upsert_bulk sobject: :TestCustomObject__c, file: 'upsert.csv' # this returns immediately
20
+ # jobinfo.id # => "750J4000003g1OaIAI" it's job ID
21
+ #
22
+ # # you can check if the upsert job completed
23
+ # sf.data.upsert_resume job_id: jobinfo.id
24
+ #
25
+ # # Or, you can wait for the job completion with one try.
26
+ # result = sf.data.upsert_bulk sobject: :TestCustomObject__c, file: 'upsert.csv', timeout: 5 # wait within 5 minutes
27
+ #
28
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_upsert_bulk_unified]
29
+ #
30
+ def upsert_bulk(file:, sobject:, external_id:, timeout: nil, target_org: nil)
31
+ flags = {
32
+ :"file" => file,
33
+ :"sobject" => sobject,
34
+ :"external-id" => external_id,
35
+ :"wait" => timeout,
36
+ :"target-org" => target_org,
37
+ }
38
+ action = __method__.to_s.tr('_', ' ')
39
+ json = exec(action, flags: flags, redirection: :null_stderr)
40
+
41
+ job_info = ::SfCli::Sf::Data::JobInfo.new(**json['result']['jobInfo'])
42
+ return job_info unless json['result']['records']
43
+
44
+ ::SfCli::Sf::Data::BulkResultV2.new(
45
+ job_info: job_info,
46
+ records: ::SfCli::Sf::Data::BulkRecordsV2.new(**json['result']['records'])
47
+ )
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ require_relative './bulk_result_v2'
2
+
3
+ module SfCli::Sf::Data
4
+ module UpsertResume
5
+ # resume a bulk upsert job you previously started with Bulk API 2.0 and return a bulk result object.
6
+ #
7
+ # *job_id* --- job ID you want to resume<br>
8
+ #
9
+ # *timeout* --- max minutes to wait for the job complete the task.<br>
10
+ #
11
+ # *target_org* --- an alias of paticular org, or username can be used<br>
12
+ #
13
+ # ======
14
+ # # start a upsert job
15
+ # jobinfo = sf.data.upsert_bulk sobject: :TestCustomObject__c, file: 'upsert.csv' # this returns immediately
16
+ # jobinfo.id # => "750J4000003g1OaIAI" it's job ID
17
+ #
18
+ # # the job has already started asynchronously.
19
+ # # So you should check its progress.
20
+ # # if you want to wait for the job complete the task, try 'timeout' option.
21
+ # result = sf.data.upsert_resume job_id: jobinfo.id
22
+ #
23
+ # puts 'yey!' if result.job_info.completed? # the job has completed
24
+ #
25
+ # To know more about a job result, take a look at SfCli::Sf::Data module
26
+ #
27
+ # For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_data_commands_unified.htm#cli_reference_data_upsert_resume_unified]
28
+ #
29
+ def upsert_resume(job_id:, timeout: nil, target_org: nil)
30
+ flags = {
31
+ :"job-id" => job_id,
32
+ :"wait" => timeout,
33
+ :"target-org" => target_org,
34
+ }
35
+ action = __method__.to_s.tr('_', ' ')
36
+ json = exec(action, flags: flags, redirection: :null_stderr)
37
+
38
+ job_info = ::SfCli::Sf::Data::JobInfo.new(**json['result']['jobInfo'])
39
+ return job_info unless json['result']['records']
40
+
41
+ ::SfCli::Sf::Data::BulkResultV2.new(
42
+ job_info: job_info,
43
+ records: ::SfCli::Sf::Data::BulkRecordsV2.new(**json['result']['records'])
44
+ )
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ require 'singleton'
2
+ require 'json'
3
+
4
+ module SfCli
5
+ module Sf
6
+ # ==== description
7
+ # The entry class of sf command. It is returned by sf method.
8
+ # With including Singleton module, the instance of this class is guaranteed as singleton.
9
+ #
10
+ # ==== examples
11
+ # sf # returns a instance of SfCli::Sf
12
+ # sf.org.display # you can follow the similar syntax to the original command by using method chain.
13
+ #
14
+ class Main
15
+ include Singleton
16
+
17
+ OPERATION_CATEGORIES = %w[Org Sobject Data Project]
18
+
19
+ OPERATION_CATEGORIES.each do |category|
20
+ require_relative %(#{category.downcase}/core)
21
+ attr_reader category.downcase.to_sym
22
+ end
23
+
24
+ def initialize
25
+ OPERATION_CATEGORIES.each do |category|
26
+ instance_variable_set(:"@#{category.downcase}", Object.const_get(%|::SfCli::Sf::#{category}::Core|).new)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,112 @@
1
+ require_relative './schema'
2
+
3
+ module SfCli
4
+ module Sf
5
+ module Model
6
+ class ClassDefinition
7
+ attr_reader :schema
8
+
9
+ def initialize(schema)
10
+ @schema = Schema.new(schema)
11
+ end
12
+
13
+ def to_s
14
+ <<~Klass
15
+ Class.new do
16
+ #{ class_methods }
17
+
18
+ field_names.each do |name|
19
+ attr_accessor name
20
+ end
21
+
22
+ #{ parent_relation_methods }
23
+ #{ children_relation_methods }
24
+
25
+ #{ define_initialize }
26
+
27
+ #{ define_to_h }
28
+ end
29
+ Klass
30
+ end
31
+
32
+ def define_initialize
33
+ <<~EOS
34
+ def initialize(attributes = {})
35
+ attributes.each do |k, v|
36
+ field_name = k.to_sym
37
+ if self.class.field_names.include?(field_name)
38
+ #instance_variable_set ('@' + field_name.to_s).to_sym, v
39
+ __send__ (field_name.to_s + '='), v
40
+ elsif self.class.parent_relations.find{|r| r[:name] == field_name}
41
+ __send__ (field_name.to_s + '='), v
42
+ elsif self.class.children_relations.find{|r| r[:name] == field_name}
43
+ __send__ (field_name.to_s + '='), (v.nil? ? [] : v)
44
+ end
45
+ end
46
+ end
47
+ EOS
48
+ end
49
+
50
+ def define_to_h
51
+ <<~EOS
52
+ def to_h(keys: nil)
53
+ self.class.field_names.each_with_object({}) do |name, hash|
54
+ if keys&.instance_of?(Array)
55
+ hash[name] = __send__(name) if keys.include?(name)
56
+ else
57
+ hash[name] = __send__(name)
58
+ end
59
+ end
60
+ end
61
+ EOS
62
+ end
63
+
64
+ def class_methods
65
+ <<~EOS
66
+ class << self
67
+ def field_names
68
+ @field_names ||= #{ schema.field_names }
69
+ end
70
+
71
+ def parent_relations
72
+ @parent_relations ||= #{ schema.parent_relations }
73
+ end
74
+
75
+ def children_relations
76
+ @children_relations ||= #{ schema.children_relations }
77
+ end
78
+ end
79
+ EOS
80
+ end
81
+
82
+ def parent_relation_methods
83
+ schema.parent_relations.each_with_object('') do |r, s|
84
+ s << <<~EOS
85
+ def #{r[:name]}
86
+ @#{r[:name]}
87
+ end
88
+
89
+ def #{r[:name]}=(attributes)
90
+ @#{r[:name]} = #{r[:class_name]}.new(attributes)
91
+ end
92
+ EOS
93
+ end
94
+ end
95
+
96
+ def children_relation_methods
97
+ schema.children_relations.each_with_object('') do |r, s|
98
+ s << <<~EOS
99
+ def #{r[:name]}
100
+ @#{r[:name]}
101
+ end
102
+
103
+ def #{r[:name]}=(records)
104
+ @#{r[:name]} = records.map{|attributes| #{r[:class_name]}.new(attributes)}
105
+ end
106
+ EOS
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../sobject/core'
2
+ require_relative './class_definition'
3
+
4
+ module SfCli
5
+ module Sf
6
+ module Model
7
+ class Generator
8
+ attr_reader :sf_sobject, :target_org
9
+
10
+ def initialize(target_org: nil)
11
+ @sf_sobject = ::SfCli::Sf::Sobject::Core.new
12
+ @target_org = target_org
13
+ end
14
+
15
+ def generate(object_name)
16
+ class_definition = ClassDefinition.new(describe object_name)
17
+
18
+ instance_eval "::#{object_name} = #{class_definition}"
19
+ end
20
+
21
+ def describe(object_name)
22
+ sf_sobject.describe object_name, target_org: target_org
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ module SfCli
2
+ module Sf
3
+ module Model
4
+ class Schema
5
+ attr_reader :schema
6
+
7
+ def initialize(schema)
8
+ @schema = schema
9
+ end
10
+
11
+ def name
12
+ schema['name']
13
+ end
14
+
15
+ def field_names
16
+ schema['fields'].map{|f| f['name'].to_sym}
17
+ end
18
+
19
+ def children_relations
20
+ schema['childRelationships']
21
+ .select{|r| r['relationshipName'].nil? == false}
22
+ .map{|r| {name: r['relationshipName'].to_sym, class_name: r['childSObject'].to_sym}}
23
+ end
24
+
25
+ def parent_relations
26
+ schema['fields']
27
+ .select{|f| f['relationshipName'].nil? == false && f['referenceTo']&.size > 0}
28
+ .map{|f| {name: f['relationshipName'].to_sym, class_name: f['referenceTo'].first.to_sym}}
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'model/generator'
2
+
3
+ module SfCli
4
+ module Sf
5
+ #
6
+ # ==== description
7
+ # The module for \Object \Model definition and generation
8
+ #
9
+ # see the section {"Object Model support"}[link://files/README_rdoc.html#label-Object+Model+support+-28experimental-2C+since+0.0.4-29] in README.
10
+ #
11
+ module Model
12
+ def self.generate(object_names, target_org: nil)
13
+ generator = Generator.new(target_org: target_org)
14
+
15
+ object_names.each do |object_name|
16
+ generator.generate(object_name)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end