Flattener

Allows to flat an object (ActiveRecord based) into one single level, so creating a form for the object becomes really simple. See usage for more details, or create an issue.

Installation

Add this line to your application's Gemfile:

gem 'flattener'

And then execute:

$ bundle

Or install it yourself as:

$ gem install flattener

Usage

Say you have something like this:

class Customer < ActiveRecord::Base
  has_one :address
end

class Address < ActiveRecord::Base
  # ... Attributes here
end

You could do something like this:

class Customer < ActiveRecord::Base

  include Flattener

  has_one :address

  flat :address do
    attribute :address_line_1
    attribute :address_line_2
    attribute :city
    attribute :state
    attribute :country
  end

end

and use it like this:

customer = Customer.new address_line_1: "Av. Santa Fe 1234", country: "Argentina"

Pretty simple. It support all the basic AR methods, like valid?, save or create.

I know what you think. "I can do that with a simple delegate call". Indeed, but you can also do this:

class Payment < ActiveRecord::Base

  include Flattener

  has_one :payment_profile

  flat :payment_profile do
    attributes :credit_card, :expiration_date
    flat :address, build: proc { proxy_payment_profile.build_address } do
      attribute :address_line_1
      attribute :address_line_2
    end

  end

end

And this way getting something really simple:

payment = Payment.new amount: "23.00", payment_profile_credit_card: "XXXXXXXXXX", expiration_date: "2011/12", 
  payment_profile_address_line_1: "Av. Mongo 1234"

payment.payment_profile_address_line_1 #=> "Av. Mongo 1234"
# and so on. 

# supose you have a validation on Address#address_line_2
payment.valid? #=> false
payment.errors[:payment_profile_address_line_1] #=> ".... is required, blah..."

Prefix is optional, and changeable:

class Payment < ActiveRecord::Base

  include Flattener

  has_one :payment_profile

  flat :payment_profile, prefix: "pp" do
    attributes :credit_card, :expiration_date
  end
end

payment = Payment.new
payment.pp_credit_card = "XXXX-XXXX-XXXX-XXXX"
payment.pp_credit_card # => "XXXX-XXXX-XXXX-XXXX"

You can use this also as a presenter (or something like that):

class PaymentBuilder < Flattener::Base

  attr_accessor :payment_profile

  flat :payment_profile, 
    prefix: "", 
    build: proc { self.payment_profile = PaymentProfile.new } do
      attributes :credit_card, :expiration_date
  end
end

payment = PaymentBuilder.new :credit_card => "XXXX-XXXX-XXXX-XXXX"
payment.credit_card # => "XXXX-XXXX-XXXX-XXXX"

You wonder why is this useful? May be you have something really complex on your app, but you want to expose it (to the view) in a simpler way, and only allowing some properties.

So what is this gem doing?

  • It builds the flattened objects automatically, or the way you want (configuration option).
  • It overwrites the AR methods in order to call the flattened methods before, so the right validation errors appear in place.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request