首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用两个Ruby库使用继承

使用两个Ruby库使用继承
EN

Code Review用户
提问于 2018-12-12 15:57:13
回答 1查看 63关注 0票数 2

目前,我正致力于将数据从表单服务解析为pdf表单。我创建了两个类,一个继承了另一个类。但是,我看到我创建的类在代码行中不断增长,我担心这些类变得很难维护。

我想征求社区对OOP原则、风格违规和最佳实践的反馈意见。如果也存在安全问题,就指出这些问题。下面是使用继承的类。

代码语言:javascript
复制
require 'open-uri'

class FillablePdfForm
  attr_writer :template_path
  attr_reader :attributes

  def initialize
    fill_out
  end

  def export(file_name, output_file_path: nil)
    output_file_path ||= "#{Rails.root}/tmp/pdfs/#{application_date}_#{which_application}_#{what_status}_#{file_name}.pdf"
    pdftk.fill_form template_path, output_file_path, attributes
    output_file_path
  end

  def form_fields
    pdftk.get_field_names template_path
  end

  def template_path
    pdf_form_application
    @template_path ||=
      "#{Rails.root}/public/pdfs/#{which_application.downcase}_#{what_status.downcase}.pdf"
  end

  protected

  def application_date
    Time.now.strftime('%Y-%m-%d')
  end

  def pdf_form_application
    File.open("#{Rails.root}/public/pdfs/#{which_application.downcase}_#{what_status.downcase}.pdf", "wb") do |file|
      file << URI.parse(url_of_pdf_form).read
    end
  end

  def url_of_pdf_form
    @form_fields.find do |field|
      field['label'] == "#{which_application}_#{what_status}_URL"
    end['default']
  end

  def attributes
    @attributes ||= {}
  end

  def fill(key, value)
    attributes[key.to_s] = value
  end

  def pdftk
    @pdftk ||= PdfForms.new
  end

  def fill_out
    raise 'Must be overridden by child class'
  end
end

另外,我正在传递构造函数FormStack::Form.new,但我想知道是否应该将它作为参数传递。

代码语言:javascript
复制
class PdfScrie < FillablePdfForm
  def initialize(user_submission_data, form_fields)
    @user_submission_data = user_submission_data
    @form_fields = form_fields
    @formstack = FormStack::Form.new
    super()
  end

  private

  # PDF Constants
  VALUE_CHECKBOX_ON = 'On'.freeze
  OPTION_SEP = ' | '.freeze
  LABEL_APPLICATION = 'APPLICATION'.freeze
  LABEL_STATUS = 'STATUS'.freeze

  def fill_out
    form_fields.each do |field| # PDF form fields
      unless dictionary[field]
        Rails.logger.warn "#{self.class.name}: Missing \"#{field}\" mapping."
        next
      end

      id = dictionary[field].split(OPTION_SEP)[0]

      @user_submission_data
        .select { |field_data| field_data[FormStack::Form::ATTR_FIELD_ID] == id }
        .each { |field_data| fill_form_with_data(field, field_data) }
    end
  end

  def fill_form_with_data(field, field_data)
    field_number = field_data[FormStack::Form::ATTR_FIELD_ID]
    value = field_data[FormStack::Form::ATTR_FIELD_VALUE]
    field_type = FormStack::Form::field_type(@form_fields, field_number)
    self_method = "fill_#{field_type}".to_sym

    if self.respond_to?(self_method, :include_private)
      send(self_method, field_number, field, value)
    else
      fill(field, value)
    end
  end

  # Field Type Methods

  def fill_address(field_number, field, value)
    address_by_section = FormStack::Form.parse_formstack_nested_attrs(value)
    address_by_section.each do |section, value|
      fill(field, value) if form_field_has_section?(field, section) ||
        FormStack::Form::address_section_aparment?(field, section)
    end
  end

  def fill_phone(field_number, field, value)
    parse_phone_number(value)
    fill(field, @phone_number_sections.shift)
  end

  def fill_name(field_number, field, value)
    full_name = FormStack::Form::parse_name(value)
    fill(field, full_name)
  end

  def fill_checkbox(field_number, field, value)
    if FormStack::Form::field_is_grouped_checkbox(@form_fields, field_number)
      FormStack::Form::parse_checked_options(value).each do |option|
        fill(field, VALUE_CHECKBOX_ON) if checked_option_matches_value(field, option)
      end
    else
      fill(field, value)
    end
  end

  # END Field Type Methods

  # Helpers

  def checked_option_matches_value(field, option)
    dictionary[field].split(OPTION_SEP)[1].include?(option)
  end

  def parse_phone_number(phone_number)
    if phone_number_sections_empty?
      @phone_number_sections = FormStack::Form::parse_phone(phone_number)
    end
  end

  def phone_number_sections_empty?
    @phone_number_sections.nil? || @phone_number_sections.empty?
  end

  def form_field_has_section?(form_field_name, address_section)
    form_field_name.include? address_section.upcase
  end

  def dictionary
    @dictionary ||= JSON.parse(find_dictionary['section_text'])
  end

  def find_dictionary
    @formstack.find_field_by_label("#{which_application}_#{what_status}_DICTIONARY",
                                   @form_fields)
  end

  def which_application
    @formstack.find_value_by_label(LABEL_APPLICATION,
                                   @form_fields,
                                   @user_submission_data)
  end

  def what_status
    @formstack
      .find_value_by_label(LABEL_STATUS, @form_fields, @user_submission_data)
  end
end

可以自由地指出改进、反馈、最佳实践和资源的领域。

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-03-03 11:12:51

以类比的方式提出的客观建议:

考虑一下DocumentCabinetDocument应该知道如何将自己的副本放在Cabinet中吗?或者Cabinet应该知道如何将文档复制到自己中?

在一个小程序中,两者都可能是好的。然而,随着系统变得越来越复杂,随着更多的交互方式的增加,它将变得不可维护。

当出现这种情况时,应该有一个处于更高抽象级别的参与者,例如“秘书”,它复制一个副本(可能通过Document#copy请求)并将副本归档到内阁(也许是通过请求Cabinet#file的副本)。在各自的孤立上下文中,它们不需要交互或相互了解,因此它们的实现将不包含对彼此的引用。

如果只有“一个秘书”,就把行动放在最高级的抽象上--主程序。随着复杂性的增加,也许可以定义一个Secretary类。

然而,请记住,Secretary的S行为是更高的抽象,而Document是一个较低的抽象。依赖方向性很重要。Document不应该强迫Secretary去行动。

适用于您的代码的

  1. export
    • FillablePdfForm是文档
    • PdfForms是内阁
    • 问题:FillablePdfForm#export是把自己放进内阁的文件

  2. fill_form_with_data
    • field_dataFormStack::Form是文档
    • PdfScrie是内阁
    • 问题:PdfScrie#fill_form_with_data是内阁把文件放在自己的地方

顺便说一句,这个概念是D in 实心

另一个问题是FillablePdfForm#template_path在哪里调用which_application,它是在Scrie子类中实现的,L在实心中讨论了这个问题。

虽然维基百科的文章有点厚,但谷歌到处寻找每一条坚实原则的替代解释。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/209536

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档