eco-helpers 0.6.0

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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/README.md +20 -0
  5. data/eco-helpers.gemspec +34 -0
  6. data/lib/eco-helpers.rb +15 -0
  7. data/lib/eco/api.rb +13 -0
  8. data/lib/eco/api/common.rb +10 -0
  9. data/lib/eco/api/common/people.rb +17 -0
  10. data/lib/eco/api/common/people/base_parser.rb +16 -0
  11. data/lib/eco/api/common/people/default_parsers.rb +40 -0
  12. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +28 -0
  13. data/lib/eco/api/common/people/default_parsers/date_parser.rb +33 -0
  14. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +33 -0
  15. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +23 -0
  16. data/lib/eco/api/common/people/default_parsers/select_parser.rb +29 -0
  17. data/lib/eco/api/common/people/entries.rb +120 -0
  18. data/lib/eco/api/common/people/person_entry.rb +380 -0
  19. data/lib/eco/api/common/people/person_factory.rb +114 -0
  20. data/lib/eco/api/common/people/person_modifier.rb +62 -0
  21. data/lib/eco/api/common/people/person_parser.rb +140 -0
  22. data/lib/eco/api/common/people/types.rb +47 -0
  23. data/lib/eco/api/common/session.rb +15 -0
  24. data/lib/eco/api/common/session/base_session.rb +46 -0
  25. data/lib/eco/api/common/session/environment.rb +47 -0
  26. data/lib/eco/api/common/session/file_manager.rb +90 -0
  27. data/lib/eco/api/common/session/logger.rb +105 -0
  28. data/lib/eco/api/common/session/mailer.rb +92 -0
  29. data/lib/eco/api/common/session/s3_uploader.rb +110 -0
  30. data/lib/eco/api/common/version_patches.rb +11 -0
  31. data/lib/eco/api/common/version_patches/external_person.rb +11 -0
  32. data/lib/eco/api/eco_faker.rb +59 -0
  33. data/lib/eco/api/organization.rb +13 -0
  34. data/lib/eco/api/organization/account.rb +23 -0
  35. data/lib/eco/api/organization/people.rb +118 -0
  36. data/lib/eco/api/organization/policy_groups.rb +51 -0
  37. data/lib/eco/api/organization/preferences.rb +28 -0
  38. data/lib/eco/api/organization/preferences_reference.json +23 -0
  39. data/lib/eco/api/organization/presets.rb +138 -0
  40. data/lib/eco/api/organization/presets_backup.rb +220 -0
  41. data/lib/eco/api/organization/presets_values.json +10 -0
  42. data/lib/eco/api/organization/tag_tree.rb +134 -0
  43. data/lib/eco/api/organization_old.rb +73 -0
  44. data/lib/eco/api/session.rb +180 -0
  45. data/lib/eco/api/session/batch.rb +132 -0
  46. data/lib/eco/api/session/batch_job.rb +152 -0
  47. data/lib/eco/api/session/batch_jobs.rb +131 -0
  48. data/lib/eco/api/session/batch_status.rb +138 -0
  49. data/lib/eco/api/session/task.rb +92 -0
  50. data/lib/eco/api/session_config.rb +179 -0
  51. data/lib/eco/api/session_config/api.rb +47 -0
  52. data/lib/eco/api/session_config/apis.rb +78 -0
  53. data/lib/eco/api/session_config/files.rb +30 -0
  54. data/lib/eco/api/session_config/logger.rb +54 -0
  55. data/lib/eco/api/session_config/mailer.rb +65 -0
  56. data/lib/eco/api/session_config/people.rb +89 -0
  57. data/lib/eco/api/session_config/s3_bucket.rb +62 -0
  58. data/lib/eco/api/session_config/use_cases.rb +30 -0
  59. data/lib/eco/api/usecases.rb +12 -0
  60. data/lib/eco/api/usecases/base_case.rb +14 -0
  61. data/lib/eco/api/usecases/case_data.rb +13 -0
  62. data/lib/eco/api/usecases/default_cases.rb +53 -0
  63. data/lib/eco/api/usecases/default_cases/change_email_case.rb +47 -0
  64. data/lib/eco/api/usecases/default_cases/create_details_case.rb +29 -0
  65. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +49 -0
  66. data/lib/eco/api/usecases/default_cases/delete_case.rb +20 -0
  67. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +24 -0
  68. data/lib/eco/api/usecases/default_cases/hris_case.rb +67 -0
  69. data/lib/eco/api/usecases/default_cases/new_email_case.rb +26 -0
  70. data/lib/eco/api/usecases/default_cases/new_id_case.rb +26 -0
  71. data/lib/eco/api/usecases/default_cases/refresh_presets.rb +25 -0
  72. data/lib/eco/api/usecases/default_cases/reinvite_case.rb +22 -0
  73. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +36 -0
  74. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +24 -0
  75. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +44 -0
  76. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +39 -0
  77. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -0
  78. data/lib/eco/api/usecases/default_cases/update_details_case.rb +30 -0
  79. data/lib/eco/api/usecases/default_cases/upsert_account_case.rb +35 -0
  80. data/lib/eco/api/usecases/use_case.rb +177 -0
  81. data/lib/eco/api/usecases/use_group.rb +104 -0
  82. data/lib/eco/cli.rb +9 -0
  83. data/lib/eco/cli/input.rb +109 -0
  84. data/lib/eco/cli/input_multi.rb +137 -0
  85. data/lib/eco/cli/root.rb +8 -0
  86. data/lib/eco/cli/session.rb +9 -0
  87. data/lib/eco/cli/session/batch.rb +9 -0
  88. data/lib/eco/common.rb +7 -0
  89. data/lib/eco/common/base_cli.rb +116 -0
  90. data/lib/eco/common/language.rb +9 -0
  91. data/lib/eco/data.rb +9 -0
  92. data/lib/eco/data/crypto.rb +7 -0
  93. data/lib/eco/data/crypto/encryption.rb +318 -0
  94. data/lib/eco/data/files.rb +10 -0
  95. data/lib/eco/data/files/directory.rb +93 -0
  96. data/lib/eco/data/files/file_pattern.rb +32 -0
  97. data/lib/eco/data/files/helpers.rb +90 -0
  98. data/lib/eco/data/mapper.rb +54 -0
  99. data/lib/eco/data/random.rb +10 -0
  100. data/lib/eco/data/random/distribution.rb +133 -0
  101. data/lib/eco/data/random/fake.rb +320 -0
  102. data/lib/eco/data/random/values.rb +80 -0
  103. data/lib/eco/language.rb +12 -0
  104. data/lib/eco/language/curry.rb +28 -0
  105. data/lib/eco/language/hash_transform.rb +68 -0
  106. data/lib/eco/language/hash_transform_modifier.rb +114 -0
  107. data/lib/eco/language/match.rb +30 -0
  108. data/lib/eco/language/match_modifier.rb +190 -0
  109. data/lib/eco/language/models.rb +11 -0
  110. data/lib/eco/language/models/attribute_parser.rb +38 -0
  111. data/lib/eco/language/models/collection.rb +181 -0
  112. data/lib/eco/language/models/modifier.rb +68 -0
  113. data/lib/eco/language/models/wrap.rb +114 -0
  114. data/lib/eco/language/values_at.rb +159 -0
  115. data/lib/eco/lexic/dictionary.rb +33 -0
  116. data/lib/eco/lexic/dictionary/dictionary.txt +355484 -0
  117. data/lib/eco/lexic/dictionary/tags.json +38 -0
  118. data/lib/eco/scripting.rb +30 -0
  119. data/lib/eco/scripting/README.md +11 -0
  120. data/lib/eco/scripting/arguments.rb +40 -0
  121. data/lib/eco/tester.rb +97 -0
  122. data/lib/eco/version.rb +3 -0
  123. metadata +325 -0
@@ -0,0 +1,105 @@
1
+ module Eco
2
+ module API
3
+ module Common
4
+ module Session
5
+ class Logger
6
+ DEFAULT_TIMESTAMP_PATTERN = '%Y-%m-%dT%H:%M:%S'
7
+
8
+ def initialize(console_level: nil, file_level: ::Logger::DEBUG, log_file: nil, timestamp_console: false, enviro: nil)
9
+ raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
10
+ @enviro = enviro
11
+
12
+ timestamp_console = fetch_timestamp_console(timestamp_console)
13
+ @console_logger = ::Logger.new(STDOUT).tap do |_logger|
14
+ _logger.formatter = proc do |severity, datetime, progname, msg|
15
+ str_timestamp = timestamp_console ? "#{datetime.strftime(DEFAULT_TIMESTAMP_PATTERN)} >" : ""
16
+ "#{severity.to_s[0]}: #{str_timestamp} #{msg}\n"
17
+ end
18
+ _logger.level = fetch_console_level(console_level)
19
+ end
20
+
21
+ if log_file = fetch_log_file(log_file)
22
+ @file_logger = ::Logger.new(log_file).tap do |_logger|
23
+ _logger.formatter = proc do |severity, datetime, progname, msg|
24
+ "#{severity.to_s[0]}: #{datetime.strftime(DEFAULT_TIMESTAMP_PATTERN)} > #{msg}\n"
25
+ end
26
+ _logger.level = fetch_file_level(file_level)
27
+ end
28
+ end
29
+ end
30
+
31
+ def debug(*args, &block)
32
+ log(:debug, *args, &block)
33
+ end
34
+
35
+ def info(*args, &block)
36
+ log(:info, *args, &block)
37
+ end
38
+
39
+ def warn(*args, &block)
40
+ return log(:warn, *args, &block)
41
+ end
42
+
43
+ def error(*args, &block)
44
+ return log(:error, *args, &block)
45
+ end
46
+
47
+ def fatal(*args, &block)
48
+ return log(:fatal, *args, &block)
49
+ end
50
+
51
+ def unkown(msg, &block)
52
+ return log(:unkown, *args, &block)
53
+ end
54
+
55
+ def << (msg)
56
+ @console_logger << msg unless !@console_logger
57
+ @file_logger << msg unless !@file_logger
58
+ end
59
+
60
+ def close()
61
+ @console_logger.close unless !@console_logger
62
+ @file_logger.close unless !@file_logger
63
+ end
64
+
65
+ def add(*args)
66
+ @console_logger.add(*args) unless !@console_logger
67
+ @file_logger.add(*args) unless !@file_logger
68
+ end
69
+
70
+ private
71
+
72
+ def log(level, *args, &block)
73
+ @console_logger.send(level, *args, &block) unless !@console_logger
74
+ @file_logger.send(level, *args, &block) unless !@file_logger
75
+ end
76
+
77
+ def config
78
+ @enviro.config || {}
79
+ end
80
+
81
+ def fetch_timestamp_console(value)
82
+ value || config.logger.timestamp_console
83
+ end
84
+
85
+ def fetch_console_level(value)
86
+ value || config.logger.console_level || ::Logger::INFO
87
+ end
88
+
89
+ def fetch_file_level(value)
90
+ value || config.logger.file_level || ::Logger::DEBUG
91
+ end
92
+
93
+ def fetch_log_file(log_file)
94
+ if log_file ||= config.logger.file
95
+ log_file = @enviro.file_manager.dir.file(log_file) if @enviro
96
+ FileManager.create_directory(log_file, includes_file: true)
97
+ end
98
+ log_file
99
+ end
100
+
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,92 @@
1
+ require 'aws-sdk-ses'
2
+ require 'dotenv/load'
3
+
4
+ module Eco
5
+ module API
6
+ module Common
7
+ module Session
8
+ class Mailer
9
+
10
+ def initialize (enviro:)
11
+ raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
12
+ @enviro = enviro
13
+ end
14
+
15
+ def mail(to:, subject:, body:)
16
+ ses.send_email(
17
+ destination: {
18
+ to_addresses: [to].flatten,
19
+ },
20
+ source: fetch_from,
21
+ message: {
22
+ subject: {
23
+ charset: "UTF-8",
24
+ data: subject,
25
+ },
26
+ body: {
27
+ # NOTE - html: will let you send html instead
28
+ # you can use both at once if you like
29
+ text: {
30
+ charset: "UTF-8",
31
+ data: body
32
+ }
33
+ }
34
+ }
35
+ ).tap do |response|
36
+ logger.debug("Sent email to #{to} (MessageId: #{response.message_id})")
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def ses
43
+ begin
44
+ @ses ||= Aws::SES::Client.new(
45
+ access_key_id: fetch_access_key_id,
46
+ secret_access_key: fetch_secret_access_key,
47
+ region: fetch_region
48
+ )
49
+ rescue Exception => e
50
+ logger.error("Trying to send an email with wrong email configuration: #{e}")
51
+ end
52
+ @ses
53
+ end
54
+
55
+ def logger
56
+ @enviro&.logger || ::Logger.new(IO::NULL)
57
+ end
58
+
59
+ def config
60
+ @enviro.config || {}
61
+ end
62
+
63
+ def fetch_from(value = nil)
64
+ value || config.mailer.from
65
+ end
66
+
67
+ def fetch_access_key_id
68
+ config.mailer.access_key_id || ENV['AWS_ACCESS_KEY_ID']
69
+ end
70
+
71
+ def fetch_secret_access_key
72
+ config.mailer.secret_access_key || ENV['AWS_SECRET_ACCESS_KEY']
73
+ end
74
+
75
+ def fetch_region
76
+ config.mailer.region || ENV['AWS_REGION']
77
+ end
78
+
79
+ def fetch_server
80
+ config.mailer.server
81
+ end
82
+
83
+ def fetch_message_id_domain
84
+ config.mailer.message_id_domain
85
+ end
86
+
87
+
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,110 @@
1
+ require 'aws-sdk-s3'
2
+ require 'dotenv/load'
3
+
4
+ module Eco
5
+ module API
6
+ module Common
7
+ module Session
8
+ class S3Uploader
9
+ attr_reader :prefix
10
+
11
+ def initialize (enviro:)
12
+ raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
13
+ @enviro = enviro
14
+ @prefix = fetch_prefix
15
+ @timestamp = Time.now.iso8601
16
+ end
17
+
18
+ def upload(filename, content)
19
+ if obj = new_s3_object(filename)
20
+ log_upload(obj) do
21
+ obj.put(body: content)
22
+ end
23
+ end
24
+ return full_path(obj)
25
+ end
26
+
27
+ def upload_file(path)
28
+ File.open(path, "rb") do |f|
29
+ upload(File.basename(path), f)
30
+ end
31
+ end
32
+
33
+ def upload_directory(path)
34
+ prefix = File.expand_path(File.join(path, ".."))
35
+ Dir.glob(File.join(path, "**/*")).sort.map do |file|
36
+ next unless File.file?(file) # Skip directories
37
+ key = file.sub(prefix,"").gsub(/\\/,"/").sub(/^\/+/,"")
38
+
39
+ File.open(file, "rb") do |f|
40
+ upload(key, f)
41
+ end
42
+ end.compact
43
+ end
44
+
45
+ private
46
+
47
+ def full_path(obj)
48
+ return "s3://#{bucket.name}/#{obj.key}" if obj
49
+ end
50
+
51
+ def bucket
52
+ begin
53
+ @bucket ||= Aws::S3::Resource.new(
54
+ access_key_id: fetch_access_key_id,
55
+ secret_access_key: fetch_secret_access_key,
56
+ region: fetch_region
57
+ ).bucket(fetch_bucket)
58
+ rescue Exception => e
59
+ logger.error("Trying to upload to S3 with wrong configuration: #{e}")
60
+ end
61
+ @bucket
62
+ end
63
+
64
+ def session_path
65
+ ["scripts", @prefix, @timestamp].join("/")
66
+ end
67
+
68
+ def new_s3_object(filename)
69
+ file_path = [session_path, filename].join("/")
70
+ bucket&.object(file_path)
71
+ end
72
+
73
+ def log_upload(obj)
74
+ yield
75
+ logger.debug("Uploaded #{full_path(obj)}")
76
+ end
77
+
78
+ def logger
79
+ @enviro&.logger || ::Logger.new(IO::NULL)
80
+ end
81
+
82
+ def config
83
+ @enviro.config || {}
84
+ end
85
+
86
+ def fetch_bucket
87
+ config.s3bucket.name
88
+ end
89
+
90
+ def fetch_prefix
91
+ config.s3bucket.prefix
92
+ end
93
+
94
+ def fetch_access_key_id
95
+ config.s3bucket.access_key_id || ENV['AWS_ACCESS_KEY_ID']
96
+ end
97
+
98
+ def fetch_secret_access_key
99
+ config.s3bucket.secret_access_key || ENV['AWS_SECRET_ACCESS_KEY']
100
+ end
101
+
102
+ def fetch_region
103
+ config.s3bucket.region || ENV['AWS_REGION']
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,11 @@
1
+ module Eco
2
+ module API
3
+ module Common
4
+ module VersionPatches
5
+ end
6
+ end
7
+ end
8
+ end
9
+
10
+ #require_relative 'version_patches/base_model.rb'
11
+ require_relative 'version_patches/external_person.rb'
@@ -0,0 +1,11 @@
1
+ module Ecoportal
2
+ module API
3
+ class V1
4
+ class Person < Common::BaseModel
5
+ def sync
6
+ @original_doc = JSON.parse(@doc.to_json)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Eco
2
+ module API
3
+ # faker object
4
+ @@faker = nil
5
+ class EcoFaker
6
+ @@api = nil
7
+ @org = nil
8
+ # attr_accessor
9
+ attr_reader :org
10
+ def initialize(init = {})
11
+ set_settings(init)
12
+ end
13
+ # allows to change certain configuration settings on an existing Faker instance object
14
+ def set_settings (init = {})
15
+ @today = (aux = init['today'])? aux : Time.now
16
+ api = init.fetch('api', nil)
17
+ @@api = api.instance_of?(Ecoportal::API::Internal) && api
18
+ # this will initialize org basics (i.e. people_schemas, policy_groups, login_providers)
19
+ @org = Organization.new(init)
20
+ end
21
+ def newPerson ()
22
+
23
+ end
24
+ def modifyPerson(person)
25
+
26
+ end
27
+ private
28
+ end
29
+
30
+ def self.new(init = {})
31
+ @@faker = EcoFaker.new(init)
32
+ end
33
+
34
+ class FakePerson < Ecoportal::API::Internal::Person
35
+ @@faker
36
+ def initialize(faker, *args)
37
+ raise "You must initialize the faker first" unless faker && faker.instance_of(Faker)
38
+ @@faker = faker
39
+ super(*args)
40
+ @@faker.set_details
41
+ end
42
+ end
43
+
44
+ class FakeField
45
+ @@types = ["boolean", "number", "text", "select", "date", "phone_number"]
46
+ @@multiple = false
47
+ attr_accessor :type, :multiple
48
+ #attr_reader
49
+ def initialize (init = {})
50
+ init_values_generator(init)
51
+ end
52
+ private
53
+ def init_values_faker (init = {})
54
+ include
55
+ @fake_values = API::Data::Random::ValuesGenerator.new(init)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ module Eco
2
+ module API
3
+ module Organization
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'organization/tag_tree'
9
+ require_relative 'organization/presets'
10
+ require_relative 'organization/preferences'
11
+ require_relative 'organization/account'
12
+ require_relative 'organization/people'
13
+ require_relative 'organization/policy_groups'
@@ -0,0 +1,23 @@
1
+ module Eco
2
+ module API
3
+ module Account
4
+ class Account
5
+
6
+ attr_accessor :policy_group_ids, :filter_tags
7
+ attr_accessor :permissions_preset, :permissions_custom
8
+ attr_accessor :login_provider_ids
9
+ attr_accessor :landing_page_id, :preferences, :starred_ids
10
+ attr_reader :prefilter
11
+ #attr_reader :presets
12
+
13
+ def initialize(init = {})
14
+ #@presets = AccountPresets.new
15
+ end
16
+ def set_details(person)
17
+
18
+ end
19
+ private
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,118 @@
1
+ module Eco
2
+ module API
3
+ module Organization
4
+ class People < Eco::Language::Models::Collection
5
+
6
+ # build the shortcuts of Collection
7
+ attr_collection :id, :external_id, :email, :name, :supervisor_id
8
+ attr_presence :account, :details
9
+
10
+ alias_method :people, :to_a
11
+
12
+ include Common::People
13
+
14
+ def initialize(people = [], klass: INTERNAL::Person, factory: nil)
15
+ @klass = INTERNAL::Person unless klass == EXTERNAL::Person
16
+ super(people, klass: @klass)
17
+ @caches_init = false
18
+ end
19
+
20
+ def [](id_or_ext)
21
+ self.id(id_or_ext) || self.external_id(id_or_ext)
22
+ end
23
+
24
+ def id(*args)
25
+ attr('id', *args).first
26
+ end
27
+
28
+ def external_id(*args)
29
+ attr('external_id', *args).first
30
+ end
31
+
32
+ def users
33
+ self.account_present(true)
34
+ end
35
+
36
+ def contacts
37
+ self.details_present(true)
38
+ end
39
+
40
+ def non_users
41
+ self.account_present(false)
42
+ end
43
+
44
+ def person(id: nil, external_id: nil, email: nil)
45
+ init_caches
46
+ pers = @by_id[id]&.first if id
47
+ pers = @by_external_id[external_id&.strip]&.first if !pers && !external_id.to_s.strip.empty?
48
+ pers = @by_users_email[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty?
49
+ pers = @by_non_users_email[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty?
50
+ pers = @by_external_id[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty?
51
+ pers
52
+ end
53
+
54
+ def find(object)
55
+ id = object.respond_to?("id")? object.send("id") : nil
56
+ external_id = object.respond_to?("external_id")? object.send("external_id") : nil
57
+ email = object.respond_to?("email")? object.send("email") : nil
58
+ person(id: id, external_id: external_id, email: email)
59
+ end
60
+
61
+ def exclude(object)
62
+ exclude_people(into_a(object))
63
+ end
64
+
65
+ def exclude_people(list)
66
+ discarded = list.map do |person|
67
+ find(person)
68
+ end.compact
69
+ newFrom self.to_a - discarded
70
+ end
71
+
72
+ def email_id_maps
73
+ self.users.group_by(:email).transform_values { |person| person.id }
74
+ end
75
+
76
+ def group_by_supervisor
77
+ self.to_h(:supervisor_id)
78
+ end
79
+
80
+ # only those that are present
81
+ def supervisors
82
+ h_all = self.to_h
83
+ sup_ids = h_all.keys & self.supervisor_ids
84
+ newFrom h_all.select do |id, person|
85
+ sup_ids.include?(id)
86
+ end.values
87
+ end
88
+
89
+ def missing_supervisors_ids
90
+ sup_ids = self.supervisor_ids
91
+ sup_ids - (sup_ids & self.ids)
92
+ end
93
+
94
+ def to_h(attr)
95
+ super(attr || "id")
96
+ end
97
+
98
+ protected
99
+
100
+ def on_change
101
+ @caches_init = false
102
+ end
103
+
104
+ private
105
+
106
+ def init_caches
107
+ return if @caches_init
108
+ @by_id = self.to_h('id')
109
+ @by_external_id = self.to_h('external_id')
110
+ @by_users_email = self.users.to_h('email')
111
+ @by_non_users_email = self.non_users.to_h('email')
112
+ @caches_init = true
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+ end