meibo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +13 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +16 -0
  7. data/Gemfile.lock +84 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +12 -0
  11. data/lib/meibo/academic_session.rb +48 -0
  12. data/lib/meibo/academic_session_set.rb +15 -0
  13. data/lib/meibo/base_profile.rb +37 -0
  14. data/lib/meibo/builder/academic_session_builder.rb +26 -0
  15. data/lib/meibo/builder/base_builder.rb +13 -0
  16. data/lib/meibo/builder/classroom_builder.rb +38 -0
  17. data/lib/meibo/builder/course_builder.rb +32 -0
  18. data/lib/meibo/builder/demographic_builder.rb +22 -0
  19. data/lib/meibo/builder/enrollment_builder.rb +30 -0
  20. data/lib/meibo/builder/organization_builder.rb +34 -0
  21. data/lib/meibo/builder/role_builder.rb +30 -0
  22. data/lib/meibo/builder/user_builder.rb +36 -0
  23. data/lib/meibo/builder/user_profile_builder.rb +22 -0
  24. data/lib/meibo/builder.rb +60 -0
  25. data/lib/meibo/classroom.rb +67 -0
  26. data/lib/meibo/classroom_set.rb +29 -0
  27. data/lib/meibo/converter.rb +259 -0
  28. data/lib/meibo/course.rb +45 -0
  29. data/lib/meibo/course_set.rb +23 -0
  30. data/lib/meibo/data_model.rb +84 -0
  31. data/lib/meibo/data_set.rb +44 -0
  32. data/lib/meibo/demographic.rb +70 -0
  33. data/lib/meibo/demographic_set.rb +18 -0
  34. data/lib/meibo/enrollment.rb +51 -0
  35. data/lib/meibo/enrollment_set.rb +22 -0
  36. data/lib/meibo/errors.rb +13 -0
  37. data/lib/meibo/factory_bot/academic_session.rb +23 -0
  38. data/lib/meibo/factory_bot/all.rb +13 -0
  39. data/lib/meibo/factory_bot/classroom.rb +28 -0
  40. data/lib/meibo/factory_bot/course.rb +21 -0
  41. data/lib/meibo/factory_bot/demographic.rb +16 -0
  42. data/lib/meibo/factory_bot/enrollment.rb +37 -0
  43. data/lib/meibo/factory_bot/manifest.rb +33 -0
  44. data/lib/meibo/factory_bot/memory_package.rb +11 -0
  45. data/lib/meibo/factory_bot/organization.rb +47 -0
  46. data/lib/meibo/factory_bot/role.rb +41 -0
  47. data/lib/meibo/factory_bot/user.rb +22 -0
  48. data/lib/meibo/factory_bot/user_profile.rb +21 -0
  49. data/lib/meibo/japan_profile/academic_session.rb +26 -0
  50. data/lib/meibo/japan_profile/classroom.rb +21 -0
  51. data/lib/meibo/japan_profile/course.rb +18 -0
  52. data/lib/meibo/japan_profile/demographic.rb +15 -0
  53. data/lib/meibo/japan_profile/enrollment.rb +35 -0
  54. data/lib/meibo/japan_profile/organization.rb +20 -0
  55. data/lib/meibo/japan_profile/role.rb +26 -0
  56. data/lib/meibo/japan_profile/user.rb +27 -0
  57. data/lib/meibo/japan_profile/user_profile.rb +7 -0
  58. data/lib/meibo/japan_profile.rb +39 -0
  59. data/lib/meibo/manifest/processing_mode.rb +40 -0
  60. data/lib/meibo/manifest.rb +163 -0
  61. data/lib/meibo/organization.rb +44 -0
  62. data/lib/meibo/organization_set.rb +15 -0
  63. data/lib/meibo/reader.rb +138 -0
  64. data/lib/meibo/role.rb +65 -0
  65. data/lib/meibo/role_set.rb +25 -0
  66. data/lib/meibo/roster.rb +166 -0
  67. data/lib/meibo/user.rb +71 -0
  68. data/lib/meibo/user_profile.rb +42 -0
  69. data/lib/meibo/user_profile_set.rb +18 -0
  70. data/lib/meibo/user_set.rb +24 -0
  71. data/lib/meibo/version.rb +5 -0
  72. data/lib/meibo.rb +17 -0
  73. data/meibo.gemspec +38 -0
  74. data/sig/meibo.rbs +4 -0
  75. metadata +148 -0
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zip'
4
+ require 'csv'
5
+
6
+ module Meibo
7
+ class Roster
8
+ class << self
9
+ def from_file(file_path, profile: BaseProfile)
10
+ Reader.open(file_path, profile: profile) do |reader|
11
+ begin
12
+ manifest = reader.manifest
13
+ rescue CsvFileNotFoundError
14
+ raise NotSupportedError, 'OneRoster 1.0はサポートしていません'
15
+ rescue
16
+ raise NotSupportedError, "#{Meibo::Manifest.filename}の読み込みに失敗しました"
17
+ end
18
+
19
+ validate_manifest_version(manifest.manifest_version)
20
+ validate_oneroster_version(manifest.oneroster_version)
21
+ validate_supported_processing_mode(manifest)
22
+ processing_modes = Meibo::Manifest::PROCESSING_MODES
23
+ validate_absent_files(reader, manifest.filenames(processing_mode: processing_modes[:absent]))
24
+ validate_bulk_files(reader, manifest.filenames(processing_mode: processing_modes[:bulk]))
25
+
26
+ new(manifest_properties: manifest.to_h, profile: profile, **reader.load_bulk_files).tap(&:check_semantically_consistent)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def validate_absent_files(reader, absent_filenames)
33
+ absent_filenames.each do |absent_filename|
34
+ next unless reader.file_entry?(absent_filename)
35
+
36
+ raise NotSupportedError, "#{absent_filename}が存在します"
37
+ end
38
+ end
39
+
40
+ def validate_bulk_files(reader, bulk_filenames)
41
+ bulk_filenames.each do |bulk_filename|
42
+ next if reader.file_entry?(bulk_filename)
43
+
44
+ raise NotSupportedError, "#{bulk_filename}が存在しません"
45
+ end
46
+ end
47
+
48
+ def validate_manifest_version(manifest_version)
49
+ return if manifest_version == Meibo::Manifest::MANIFEST_VERSION
50
+
51
+ raise NotSupportedError, "manifest.version: #{manifest_version}はサポートしていません"
52
+ end
53
+
54
+ def validate_oneroster_version(oneroster_version)
55
+ return if oneroster_version == Meibo::Manifest::ONEROSTER_VERSION
56
+
57
+ raise NotSupportedError, "oneroster.version: #{oneroster_version}はサポートしていません"
58
+ end
59
+
60
+ def validate_supported_processing_mode(manifest)
61
+ return if manifest.file_attributes(processing_mode: Meibo::Manifest::PROCESSING_MODES[:delta]).empty?
62
+
63
+ raise NotSupportedError, 'DELTAはサポートしていません'
64
+ end
65
+ end
66
+
67
+ attr_reader :profile, :manifest_properties, :academic_sessions, :classes, :courses, :demographics, :enrollments, :organizations, :roles, :user_profiles, :users
68
+
69
+ def initialize(profile: BaseProfile, manifest_properties: {}, academic_sessions: [], classes: [], courses: [], demographics: [], enrollments: [], organizations: [], roles: [], user_profiles: [], users: [])
70
+ @profile = profile
71
+ @manifest_properties = manifest_properties
72
+ @academic_sessions = AcademicSessionSet.new(academic_sessions)
73
+ @organizations = OrganizationSet.new(organizations)
74
+ @courses = CourseSet.new(courses, academic_session_set: @academic_sessions, organization_set: @organizations)
75
+ @classes = ClassroomSet.new(classes, academic_session_set: @academic_sessions, course_set: @courses, organization_set: @organizations)
76
+ @users = UserSet.new(users, organization_set: @organizations)
77
+ @demographics = DemographicSet.new(demographics, user_set: @users)
78
+ @user_profiles = UserProfileSet.new(user_profiles, user_set: @users)
79
+ @roles = RoleSet.new(roles, organization_set: @organizations, user_set: @users, user_profile_set: @user_profiles)
80
+ @enrollments = EnrollmentSet.new(enrollments, classroom_set: @classes, organization_set: @organizations, user_set: @users)
81
+ end
82
+
83
+ def check_semantically_consistent
84
+ [
85
+ academic_sessions,
86
+ classes,
87
+ courses,
88
+ demographics,
89
+ enrollments,
90
+ organizations,
91
+ roles,
92
+ user_profiles,
93
+ users
94
+ ].each(&:check_semantically_consistent)
95
+ end
96
+
97
+ def write(path)
98
+ Zip::File.open(path, ::Zip::File::CREATE) do |zipfile|
99
+ manifest = build_manifest
100
+ zipfile.get_output_stream(::Meibo::Manifest.filename) do |f|
101
+ f.puts ::Meibo::Manifest.header_fields.to_csv
102
+ manifest.to_a.each do |row|
103
+ f.puts row.to_csv
104
+ end
105
+ end
106
+ file_properties.each do |file_attribute, processing_mode|
107
+ next if processing_mode.absent?
108
+
109
+ klass = profile.data_model_for(file_attribute)
110
+ filename = Manifest.filename_for(file_attribute)
111
+ data = data_for(file_attribute)
112
+ zipfile.get_output_stream(filename) do |f|
113
+ f.puts klass.header_fields.to_csv
114
+ data.each do |row|
115
+ f.puts row.to_csv(write_converters: klass.write_converters)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def build_manifest
125
+ new_manifest_properties = file_properties.merge(manifest_properties)
126
+ Meibo::Manifest.build_from_default(**new_manifest_properties)
127
+ end
128
+
129
+ def data_for(file_attribute)
130
+ data_method = {
131
+ file_academic_sessions: :academic_sessions,
132
+ file_classes: :classes,
133
+ file_courses: :courses,
134
+ file_demographics: :demographics,
135
+ file_enrollments: :enrollments,
136
+ file_orgs: :organizations,
137
+ file_roles: :roles,
138
+ file_user_profiles: :user_profiles,
139
+ file_users: :users
140
+ }[file_attribute]
141
+ public_send(data_method)
142
+ end
143
+
144
+ def file_properties
145
+ {
146
+ file_academic_sessions: procesing_mode(academic_sessions),
147
+ file_classes: procesing_mode(classes),
148
+ file_courses: procesing_mode(courses),
149
+ file_demographics: procesing_mode(demographics),
150
+ file_enrollments: procesing_mode(enrollments),
151
+ file_orgs: procesing_mode(organizations),
152
+ file_roles: procesing_mode(roles),
153
+ file_user_profiles: procesing_mode(user_profiles),
154
+ file_users: procesing_mode(users)
155
+ }
156
+ end
157
+
158
+ def procesing_mode(data)
159
+ if data.empty?
160
+ Meibo::Manifest::PROCESSING_MODES[:absent]
161
+ else
162
+ Meibo::Manifest::PROCESSING_MODES[:bulk]
163
+ end
164
+ end
165
+ end
166
+ end
data/lib/meibo/user.rb ADDED
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class User
5
+ USER_ID_FORMAT_REGEXP = /\A\{[^:\}]+:[^\}]+\}\z/
6
+
7
+ DataModel.define(
8
+ self,
9
+ attribute_name_to_header_field_map: {
10
+ sourced_id: 'sourcedId',
11
+ status: 'status',
12
+ date_last_modified: 'dateLastModified',
13
+ enabled_user: 'enabledUser',
14
+ username: 'username',
15
+ user_ids: 'userIds',
16
+ given_name: 'givenName',
17
+ family_name: 'familyName',
18
+ middle_name: 'middleName',
19
+ identifier: 'identifier',
20
+ email: 'email',
21
+ sms: 'sms',
22
+ phone: 'phone',
23
+ agent_sourced_ids: 'agentSourcedIds',
24
+ grades: 'grades',
25
+ password: 'password',
26
+ user_master_identifier: 'userMasterIdentifier',
27
+ resource_sourced_ids: 'resourceSourcedIds',
28
+ preferred_given_name: 'preferredGivenName',
29
+ preferred_middle_name: 'preferredMiddleName',
30
+ preferred_family_name: 'preferredFamilyName',
31
+ primary_org_sourced_id: 'primaryOrgSourcedId',
32
+ pronouns: 'pronouns'
33
+ }.freeze,
34
+ converters: {
35
+ boolean: [:enabled_user].freeze,
36
+ datetime: [:date_last_modified].freeze,
37
+ list: [:user_ids, :agent_sourced_ids, :grades, :resource_sourced_ids].freeze,
38
+ required: [:sourced_id, :enabled_user, :username, :given_name, :family_name].freeze,
39
+ status: [:status].freeze,
40
+ user_ids: [:user_ids].freeze
41
+ }.freeze
42
+ )
43
+
44
+ def initialize(sourced_id:, status: nil, date_last_modified: nil, enabled_user: nil, username:, user_ids: nil, given_name:, family_name:, middle_name: nil, identifier: nil, email: nil, sms: nil, phone: nil, agent_sourced_ids: [], grades: nil, password: nil, user_master_identifier: nil, resource_sourced_ids: nil, preferred_given_name: nil, preferred_middle_name: nil, preferred_family_name: nil, primary_org_sourced_id: nil, pronouns: nil, kana_given_name: nil, kana_family_name: nil, kana_middle_name: nil, home_class: nil, **extension_fields)
45
+ @sourced_id = sourced_id
46
+ @status = status
47
+ @date_last_modified = date_last_modified
48
+ @enabled_user = enabled_user
49
+ @username = username
50
+ @user_ids = user_ids
51
+ @given_name = given_name
52
+ @family_name = family_name
53
+ @middle_name = middle_name
54
+ @identifier = identifier
55
+ @email = email
56
+ @sms = sms
57
+ @phone = phone
58
+ @agent_sourced_ids = agent_sourced_ids
59
+ @grades = grades
60
+ @password = password
61
+ @user_master_identifier = user_master_identifier
62
+ @resource_sourced_ids = resource_sourced_ids
63
+ @preferred_given_name = preferred_given_name
64
+ @preferred_middle_name = preferred_middle_name
65
+ @preferred_family_name = preferred_family_name
66
+ @primary_org_sourced_id = primary_org_sourced_id
67
+ @pronouns = pronouns
68
+ @extension_fields = extension_fields
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class UserProfile
5
+ DataModel.define(
6
+ self,
7
+ attribute_name_to_header_field_map: {
8
+ sourced_id: 'sourcedId',
9
+ status: 'status',
10
+ date_last_modified: 'dateLastModified',
11
+ user_sourced_id: 'userSourcedId',
12
+ profile_type: 'profileType',
13
+ vendor_id: 'vendorId',
14
+ application_id: 'applicationId',
15
+ description: 'description',
16
+ credential_type: 'credentialType',
17
+ username: 'username',
18
+ password: 'password'
19
+ },
20
+ converters: {
21
+ datetime: [:date_last_modified],
22
+ required: [:sourced_id, :user_sourced_id, :profile_type, :vendor_id, :credential_type, :username],
23
+ status: [:status]
24
+ }
25
+ )
26
+
27
+ def initialize(sourced_id:, status: nil, date_last_modified: nil, user_sourced_id:, profile_type:, vendor_id:, application_id: nil, description: nil, credential_type:, username:, password: nil, **extension_fields)
28
+ @sourced_id = sourced_id
29
+ @status = status
30
+ @date_last_modified = date_last_modified
31
+ @user_sourced_id = user_sourced_id
32
+ @profile_type = profile_type
33
+ @vendor_id = vendor_id
34
+ @application_id = application_id
35
+ @description = description
36
+ @credential_type = credential_type
37
+ @username = username
38
+ @password = password
39
+ @extension_fields = extension_fields
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class UserProfileSet < DataSet
5
+ def initialize(data, user_set:)
6
+ super(data)
7
+ @user_set = user_set
8
+ end
9
+
10
+ def check_semantically_consistent
11
+ super
12
+
13
+ each do |user_profile|
14
+ @user_set.find_by_sourced_id(user_profile.user_sourced_id)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class UserSet < DataSet
5
+ def initialize(data, organization_set:)
6
+ super(data)
7
+ @organization_set = organization_set
8
+ end
9
+
10
+ def check_semantically_consistent
11
+ super
12
+
13
+ each do |user|
14
+ if user.primary_org_sourced_id
15
+ @organization_set.find_by_sourced_id(user.primary_org_sourced_id)
16
+ end
17
+
18
+ user.agent_sourced_ids.each do |agent_sourced_id|
19
+ find_by_sourced_id(agent_sourced_id)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ VERSION = "0.1.0"
5
+ end
data/lib/meibo.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ loader = Zeitwerk::Loader.for_gem
5
+ loader.ignore("#{__dir__}/meibo/errors.rb")
6
+ loader.ignore("#{__dir__}/meibo/factory_bot")
7
+ loader.setup
8
+
9
+ require_relative "meibo/version"
10
+ require_relative "meibo/errors"
11
+
12
+ module Meibo
13
+ CSV_ENCODING = 'UTF-8'
14
+ ENUM_EXT_PATTERN = /\Aext:[a-zA-Z0-9\.\-_]+\z/
15
+ end
16
+
17
+ loader.eager_load
data/meibo.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/meibo/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "meibo"
7
+ spec.version = Meibo::VERSION
8
+ spec.authors = ["Seiei Miyagi"]
9
+ spec.email = ["hanachin@gmail.com"]
10
+
11
+ spec.summary = "Meibo is a ruby library for OneRoster CSV Binding Japan Profile."
12
+ spec.homepage = "https://github.com/hanachin/meibo"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.7.0"
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/hanachin/meibo"
20
+ spec.metadata["changelog_uri"] = "https://github.com/hanachin/meibo/blob/meibo/v#{Meibo::VERSION}/CHANGELOG.md"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "rubyzip"
34
+ spec.add_dependency "zeitwerk"
35
+
36
+ # For more information and examples about making a new gem, check out our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ end
data/sig/meibo.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Meibo
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meibo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Seiei Miyagi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-11-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - hanachin@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rspec"
49
+ - ".rubocop.yml"
50
+ - CHANGELOG.md
51
+ - CODE_OF_CONDUCT.md
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - lib/meibo.rb
58
+ - lib/meibo/academic_session.rb
59
+ - lib/meibo/academic_session_set.rb
60
+ - lib/meibo/base_profile.rb
61
+ - lib/meibo/builder.rb
62
+ - lib/meibo/builder/academic_session_builder.rb
63
+ - lib/meibo/builder/base_builder.rb
64
+ - lib/meibo/builder/classroom_builder.rb
65
+ - lib/meibo/builder/course_builder.rb
66
+ - lib/meibo/builder/demographic_builder.rb
67
+ - lib/meibo/builder/enrollment_builder.rb
68
+ - lib/meibo/builder/organization_builder.rb
69
+ - lib/meibo/builder/role_builder.rb
70
+ - lib/meibo/builder/user_builder.rb
71
+ - lib/meibo/builder/user_profile_builder.rb
72
+ - lib/meibo/classroom.rb
73
+ - lib/meibo/classroom_set.rb
74
+ - lib/meibo/converter.rb
75
+ - lib/meibo/course.rb
76
+ - lib/meibo/course_set.rb
77
+ - lib/meibo/data_model.rb
78
+ - lib/meibo/data_set.rb
79
+ - lib/meibo/demographic.rb
80
+ - lib/meibo/demographic_set.rb
81
+ - lib/meibo/enrollment.rb
82
+ - lib/meibo/enrollment_set.rb
83
+ - lib/meibo/errors.rb
84
+ - lib/meibo/factory_bot/academic_session.rb
85
+ - lib/meibo/factory_bot/all.rb
86
+ - lib/meibo/factory_bot/classroom.rb
87
+ - lib/meibo/factory_bot/course.rb
88
+ - lib/meibo/factory_bot/demographic.rb
89
+ - lib/meibo/factory_bot/enrollment.rb
90
+ - lib/meibo/factory_bot/manifest.rb
91
+ - lib/meibo/factory_bot/memory_package.rb
92
+ - lib/meibo/factory_bot/organization.rb
93
+ - lib/meibo/factory_bot/role.rb
94
+ - lib/meibo/factory_bot/user.rb
95
+ - lib/meibo/factory_bot/user_profile.rb
96
+ - lib/meibo/japan_profile.rb
97
+ - lib/meibo/japan_profile/academic_session.rb
98
+ - lib/meibo/japan_profile/classroom.rb
99
+ - lib/meibo/japan_profile/course.rb
100
+ - lib/meibo/japan_profile/demographic.rb
101
+ - lib/meibo/japan_profile/enrollment.rb
102
+ - lib/meibo/japan_profile/organization.rb
103
+ - lib/meibo/japan_profile/role.rb
104
+ - lib/meibo/japan_profile/user.rb
105
+ - lib/meibo/japan_profile/user_profile.rb
106
+ - lib/meibo/manifest.rb
107
+ - lib/meibo/manifest/processing_mode.rb
108
+ - lib/meibo/organization.rb
109
+ - lib/meibo/organization_set.rb
110
+ - lib/meibo/reader.rb
111
+ - lib/meibo/role.rb
112
+ - lib/meibo/role_set.rb
113
+ - lib/meibo/roster.rb
114
+ - lib/meibo/user.rb
115
+ - lib/meibo/user_profile.rb
116
+ - lib/meibo/user_profile_set.rb
117
+ - lib/meibo/user_set.rb
118
+ - lib/meibo/version.rb
119
+ - meibo.gemspec
120
+ - sig/meibo.rbs
121
+ homepage: https://github.com/hanachin/meibo
122
+ licenses:
123
+ - MIT
124
+ metadata:
125
+ allowed_push_host: https://rubygems.org/
126
+ homepage_uri: https://github.com/hanachin/meibo
127
+ source_code_uri: https://github.com/hanachin/meibo
128
+ changelog_uri: https://github.com/hanachin/meibo/blob/meibo/v0.1.0/CHANGELOG.md
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 2.7.0
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 3.2.26
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Meibo is a ruby library for OneRoster CSV Binding Japan Profile.
148
+ test_files: []