Giter Site home page Giter Site logo

trailblazer / representable Goto Github PK

View Code? Open in Web Editor NEW

This project forked from empact/roxml

688.0 688.0 107.0 3.2 MB

Maps representation documents from and to Ruby objects. Includes JSON, XML and YAML support, plain properties and compositions.

Home Page: http://trailblazer.to/2.1/docs/representable.html

License: MIT License

Ruby 100.00%
json-parser json-serialization xml-parser xml-serialization yaml

representable's Introduction

Trailblazer

Battle-tested Ruby framework to help structuring your business logic.

Gem Version

What's Trailblazer?

Trailblazer introduces new abstraction layers into Ruby applications to help you structure your business logic.

It ships with our canonical "service object" implementation called operation, many conventions, gems for testing, Rails support, optional form objects and much more.

Should I use Trailblazer?

Give us a chance if you say "yes" to this!

  • You hate messy controller code but don't know where to put it?
  • Moving business code into the "fat model" gives you nightmares?
  • "Service objects" are great?
  • Anyhow, you're tired of 12 different "service object" implementations throughout your app?
  • You keep asking for additional layers such as forms, policies, decorators?

Yes? Then we got a well-seasoned framework for you: Trailblazer.

Here are the main concepts.

Operation

The operation encapsulates business logic and is the heart of the Trailblazer architecture.

An operation is not just a monolithic replacement for your business code. It's a simple orchestrator between the form objects, models, your business code and all other layers needed to get the job done.

# app/concepts/song/operation/create.rb
module Song::Operation
  class Create < Trailblazer::Operation
    step :create_model
    step :validate
    left :handle_errors
    step :notify

    def create_model(ctx, **)
      # do whatever you feel like.
      ctx[:model] = Song.new
    end

    def validate(ctx, params:, **)
      # ..
    end
    # ...
  end
end

The step DSL takes away the pain of flow control and error handling. You focus on what happens: creating models, validating data, sending out notifications.

Control flow

The operation takes care when things happen: the flow control. Internally, this works as depicted in this beautiful diagram.

Flow diagram of a typical operation.

The best part: the only way to invoke this operation is Operation.call. The single entry-point saves programmers from shenanigans with instances and internal state - it's proven to be an almost bullet-proof concept in the past 10 years.

result = Song::Operation::Create.(params: {title: "Hear Us Out", band: "Rancid"})

result.success? #=> true
result[:model]  #=> #<Song title="Hear Us Out" ...>

Data, computed values, statuses or models from within the operation run are exposed through the result object.

Operations can be nested, use composition and inheritance patterns, provide variable mapping around each step, support dependency injection, and save you from reinventing the wheel - over and over, again.

Leveraging those functional mechanics, operations encourage a high degree of encapsulation while giving you all the conventions and tools for free (except for a bit of a learning curve).

Tracing

In the past years, we learnt from some old mistakes and improved developer experience. As a starter, check out our built-in tracing!

result = Song::Operation::Create.wtf?(params: {title: "", band: "Rancid"})

Tracing the internal flow of an operation.

Within a second you know which step failed - a thing that might seem trivial, but when things grow and a deeply nested step in an iteration fails, you will start loving #wtf?! It has saved us days of debugging.

We even provide a visual debugger to inspect traces on the webs.

There's a lot more

All our abstraction layers such as operations, form objects, view components, test gems and much more are used in hundreds of OSS projects and commercial applications in the Ruby world.

We provide a visual debugger, a BPMN editor for long-running business processes, thorough documentation and a growing list of onboarding videos (TRAILBLAZER TALES).

Trailblazer is both used for refactoring legacy apps (we support Ruby 2.5+) and helps big teams organizing, structuring and debugging modern, growing (Rails) applications.

Documentation

Make sure to check out the new beginner's guide to learning Trailblazer. The new book discusses all aspects in a step-wise approach you need to understand Trailblazer's mechanics and design ideas.

The new begginer's guide.

representable's People

Contributors

abinoam avatar and0x000 avatar apotonick avatar d4rky-pl avatar dblock avatar dplummer avatar empact avatar faucct avatar fran-worley avatar guiocavalcanti avatar jandudulski avatar luxflux avatar mmartinson avatar myabc avatar nakajima avatar nashby avatar parndt avatar richardboehme avatar rsutphin avatar sadjow avatar scharfie avatar seuros avatar sheax0r avatar shirren avatar timoschilling avatar toadle avatar tylerrick avatar vihai avatar yob avatar yogeshjain999 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

representable's Issues

Virtus Coercion fails when used in conjunction with nested

I am sorry I don't know how to fix this. I can however provide a failing test for you.

describe "on class level" do
  class ImmigrantSong
    include Representable::JSON
    include Representable::Coercion

    nested 'length' do
      property :duration,   :type => Integer
    end

    attr_accessor :duration
  end

  it "works with nested" do
    song = ImmigrantSong.new.from_json('{"length":{"duration":"420"}}')
    song.duration.must_equal 420
  end
end

This will return

Expected: 420
Actual: "420"

"on object level" and "on class level" both appear to suffer from the same bug.

Thanks ๐Ÿ‘
Karl

JRuby: RuntimeError: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.

Been trying to add JRuby support. Tests fail with the following:

  1) Error:
PropertyBinding::with an object#test_0002_inserts with #write:
RuntimeError: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
    nokogiri/XmlNode.java:1640:in `add_child_node'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:973:in `add_child_node_and_reparent_attrs'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:275:in `add_child'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/document.rb:242:in `add_child'
    /Users/dblock/source/representable/dblock/lib/representable/bindings/xml_bindings.rb:25:in `write'
    /Users/dblock/source/representable/dblock/test/xml_bindings_test.rb:52:in `test_0002_inserts with #write'


  2) Error:
PropertyBinding::with an object and :extend#test_0002_inserts with #write:
RuntimeError: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
    nokogiri/XmlNode.java:1640:in `add_child_node'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:973:in `add_child_node_and_reparent_attrs'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:275:in `add_child'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/document.rb:242:in `add_child'
    /Users/dblock/source/representable/dblock/lib/representable/bindings/xml_bindings.rb:25:in `write'
    /Users/dblock/source/representable/dblock/test/xml_bindings_test.rb:67:in `test_0002_inserts with #write'


  3) Error:
CollectionBinding::with objects#test_0002_inserts with #write:
RuntimeError: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.
    nokogiri/XmlNode.java:1640:in `add_child_node'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:973:in `add_child_node_and_reparent_attrs'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/node.rb:275:in `add_child'
    /Users/dblock/.rvm/gems/jruby-1.7.9/gems/nokogiri-1.6.2.1-java/lib/nokogiri/xml/document.rb:242:in `add_child'
    /Users/dblock/source/representable/dblock/lib/representable/bindings/xml_bindings.rb:25:in `write'
    /Users/dblock/source/representable/dblock/test/xml_bindings_test.rb:101:in `test_0002_inserts with #write'

I couldn't figure out a way to fix this, too many to_node and other indirections :)

I have a branch with the beginning of JRuby support changes, just minor stuff.

Decorator Use Case: JSON String to JSON String + Nesting

Hey Nick,

I was working on a use case where I have a JSON string, need to manipulate the object, and then convert it back to a JSON string via the Decorator.

I can achieve it in the first two cases, but I was hoping to achieve it via Example 3.
Was it intended for this use? or is Example 2 the correct way to use the library?

***Follow up, I'm also trying to nest objects and create them automatically from JSON, but having some issues:

require 'json'
require 'ostruct'
require 'representable/json'

class SDate < OpenStruct
end

class SDateDecorator < Representable::Decorator
  include Representable::JSON

  property :created
  property :modified
  property :published
end

json = '{"created": "d1", "modified": "d2", "published": "d3"}'

# Example 1: Hash to JSON String
# prints (correct): {"created":"d1","modified":"d2","published":"d3"}
d1 = SDate.new(:created => "d1", :modified => "d2", :published => "d3")
puts SDateDecorator.new(d1).to_json 

# Example 2: JSON String to JSON String (succeeds)
# prints (correct): {"created":"d1","modified":"d2","published":"d3"}
d2 = SDate.new(JSON.parse(json))
puts SDateDecorator.new(d2).to_json 

# Example 3: JSON String to JSON String (fails)
# prints (incorrect): "#<SDate created=\"d1\", modified=\"d2\", published=\"d3\">"
dec = SDateDecorator.new(SDate.new).from_json(json)
puts dec.to_json 

# Followup: Having issues with nested objects from JSON
class Container < OpenStruct
end

class ContainerDecorator < Representable::Decorator
  include Representable::JSON

  self.representation_wrap= :content
  property :sid
  property :date, :class => SDate, :decorator => SDateDecorator
end

json = '{"sid": "512..", "date":{"created":"d1","modified":"d2","published":"d3"}}'
c = Container.new(JSON.parse(json))
puts ContainerDecorator.new(c).to_json
ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:70:in `block in get': undefined method `created' for {"created"=>"d1", "modified"=>"d2", "published"=>"d3"}:Hash (NoMethodError)
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:88:in `represented_exec_for'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:69:in `get'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:29:in `block in compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:88:in `represented_exec_for'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:28:in `compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:67:in `compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:35:in `serialize_property'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/feature/readable_writeable.rb:11:in `serialize_property'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:25:in `block in serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:24:in `each'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:24:in `serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable.rb:29:in `create_representation_with'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/hash.rb:38:in `to_hash'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/serializer.rb:18:in `serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/serializer.rb:10:in `call'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:112:in `serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/bindings/hash_bindings.rb:22:in `write'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:50:in `write_fragment_for'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:45:in `write_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:29:in `block in compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:88:in `represented_exec_for'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/binding.rb:28:in `compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:67:in `compile_fragment'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:35:in `serialize_property'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/feature/readable_writeable.rb:11:in `serialize_property'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:25:in `block in serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:24:in `each'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/mapper.rb:24:in `serialize'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable.rb:29:in `create_representation_with'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/hash.rb:38:in `to_hash'
    from /Users/zerocool/.rvm/gems/ruby-2.0.0-p353/gems/representable-1.7.3/lib/representable/json.rb:40:in `to_json'
    from test.rb:38:in `<main>'

Thanks for your help,

Rizwan

Bad generated json for lonely collections in development mode

I have the following representers defined in a Rails 4 application:

product_representer.rb:

require 'representable/json'

module ProductRepresenter
  include Representable::JSON

  property :id
  property :name
end

products_representer.rb:

require 'representable/json/collection'

module ProductsRepresenter
  include Representable::JSON::Collection

  items extend: ProductRepresenter, class: Product
end

product.rb:

class Product < ActiveRecord::Base
end

When I try #to_json in development mode I get the array inside a "_self" key:

$ rails console -e development

> [Product.new, Product.new].extend(ProductsRepresenter).to_json
=> "{\"_self\":[{},{}]}"

But in production I get the expected behaviour:

$ rails console -e production

> [Product.new, Product.new].extend(ProductsRepresenter).to_json
=> "[{},{}]"

This is driving me crazy because the production application is running ok, but I have this strange bug in development, do you have any idea what could be the problem?

Propagation of include and exclude

to_hash and to_json does not appear to propagate :include or :exclude options. Other user options do appear to however. Is this by design? It would be nice to be able to able to specify inclusions and exclusions for properties and nested properties.

class Song < OpenStruct; end
class Album < OpenStruct; end

module SongRepresenter
  include Representable::JSON

  property :title
  property :track
  property :length, :if => lambda { |opts| opts[:include_length] }
end

module AlbumRepresenter
  include Representable::JSON

  property :title
  collection :songs, :class => Song, :extend => SongRepresenter
end

album = Album.new(:title => 'Showbiz', :songs => [Song.new(:title => 'Sunburn', :track => 1, :length => '3:54')])
album.extend AlbumRepresenter

1.8 :030 > album.to_hash
 => {"songs"=>[{"track"=>1, "title"=>"Sunburn"}], "title"=>"Showbiz"} 
1.8 :031 > album.to_hash(:exclude => [:track])
 => {"songs"=>[{"track"=>1, "title"=>"Sunburn"}], "title"=>"Showbiz"} 
1.8 :032 > album.to_hash(:exclude => {:songs => [:track]})
 => {"title"=>"Showbiz"}
1.8 :033 > album.to_hash(:include_length => true)
 => {"songs"=>[{"track"=>1, "title"=>"Sunburn", "length"=>"3:54"}], "title"=>"Showbiz"}

Pretty JSON output?

Would it be possible to have pretty JSON output? As in:

1.9.3p194 :001 > require 'json'
 => true 
1.9.3p194 :002 > puts JSON.pretty_unparse(:foo => [{:bar => 'Hello world!'}])
{
  "foo": [
    {
      "bar": "Hello world!"
    }
  ]
}

Circular Dependencies

Is there a way in Representable to use symbols or strings in place of class names in order to avoid circular dependency issues? i.e. similar to Virtus.finalize?

YAML representer

module SongRepresenter
  ...
  collection :composers, :style => :inline

Segfault in mapper.rb on Ruby 2.0.0

We've managed to run into a segfault originating from https://github.com/apotonick/representable/blob/v1.7.7/lib/representable/mapper.rb#L58 on two occasions.

There is quite a lot of output, so I've pruned it down a little and put it into a gist here: https://gist.github.com/dwo/dd300c7e6c6145331b9f

For a bit of context

I haven't had a chance to dig into this further or reproduce it (it's only happened twice this week and I haven't put in more logging to track down the exact response causing the segfault).

I am wondering if it is really that line in representable causing the problem (and if so, why?). Any ideas?

License

Thanks for your work on this gem.
I was hoping you could clarify the license for this project.
Thanks!

Parse Vimeo

I am trying to parse Vimeo with the following code

require 'representable'
require 'representable/json'
require 'representable/json/collection'
require 'open-uri'
require 'ostruct'

class Vimeo < OpenStruct
end

class Video < OpenStruct
end


module VideoRepresenter
  include Representable::JSON

  property :title
end

module VimeoRepresenter
  include Representable::JSON::Collection

  items extend: VideoRepresenter, class: Video
end


json = open('http://vimeo.com/api/v2/video/92044309.json').read

song = Vimeo.new.extend(VimeoRepresenter).from_json(json)

I cannot figure this out. How can I parse a JSON array?

I am sorry to bother you and I could use the XML feed but I don't understand why the above code is not working. Its like I need support for an anonymous array....... (if that makes sense)

I'm probably just an idiot and not understanding the readme

Cheers lads! ๐Ÿ‘ฏ

Lonely for YAML

Should Lonely Collections and Lonely Arrays be supported for YAML too?

Creating new XML doc for every node leads to GC segfault

When creating a new node in Nokogiri using Nokogiri::XML::Node.new, it's necessary to pass a Nokogiri XML document in as the second parameter. This document reference acts as a kind of handle for the Nokogiri-to-libxml2 bridge so that Ruby can properly track which nodes need garbage collection and which do not.

Currently, Representable's XML implementation creates a new Nokogiri::XML::Document for each node (lib/representable/xml.rb:52):

    # Returns a Nokogiri::XML object representing this object.
    def to_node(options={})
      root_tag = options[:wrap] || representation_wrap

      create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, Nokogiri::XML::Document.new), options, PropertyBinding)
    end

As a result, when pushing the Representable implementation of XML to use larger documents, Ruby will segfault with higher and higher frequency. See sparklemotion/nokogiri#1007

What is the best way to pass a single once-instantiated Nokogiri::XML::Document object in to the to_node method?

Suggestion: Alias :from to :as in the accessor property

We were bit by this earlier today, and wanted to just ask if you'd be open to aliasing:

property :title, :from => :display_title

to this:

property :title, :as => :display_title

I know this was discussed in #4 but I think context was inverted. It seems the DSL is written in terms of a client, where I am using it in a rails app to create the json representations of my document. In this case it feels more natural (more rails-y) to use :as.

It would read more like: Display the property 'title' in the document as 'display_title'"

I am more than happy to send a pull request with the changes (and tests) if you are open to this.

Inline Decorators

They appear to be broken. I can create an anonymous class and assign it to the :decorator key, but when trying to use a block I get a NoMethodError on represented#to_hash. After some light skimming through the code, I suspect this may be a problem with #representer_engine not being defined on Representable::Decorator.

Can't convert JSON hash to Ruby array

I can't seem to find a way to convert a JSON hash (that has a single key/array value) into a Ruby array. Here's an example of what I mean.

JSON:

{"users": [ {"id":1,"name":"Joe"}, {"id":2,"name":"Bob"} ]}

RUBY: (assuming a User class)

[ #<User @id=1 @name=Joe>, #<User @id=2, @name=Bob> ]

I think the problem is that there is no way (as far as I can tell) to represent a JSON hash as an array. Do I instead need to represent it as a hash and grab the array collection from there?

Clarifying Virtus coercion examples

Hi

I just ran into an issue in one of my own apps when I added include Representable::Coercion to a representation class (I'm using classes, not extending individual objects).

There is a test that recreates the behaviour I was seeing, but it is currently being skipped, with a note that the cause lies in Virtus: https://github.com/apotonick/representable/blob/master/test/coercion_test.rb#L38

The Representable coercion test gives examples of using Virtus in object-extend form and class-include form. The class in the class-include form example defines attr_accessor :track, which is what it appears Virtus isn't handling.

Virtus works by defining a module that contains the accessor methods: https://github.com/solnic/virtus/blob/master/lib/virtus/class_methods.rb#L72-L88

In the object-extend form, this gets mixed into the object's singleton class; in the class-include form it gets mixed into the object's class. This means that, In the object-extend form, if you define an accessor in the class or the representation module, the accessor which Virtus creates will be found first by Ruby's method lookup in the object's singleton class. On the other hand, in the class-include form, the accessor defined in the class will be found before the one in the dynamically-created Virtus module.

I've created a fork and updated the example with one way of overriding a Virtus accessor defined in the class-include form: https://github.com/ashmoran/representable/blob/clarify_virtus_coercion_example/test/coercion_test.rb#L26-L57

I don't think Virtus is doing anything wrong here, at least in the sense it's not breaking any standard Ruby behaviour, although it would be nice if it was easier to override the accessors (I haven't spent too long looking for a better way). I might file an issue there as it could be a useful improvement. I'm almost brand-new to both Representable and Virtus so I've had to do quite a bit of digging into the code here but I haven't actually used either project much yet.

Now I know what caused the problem, it was easy for me to fix the bug in my own code (delete the attr_accessor line). So I think the real issue is just that the Representable example is a bit misleading about where this behaviour comes from.

Can't get embedded docs to work.

I'm using mongoid 4:

I have something like this:

module ParentRepresenter
  include Roar::Representer::JSON

  collection :children, representer: ChildrenRepresenter, parse_strategy: sync
end

module ChildrenRepresenter
  include Roar::Representer::JSON
  property :child_property
end

Sending the following json makes it fail:

{
  "children": [{ child_property: "Foo" }]
}

and in relations I would have Parent embeds_many declaration.

Is there something I'm doing wrong and this isn't working (I've done a bit of research though).

Backtrace:

NoMethodError: undefined method `child_property=' for #<Hash:0x007fb40054ad38>
from ...representable/lib/representable/binding.rb:82:in `block in set'

I used to have the same but instead for nil until I pointed my Gemfile to master.
Any clues?

more flexible collection getter method

hi,

i really love your work in representers and roar. especially the HAL module is great for my use case.

there is one problem i'm currently facing and i'm not sure if there is a solution yet or if i'm missing something.

what i need/want to do is to somehow filter the items that are serialized by a collection property.

i hope you get the idea by this example:

module DirectoryRepresenter
  include ::Roar::Representer::JSON
  include ::Roar::Representer::JSON::HAL::Links
  include ::Roar::Representer::Feature::Hypermedia

  collection :files, class: File, extend: FilesRepresenter do |options|
    self.files.modified_after(options[:date])
  end
end
  directory.extend(DirectoryRepresenter).to_json(date: 10.days.ago)

the block for the collection property would be called instead of sending :files to the serialized object.

is this functionality somehow available already? could you give me hint where to start implementing such a feature? i played around with your code but i'm not really sure if my approach fits your design best and i couldn't get it to work.

thx, simon

What's the easiest way to omit attribute when collection is optional?

require 'json'
require 'virtus'
require 'representable/json'

class PhoneNumber
  include Virtus.model

  attribute :number, Integer
end

class Address
  include Virtus.model

  attribute :name, String
  attribute :phone_numbers, Array[PhoneNumber]
end

module PhoneNumberRepresenter
  include Representable::JSON

  property :number
end

module AddressRepresenter
  include Representable::JSON

  property :name
  collection :phone_numbers, class: PhoneNumber, extend: PhoneNumberRepresenter
end

address = Address.new(name: 'Bart Simpson')
puts address.extend(AddressRepresenter).to_json

address2 = Address.new(name: 'Bart Simpson', phone_numbers: [{number: 3231312}])
puts address2.extend(AddressRepresenter).to_json

What's the easiest way to prevent outputting "phone_numbers" when there are none?

Override properties

It would be cool to allow overriding properties in inheriting representers.

module SongRepresenter
  include Representable::Hash

  property :title
end

And in the inheriting representer you replace the attribute:

module HitRepresenter
  include SongRepresenter

  property :title, as: :name # overrides the old

Would that break anything?

And, maybe, instead of replacing you could do a real override:

module HitAlbumRepresenter
  include HitRepresenter

  property(:title).override do |options|
    {getter: ...}
  end # => property :title, as: :name, getter: ...

CData content is escaped

I have the following simple implementation

class JobPostingRepresenter < Roar::Decorator
include Roar::Representer::XML

property :title, as: :JobTItle, getter: lambda { |opt| Nokogiri::XML::CDATA.new(opt[:doc], advert.title) }

end

as I would like to wrap my job title in a CDATA tag. But the builder in representable uses the function content= from Nokogiri. This function escapes any XML meta characters. Is it possible to add support for using the native_content= function in Nokogiri?

And thanks so much for writing this wonderful gem, I use it on a few projects ๐Ÿ‘

uninitialized constant Representable

Been scratching my head over this one:

On two machines, one running windows and ruby-2.0.0-p481, and another running linux and 2.1.1, rackup barfs when trying to parse lib/representable/hash/collection.rb

https://gist.github.com/oliverbarnes/12d4be3d132e81f77d2d

But runs smoothly on my mac mavericks, with both ruby versions.

On the windows machine, requiring representable on irb and then trying to open the Representable::Hash module goes smoothly as well. Why would it not when rack tries to load everything?

Using representable 1.7.5. Not sure if this is a representable issue, but I'm hoping somebody using it has come across this or has any clues

#from_json fails on typed properties when missing in JSON payload

I'm having problems with creating new versions of existing APIs that are backward compatible with older clients. The issue is that any typed property that is missing from a JSON payload causes an error during #from_json, preventing older clients from being able to communicate with new APIs that now require that property to exist.

I have created a spec to reproduce the issue using Representable 1.1.5:

https://gist.github.com/2408242

I'm hoping for some kind of relaxed_mode=true to enable this feature while retaining backward compatibility for other consumers of the rubygem.

Thanks!

Coerce helper method value on decorator scope

I do not have recoreded_at method defined on song object.

My SongRepresenter looks like this:

class SongRepresenter < Representable::Decorator
  include Representable::JSON
  include Representable::Coercion

  property :recorded_at, :decorator_scope => true, :type => DateTime

  def recorded_at
    Time.now
  end
end

I'm trying to coerce recoreded_at but on

SongRepresenter.new(song).to_json

I get undefined method for song object. I would like to coerce the value from the helper method defined in SongRepresenter class. Is this possible?

allow to mix module and inline decorators

collection :foo, :extend => FooRepresenter do
  property :bar
end

would be awesome to allow to decorate existing representers with a inline representer.
in addition it should allow to overwrite existing definitions for properties and collections, ...

thanks!

Make extend less intrusive

Internally, when using object.extend(SongRepresenter) we could just add to_hash and pass representable_attrs as an argument to to_hash. This avoids changing state of the represented object.

This would also allow using ::nested in modules.

Fix deprecation warning -> Virtus module is deprecated

$ rspec
including Virtus module is deprecated. 
Use 'include Virtus.model' instead /Users/karlingen/.rvm/gems/ruby-2.1.0/gems/representable-1.8.1/lib/representable/coercion.rb:5:in `include'

Will this be fixed soon?

Add key(s) translation

I want to support this and I'm pretty sure this would be a neat feature for representable.
The thing is that per API best practices I should be returning attributes in camelCase while my backend system's convention is to underscored keys.
Now it would be neat if you could specify a translation strategy for keys both in to_json and from_json that'll do the #camelize or similar for you.

I want this hash:

{ some_key: "some value" }

to serialize as:

{ "someKey": "some value" }

so the client code (frequently JS), reads:

someKey.someValue

which is pretty natural there.

This should work both ways and recursively. Thanks.

Allow including representer without Representable::*

Currently, inheriting from a representer implies including the representer base module, too.

module DrinkRepresenter
  include Representable::JSON
  include BaseRepresenter
end

Why not make it easier?

module DrinkRepresenter
  include BaseRepresenter
end

Default options

It would be awesome if we could have a clean way to specify default options for all properties in a single line.

Example:

module BandRepresenter
  include Roar::Representer::JSON
  include Roar::Representer::Feature::Hypermedia

  # This is how I do it right now
  def self.property(name, options={})
    super(name, options.merge!(render_nil: true))
  end

  # This is how I would like to do it.
  default_options render_nil: true

  property :id
  property :whatever1
  property :whatever2
  property :whatever3
  property :whatever4
  property :whatever5
end

Thanks

Decorator :exec_context not functioning correctly?

Hello! I have just started using representable through roar-rails and it has been a fantastic replacement for our current serialization needs. I have however ran into a couple issues and wanted to see if I have a bug or just have been delegating incorrectly.

I cannot seem to get exec_context to work correctly within my decorator:

# => page_representer.rb
require 'roar/decorator'

class V1::PageRepresenter < Roar::Decorator
    include Roar::Representer::JSON
    include Roar::Representer::JSON::HAL

    property  :about,           render_nil: true
    property  :avatar,
              :getter => lambda { |*| avatar.serializable_hash }
    property  :email,           render_nil: true
    property  :first_name,      render_nil: true

    property :last_name, :exec_context => :decorator

    def last_name
        represented.first_name
    end

end

This will continue to display @page.last_name in the JSON response rather than the @page.first_name value that I expect:

// generated json
{
    "about": "about about about",
    "avatar": {
        "url": null,
        "thumb": {
            "url":  null
        },
        "medium": {
            "url": null
        }
    },
    "email": "[email protected]",
    "first_name": "lucas",
    "last_name": "charles"
}

and version:

$ bundle list | grep roar
  * roar (0.12.4)
  * roar-rails (0.1.5)
$ bundle list | grep representable
  * representable (1.7.7)

Am I doing something wrong or is this a known bug?

Definition's :getter alias should be :from, not :name?

As of commit e07abbd, if we did:

property :title, :from => :display_title

create_representation_with method in lib/representable.rb will always get the value from the method 'title', not 'display_title'.

value = send(bin.definition.getter) || bin.definition.default

This is because the bin.definition.getter will always get whatever the 'name' method returns, not the 'from' method...

class Definition declares the alias:

alias_method :getter, :name

JRuby: default XML serialization options different

These fail on JRuby. Looks like the serialization has different formatting options.

  1) Failure:
[xml] with :class#test_0001_anonymous [/Users/dblock/source/representable/dblock/test/inline_test.rb:22]:
--- expected
+++ actual
@@ -1,5 +1 @@
-"<open_struct>
-  <song>
-    <name>Alive</name>
-  </song>
-</open_struct>"
+"<open_struct><song><name>Alive</name></song></open_struct>"



  2) Failure:
[xml] collection with :class#test_0001_anonymous [/Users/dblock/source/representable/dblock/test/inline_test.rb:45]:
--- expected
+++ actual
@@ -1,5 +1 @@
-"<open_struct>
-  <song>
-    <name>Alive</name>
-  </song>
-</open_struct>"
+"<open_struct><song><name>Alive</name></song></open_struct>"

I have a branch with the beginning of JRuby support changes, just minor stuff.

Property defined as XML content

Suppose I have the following XML:

<user>
  <addresses>
    <address type="01">123 Street</address>
    <address type="02">Cardboard Box</address>
  </addresses>
</user>

Can I direct Representable to use the content of an XML node as well as an attribute of the node? Here's my attempt to represent the above XML as decorators:

class UserRep < Representable::Decorator
  include Representable::XML
  collection :addresses, :wrap => :addresses,
                 :class => Address, :decorator => AddressRep
end

class AddressRep < Representable::Decorator
  include Representable::XML
  property :type, :attribute => true
  property :value # ??? should be "123 Street"
end

Representing heterogenous array objects?

Is it possible to represent an object as a heterogeneous, index-based, JSON array, instead of a JSON object (hash)?

So instead of the following:

{ "name": "Album Name", "num_tracks": 10, "isbn": "ISBN", "etc": "etc" }

I'd like to use the following:

["Album Name", 10, "ISBN", "etc"]

I've had good luck using a :reader with the right index, however the :writer is passed a JSON object ({}) instead of an array ([]), so my output JSON looks more like:

{ "0": "Album Name", "1": 10, "2": "ISBN", "3": "etc" }

Is this supported?

Issue passing options for a representable - wrong number of arguments (1 for 0)

I'm trying to use passing options in representers gem

I have a decorator:

class ProductDecorator < Representable::Decorator
  include Representable::JSON

  property :code
  property :name

  property :store_id, getter: lambda { |opts| opts[:store_id] },
    type: Integer

  property :url, decorator_scope: true, getter: lambda { |options|
    File.join(options.fetch(:site,""), url)
  }, type: String

  def url
    File.join(represented.path ,"p")
  end

end

But when I try to pass my options using:

ProductDecorator.new(product).
        to_hash(store_id: 1, site: "my_url")

I'm getting the message wrong number of arguments (1 for 0)

I've tried many options, but I could find what's going on.

No way to have array of links

Reading through http://nicksda.apotomo.de/2012/04/roar-0-10-with-json-hal-support-and-representable-1-1-6-released/ to look at how I can represent links to a collection.

{
  "_links": {
    "self": { "href": "/product/987" },
    "upsell": [
      { "href": "/product/452", "title": "Flower pot" },
      { "href": "/product/832", "title": "Hover donkey" }
    ]
  },
  "name": "A product",
  "weight": 400,
  .. *snip* ..
}

It doesn't appear to be any way to represent "upsell" as a link.

We have link :upsel do end, but it doesn't accept an array as a result.

:if condition fails with "wrong number of arguments (1 for 0) (ArgumentError)" when lambda doesn't accept an argument

We recently upgraded from 1.7.7 to 1.8.5 (as a transitive dependency of roar) and our conditional blocks are now breaking when they don't accept an argument.

This simplified example demonstrates the problem:

require 'representable/json'
require 'ostruct'

module SongRepresenter
  include Representable::JSON

  property :track, if: lambda { track > 0 }
end

song = OpenStruct.new(track: 2)
song.extend(SongRepresenter)
puts song.to_json

Leads to:

representable_example.rb:7:in `block in <module:SongRepresenter>': wrong number of arguments (1 for 0) (ArgumentError)
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/uber-0.0.6/lib/uber/options.rb:77:in `instance_exec'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/uber-0.0.6/lib/uber/options.rb:77:in `proc!'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/uber-0.0.6/lib/uber/options.rb:69:in `evaluate_for'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/uber-0.0.6/lib/uber/options.rb:58:in `evaluate'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/binding.rb:118:in `evaluate_option'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:56:in `skip_conditional_property?'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:44:in `skip_property?'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:31:in `serialize_property'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/readable_writeable.rb:10:in `serialize_property'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:24:in `block in serialize'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:23:in `each'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/mapper.rb:23:in `serialize'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable.rb:26:in `create_representation_with'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/hash.rb:39:in `to_hash'
  from .../.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/representable-1.8.5/lib/representable/json.rb:40:in `to_json'
  from representable_example.rb:12:in `<main>'

I noticed that uber was a new dependency that has just come in, does the problem lie there? I'd suggest adding a check for the arity of the block before attempting to pass it arguments.

Be able to specify a custom binding for an attribute

It would be awesome if I could specify a custom binding for an attribute, for example

collection :attributes, :class => Attribute, extend: Representer, binding: CustomBinding

At the moment I want this because I need to update an attribute by deserializer a collection recursively. The default binding instantiates collection members and then assigns the collection, rather that delegating the deserialization to the members of the collection.

It appears that this would only require adding :binding to Definition and updating PropertyBinding.build_for to return the custom binding if specified.

Thoughts?

Coercion with nesting/collections?

I'm wondering how I could get coercion to work with nesting/collections?
Could someone possibly post an example on how to do it right?

My current approach listed below doesn't work right, I always get an error with the to_jsonmethod besides of the values not being coerced as expected:

class Address
  attr_accessor :street, :number

  include Representable::JSON
  include Representable::Coercion

  property :street, type: String
  property :number, type: Fixnum 
end

class User
  attr_accessor :id, :firstname, :lastname, :address

  include Representable::JSON
  include Representable::Coercion

  property :id, type: Fixnum
  property :firstname, type: Symbol
  property :firstname, type: Symbol  

  property :address, type: Address, :class => Address
end


# --------------


test_data = <<DEMO

{
  "id": "1",
  "firstname": "Frank",
  "lastname": "Sinatra",
  "address": {
    "street": "HollywoodBlvd",
    "number": 1234
  }
}
DEMO


frank = User.from_json(test_data)
puts frank.to_json

Error:

/Users/grinser/.rvm/gems/ruby-1.9.3-p286/gems/virtus-0.5.2/lib/virtus/instance_methods.rb:131:in `to_hash': wrong number of arguments (1 for 0) (ArgumentError)
    from /Users/grinser/.rvm/gems/ruby-1.9.3-p286/gems/representable-1.2.7/lib/representable/json.rb:35:in `to_json'
    from /Users/grinser/.rvm/gems/ruby-1.9.3-p286/gems/representable-1.2.7/lib/representable/json.rb:35:in `to_json'
    from /Users/grinser/.rvm/gems/ruby-1.9.3-p286/gems/representable-1.2.7/lib/representable/json.rb:35:in `to_json'
    from demo2.rb:95:in `<main>'

Lonley Hash property aliasing does not work

Using v1.6.0.

require 'representable/json/hash'

module SongRepresenter
  include Representable::JSON::Hash

  property :title, as: :name
  property :track
end

song = {"title" => "Fallout", "track" => 1}

song.extend(SongRepresenter).to_json
# => {"title":"Fallout","track":1}
# expecting: {"name":"Fallout","track":1}

This works fine for normal objects, but when using a lonely hash, the aliasing does not work.

Undefined method nested for my class. How can I solve it?

I'm trying to use the nested method but I'm getting this message: undefined method 'nested' for OrdersRepresenter.

I already put representable gem im my Gemfile.

This is my class:

class OrdersRepresenter < Representable::Decorator
  include Representable::JSON

    nested :body do 
      collection :orders
    end
end

I also have roar-rails in my Gemfile

Thanks

How can I use parse_strategy to add items to my collection (not to update)?

Hi, I'd like to know if its possible to use parse strategy to add items to my list, not to update it.

I have a representable that has a list. I'm inside a loop and I'd like that this list get incremented. So I decided to use Sync objects

   begin
     response = get
     orders_collection.extend(Representers::OrdersCollectionRepresenter)

     orders_collection.from_hash(response.body)
  end while not the_end(orders_collection)

module OrdersCollectionRepresenter
    include Representable::JSON

    collection :list, extend: Representers::OrderCollectionItemRepresenter, 
      class: OrderCollectionItem, parse_strategy: :sync

end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.