如何在Ruby on Rails中通过OOP方式合并department与person的CSV数据
Ruby on Rails 多CSV合并OOP实现方案
1. 定义基础领域类
首先明确Department和Person的职责,只封装自身属性和相关业务方法:
# app/models/department.rb class Department attr_reader :name, :students, :teachers # 支持默认空数组传入students、teachers参数 def initialize(name, midterm_score_ratio, final_score_ratio, minimum_passing_score, university_name, students = [], teachers = []) @name = name @midterm_score_ratio = midterm_score_ratio.to_f @final_score_ratio = final_score_ratio.to_f @minimum_passing_score = minimum_passing_score.to_i @university_name = university_name @students = students @teachers = teachers end def print_department_name puts "院系名称:#{@name}" end # 其他院系相关业务方法可在这里扩展 end
# app/models/person.rb class Person attr_reader :name, :role, :department_name def initialize(name, role, department_name, **other_attributes) @name = name @role = role # 字段值约定为 student 或 teacher @department_name = department_name # 学号、工号等其他属性可自行扩展接收 end end
如果需要区分学生、教师的不同行为,可以继承Person生成Student、Teacher子类。
2. 封装CSV合并导入服务
遵循单一职责原则,单独封装服务类处理CSV读取、数据关联逻辑,不要和领域类逻辑耦合:
# app/services/csv_merger_service.rb require 'csv' class CsvMergerService def initialize(department_csv_path, person_csv_path) @department_csv_path = department_csv_path @person_csv_path = person_csv_path # 人员分组缓存结构:{ "数学系" => { students: [], teachers: [] } } @person_group_cache = {} end # 对外暴露的执行入口,返回所有关联完成的Department实例数组 def execute load_and_group_persons load_departments_with_persons end private # 先读取所有人员数据,按院系+角色分组缓存,避免重复遍历 def load_and_group_persons CSV.foreach(@person_csv_path, headers: true) do |row| person = Person.new( row["name"], row["role"], row["department_name"] # 其他CSV字段按需传入即可 ) # 初始化对应院系的分组容器 @person_group_cache[person.department_name] ||= { students: [], teachers: [] } # 按角色归类 if person.role == "student" @person_group_cache[person.department_name][:students] << person elsif person.role == "teacher" @person_group_cache[person.department_name][:teachers] << person end end end # 读取院系CSV,直接从缓存取对应人员生成实例 def load_departments_with_persons departments = [] CSV.foreach(@department_csv_path, headers: true) do |row| # 没有对应人员的院系默认返回空数组 dept_persons = @person_group_cache.fetch(row["name"], { students: [], teachers: [] }) department = Department.new( row["name"], row["midterm_score_ratio"], row["final_score_ratio"], row["minimum_passing_score"], row["university_name"], dept_persons[:students], dept_persons[:teachers] ) department.print_department_name departments << department end departments end end
3. 调用示例
直接替换你原来的CSV读取逻辑即可:
dept_csv_path = "#{__dir__}/../csv/department.csv" person_csv_path = "#{__dir__}/../csv/person.csv" # 执行后拿到所有关联完成的院系实例 all_departments = CsvMergerService.new(dept_csv_path, person_csv_path).execute
扩展说明
- 如果是Rails ActiveRecord持久化场景,把领域类的
new方法替换为find_or_initialize_by/create即可,整体逻辑不需要修改 - 后续如果要新增其他CSV关联、调整字段映射规则,只需要修改对应服务类方法,不需要改动业务逻辑代码
- 结构可测试性高:可以单独给服务类写单元测试,模拟CSV输入验证关联逻辑是否正确
内容的提问来源于stack exchange,提问作者Alper Efe Sahin
相关产品推荐
相关产品推荐

