solnic / virtus

[DISCONTINUED ] Attributes on Steroids for Plain Old Ruby Objects
MIT License
3.77k stars 229 forks source link

Map attribute to a JSON attribute with different name #319

Closed lenart closed 9 years ago

lenart commented 9 years ago

Sorry for opening an issue but I cannot find this anywhere.

I'm working with an API that uses camelcased attributes (objectId, createdAt, etc.). Is it possible to map this to underscored version of attribute in my model?

The problem is that now I have to use attribute :objectId inside my model and access the value through product.objectId which looks weird in my ruby code. I imagine something like:

class Product
  include Virtus.model

  attribute :name, String
  # remote API sends createdAt but we want to use attribute as created_at
  attribute :created_at, String, map_to: 'createdAt'
end
nddeluca commented 9 years ago

I'm using Virtus in a similar scenario and opted to convert the attributes (using a hash extension - https://gist.github.com/nddeluca/44e07faac0b922c94a20) to underscore before passing the attributes to Virtus. This way, my virtus models stay clean and unaware of whether the external api uses camelcase or underscore attributes.

response = ... # Some GET request
product_attributes = JSON.parse(response.body) # returns a Hash
product = Product.new(product_attributes.to_underscore)

The reverse is also possible

product = Product.new(...)
product_attributes = product.as_json
JSON.generate(product_attributes.to_camel_case)
lenart commented 9 years ago

Thanks a lot @nddeluca. Seems like a good way to tackle such problem without having Virtus to worry about this.

solnic commented 9 years ago

For data mapping I'd recommend transproc that's used as a mapping backend for ROM. It's possible it'll be used in Virtus 2.0 too.

Virtus could be used as a mapper right now but IMO it's not feasible, attribute DSL is not the best way of expressing data mapping and complex transformations even though in simple cases like the one above it may seem to make perfect sense.

In example your attributes could be transformed via transproc like that:

require 'transproc/hash'

include Transproc::Helper # gives us `t` shortcut

transform = t(:map_key, :name, t(:to_string)) >> t(:map_hash, 'createdAt' => :created_at)

transform[name: :Jane, 'createdAt' => 'foo']
# => {:name=>"Jane", :created_at=>"foo"}