class ActiveZuora::Generator

Attributes

classes[R]
document[R]

Public Class Methods

new(document, options={}) click to toggle source
# File lib/active_zuora/generator.rb, line 6
def initialize(document, options={})
  # document is a parsed wsdl document.
  @document = document
  @classes = []
  @class_nesting = options[:inside] || ActiveZuora
  @class_nesting.const_set("CollectionProxy", CollectionProxy) unless @class_nesting.constants.include?(:CollectionProxy)
end

Public Instance Methods

generate_classes() click to toggle source
# File lib/active_zuora/generator.rb, line 14
def generate_classes

  # Defines the classes based on the wsdl document.
  # Assumes the following namespaces in the wsdl.
  # xmlns="http://schemas.xmlsoap.org/wsdl/"
  # xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
  # xmlns:xs="http://www.w3.org/2001/XMLSchema"
  # xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  # xmlns:zns="http://api.zuora.com/"
  # xmlns:ons="http://object.api.zuora.com/"
  # xmlns:fns="http://fault.api.zuora.com/"
  @document.xpath('.//xs:schema[@targetNamespace]').each do |schema|
    namespace = schema.attribute("targetNamespace").value

    schema.xpath('.//xs:complexType[@name]').each do |complex_type|
      class_name = complex_type.attribute("name").value
      # Skip the zObject base class, we define our own.
      next if class_name == "zObject"

      class_name[0] = class_name[0].upcase
      zuora_class = Class.new
      @class_nesting.const_set(class_name, zuora_class)
      @classes << zuora_class

      # Include the Base module for adding fields.
      zuora_class.send :include, Base
      zuora_class.namespace = namespace

      # If it's a zObject, include that module as well.
      if complex_type.xpath(".//xs:extension[@base='ons:zObject']").any?
        zuora_class.send :include, ZObject
      end

      # Define the fields
      complex_type.xpath('.//xs:element[@name][@type]').each do |element|
        # attributes: name, type, nillable, minoccurs, maxoccurs
        zuora_name = element.attribute("name").value
        field_name = zuora_name.underscore
        field_type = element.attribute("type").value
        is_array = element_is_an_array?(element)

        case field_type
        when "string", "xs:string", "zns:ID", "xs:base64Binary"
          zuora_class.field field_name, :string,
            :zuora_name => zuora_name, :array => is_array
        when "boolean", "xs:boolean"
          zuora_class.field field_name, :boolean,
            :zuora_name => zuora_name, :array => is_array
        when "int", "short", "long", "xs:int"
          zuora_class.field field_name, :integer,
            :zuora_name => zuora_name, :array => is_array
        when "decimal"
          zuora_class.field field_name, :decimal,
            :zuora_name => zuora_name, :array => is_array
        when "date"
          zuora_class.field field_name, :date,
            :zuora_name => zuora_name, :array => is_array
        when "dateTime"
          zuora_class.field field_name, :datetime,
            :zuora_name => zuora_name, :array => is_array
        when /\A(zns:|ons:)/
          zuora_class.field field_name, :object,
            :zuora_name => zuora_name, :array => is_array,
            :class_name => zuora_class.nested_class_name(field_type.split(':').last)
        else
          puts "Unknown field type: #{field_type}"
        end
      end # each element

    end # each complexType
  end # each schema

  add_obvious_associations
  add_extra_customizations
end

Private Instance Methods

add_extra_customizations() click to toggle source
# File lib/active_zuora/generator.rb, line 115
def add_extra_customizations
  # We can't know everything from the wsdl, like which fields are
  # usable in queries.  This function does some final customizations
  # based on the existing Zuora documentation.
  # Also, it's possible classes customized here weren't defined
  # in your wsdl, so it will only customize them if they are defined.

  nesting = @class_nesting

  customize 'Account' do
    belongs_to :bill_to, :class_name => nested_class_name('Contact') if field? :bill_to_id
    belongs_to :default_payment_method, :class_name => nested_class_name('PaymentMethod') if field? :default_payment_method_id
    if field? :parent_id
      belongs_to :parent, :class_name => nested_class_name('Account')
      has_many :children, :class_name => nested_class_name('Account'), :foreign_key => :parent_id, :inverse_of => :parent
    end
    belongs_to :sold_to, :class_name => nested_class_name('Contact') if field? :sold_to_id
    validates :currency, :presence => true if field? :currency
    validates :name, :presence => true if field? :name
    validates :status, :presence => true if field? :status
  end

  customize 'Amendment' do
    exclude_from_queries :rate_plan_data,
      :destination_account_id, :destination_invoice_owner_id
  end

  customize 'AmendRequest' do
    include Amend
  end

  customize 'Import' do
    exclude_from_queries :file_content
  end

  customize 'Invoice' do
    include Generate
    include LazyAttr
    # The body field can only be accessed for a single invoice at a time, so
    # exclude it here to not break collections of invoices. The contents of
    # the body field will be lazy loaded in when needed.
    exclude_from_queries :regenerate_invoice_pdf, :body, :bill_run_id
    lazy_load :body
  end

  customize 'InvoiceItem' do
    exclude_from_queries :product_rate_plan_charge_id
  end

  customize 'BillingPreviewRequest' do
    include BillingPreview
  end

  customize 'InvoiceItemAdjustment' do
    exclude_from_queries :customer_name, :customer_number
  end

  customize 'Payment' do
    exclude_from_queries :applied_invoice_amount,
      :gateway_option_data, :invoice_id, :invoice_number, :invoice_payment_data
  end

  customize 'PaymentMethod' do
    exclude_from_queries :ach_account_number, :bank_transfer_account_number,
      :credit_card_number, :credit_card_security_code, :gateway_option_data,
      :second_token_id, :skip_validation, :token_id
  end

  customize 'ProductRatePlan' do
    include LazyAttr
    exclude_from_queries :active_currencies
    lazy_load :active_currencies
  end

  customize 'ProductRatePlanCharge' do
    exclude_from_queries :product_rate_plan_charge_tier_data, :revenue_recognition_rule_name, :deferred_revenue_account, :recognized_revenue_account, :product_discount_apply_detail_data
  end

  customize 'Usage' do
    exclude_from_queries :ancestor_account_id, :invoice_id, :invoice_number
  end

  customize 'RatePlanCharge' do
    include LazyAttr
    exclude_from_queries :overage_price, :included_units,
      :discount_amount, :discount_percentage, :rollover_balance, :price, :revenue_recognition_rule_name
    lazy_load :price
  end

  customize 'Refund' do
    exclude_from_queries :gateway_option_data, :payment_id
  end

  customize 'Subscription' do
    exclude_from_queries :ancestor_account_id
    belongs_to :creator_account, :class_name => nested_class_name('Account') if field? :creator_account_id
    belongs_to :creator_invoice_owner, :class_name => nested_class_name('Account') if field? :creator_invoice_owner_id
    belongs_to :invoice_owner, :class_name => nested_class_name('Account') if field? :invoice_owner_id
    belongs_to :original, :class_name => name if field? :original_id
    belongs_to :original_subscription, :class_name => name if field? :original_subscription_id
    belongs_to :previous_subscription, :class_name => name if field? :previous_subscription_id
  end

  customize 'SubscribeRequest' do
    include Subscribe
  end
end
add_obvious_associations() click to toggle source
# File lib/active_zuora/generator.rb, line 92
def add_obvious_associations
  # When a zuora class has a field called InvoiceId, it's a safe
  # assuption that it references the an Invoice.
  # Build those associations automatically.
  @classes.each do |zuora_class|
    zuora_class.fields.each do |field|
      # If it looks like an Id field and the name
      # matches a generated ZObject class
      if match = field.zuora_name.match(/\A(.+?)Id\Z/)
        if zobject_class = zobject_class_with_name(match[1])
          # Add a belongs to relationship.
          zuora_class.belongs_to zobject_class.zuora_object_name.underscore
          # If the current class is also a ZObject, add a has_many
          # to the referenced class.
          if zuora_class < ZObject
            zobject_class.has_many zuora_class.zuora_object_name.underscore.pluralize
          end
        end
      end
    end
  end
end
attribute_is_more_than_one?(attribute) click to toggle source
# File lib/active_zuora/generator.rb, line 235
def attribute_is_more_than_one?(attribute)
  attribute && ( attribute.value == "unbounded" || attribute.value.to_i > 1 )
end
customize(zuora_class_name, &block) click to toggle source
# File lib/active_zuora/generator.rb, line 223
def customize(zuora_class_name, &block)
  if @class_nesting.const_defined?(zuora_class_name)
    @class_nesting.const_get(zuora_class_name).instance_eval(&block)
  end
end
element_is_an_array?(element) click to toggle source
# File lib/active_zuora/generator.rb, line 230
def element_is_an_array?(element)
  attribute_is_more_than_one?(element.attribute("minOccurs")) ||
    attribute_is_more_than_one?(element.attribute("maxOccurs"))
end
zobject_class_with_name(name) click to toggle source
# File lib/active_zuora/generator.rb, line 239
def zobject_class_with_name(name)
  @classes.find { |zuora_class| zuora_class.zuora_object_name == name && zuora_class < ZObject }
end