Code Monkey home page Code Monkey logo

roar-rails'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.

roar-rails's People

Contributors

apotonick avatar aquateen avatar audionerd avatar bengreenberg avatar c0va23 avatar cbeer avatar chadwtaylor avatar chrisupb avatar cowboyd avatar ekampp avatar fltiago avatar guiocavalcanti avatar jandudulski avatar jocko-wowza avatar joefiorini avatar joshco avatar jrmhaig avatar mfrister avatar mikekelly avatar mpeychich avatar myabc avatar paulccarey avatar pgaertig avatar thegcat avatar timoschilling avatar whittle avatar yogeshjain999 avatar zavan 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

roar-rails's Issues

undefined method `mounted_helpers' for rails 3.0.10

I'm getting this error when I try to startup the rails development server on my machine.

redu (api) > rails s
=> Booting WEBrick
=> Rails 3.0.10 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Exiting
/Users/guiocavalcanti/.rvm/gems/ruby-1.8.7-p352/gems/roar-rails-0.0.2/lib/roar/rails/railtie.rb:12: undefined method `mounted_helpers' for #<ActionDispatch::Routing::RouteSet:0x103a2fc98> (NoMethodError)

I just created a representer as follows:

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

  property :name
  property :description
  property :description

  link :self do
    environment_url(self)
  end
end

And added this config to my development.rb config file:

  config.representer.default_url_options = {:host => "127.0.0.1:3000"}

Commenting this everything worked fine.

Consume! not consuming

Working on the create action for an app. When inspecting the new lander in #create after calling #consume!, all attributes are empty. lander_params does exist. Any ideas? Maybe missing something simple. Thanks!

New Lander after calling consume!

#<Lander id: nil, name: nil, location: nil, default_offer_id: nil, created_at: nil, updated_at: nil>

Lander Params in create

{"name"=>"foo", "location"=>"www.google.com"}
module Api
  module V1
    class LandersController < ApplicationController
      include Roar::Rails::ControllerAdditions
      before_action :set_lander, only: [:show, :edit, :update, :destroy]
      respond_to :json

      def create
        lander = consume!(Lander.new, represent_with: LanderRepresenter)
        lander.save
        respond_with @lander, represent_with: LanderRepresenter
      end
  end
end  
module LanderRepresenter
  include Roar::Representer::JSON
  include Roar::Representer::Feature::Hypermedia

  property :name
  property :location
  property :default_offer

  link :self do
    v1_lander_url self
  end
end
  describe '#create' do
    before do
      json = { format: 'json', lander: { :name => "foo", :location => "www.google.com" } }
      post v1_landers_path, json.to_json, {'CONTENT_TYPE' => "application/json", 'ACCEPT' => 'application/json'}
    end

    it 'should create a lander' do
      binding.pry
    end
  end

License

Thanks for your work on this gem.

I was hoping you could clarify the license for this project.

Thanks!

Sending JSON data in rspec

I have this code in my spec:

before { post :update, { profile: { current_city: "Washington, DC" } }, format: :json }

Controller:

  def update
    @profile = consume!(Profile.find(params[:id]))
    @profile.save
    respond_with @profile, location: profile_url(@profile)
  end

When I run this, I get this error:

Failure/Error: before { post :update, profile: { current_city: { value: "Washington, DC" } }.to_json, format: :json }
  JSON::ParserError:
    757: unexpected token at 'profile=%7B%22current_city%22%3A%7B%22value%22%3A%22Washington%2C+DC%22%7D%7D'

It's interesting that request.body.read doesn't have the JSON string.

Strange Model class name lookups when using namespaces

The following case fails (assume that ApplicationRepresenter sets up the included modules for Roar and ApplicationController includes Roar::Rails::ControllerAdditions:

module Instruments
  class Trumpet < ActiveRecord::Base
  end

  class TrumpetRepresenter < ApplicationRepresenter
  end

  class TrumpetsController < ApplicationController
    def show
      trumpet = Trumpet.find(params[:id])
      respond_with trumpet
    end
  end
end

The error is: Uninitialized Constant Instruments::Instruments. Seems like a better method of selecting a representer would be the following:

Assume you have a controller namespaced as N1::N2::Controller and a representer namespaced as N1::ModelRepresenter:

Check for the following constants in order and if none is found then fail with Uninitialized constant: N1::ModelRepresenter

N1::N2::N1::ModelRepresenter
N1::N1::ModelRepresenter
N1::ModelRepresenter

This way if the model is not namespaced it takes on the controllers namespace by default but can still find an un-namespaced representer.

Handle validation errors

It seems for now roar-rails' responder doesn't handle validation errors transparently (correct me if I'm wrong) like rails' builtin responder does, see ActionController::Responder docs. Today I write this:

def create
  singer = Singer.new(params[:singer])
  if singer.save
    respond_with singer
  else
    render json: { message: "Validation Failed", errors: singer.errors }.to_json
  end
end

but I'd like this to automatically happen (depending if resource has_errors?..) so we can write this:

def create
  respond_with Singer.create(params[:singer])
end

Could you :

  • confirm this doesn't work like that for now ? (just tested it with rails 4 / ruby 2.0)
  • tell me if this could be interesting or if it's achievable in an other way ?

If it sounds good I can work on that and submit a pull request in a few weeks.

Rails 4.2 compatibility

Updating to rails 4.2.0.beta2 I got the following error message booting up the server:

/Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar/rails/rails4_0_strategy.rb:2:in `<module:Rails>': cannot load such file -- action_controller/metal/responder (LoadError)
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar/rails/rails4_0_strategy.rb:1:in `<top (required)>'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar-rails.rb:34:in `require'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar-rails.rb:34:in `<module:Rails>'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar-rails.rb:21:in `<module:Roar>'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/roar-rails-0.1.6/lib/roar-rails.rb:20:in `<top (required)>'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:76:in `require'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:76:in `block (2 levels) in require'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:72:in `each'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:72:in `block in require'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:61:in `each'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler/runtime.rb:61:in `require'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/bundler-1.7.3/lib/bundler.rb:133:in `require'
        from /Users/leifg/projects/coruscant/config/application.rb:5:in `<top (required)>'
        from /Users/leifg/projects/coruscant/config/environment.rb:2:in `require'
        from /Users/leifg/projects/coruscant/config/environment.rb:2:in `<top (required)>'
        from /Users/leifg/projects/coruscant/spec/spec_helper.rb:5:in `require'
        from /Users/leifg/projects/coruscant/spec/spec_helper.rb:5:in `<top (required)>'
        from /Users/leifg/projects/coruscant/spec/controllers/api/v1/api_keys_controller_spec.rb:1:in `require'
        from /Users/leifg/projects/coruscant/spec/controllers/api/v1/api_keys_controller_spec.rb:1:in `<top (required)>'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/configuration.rb:1105:in `load'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/configuration.rb:1105:in `block in load_spec_files'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/configuration.rb:1105:in `each'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/configuration.rb:1105:in `load_spec_files'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/runner.rb:96:in `setup'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/runner.rb:84:in `run'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/runner.rb:69:in `run'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/lib/rspec/core/runner.rb:37:in `invoke'
        from /Users/leifg/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/rspec-core-3.1.5/exe/rspec:4:in `<top (required)>'
        from /Users/leifg/.rbenv/versions/2.1.3/bin/rspec:23:in `load'
        from /Users/leifg/.rbenv/versions/2.1.3/bin/rspec:23:in `<main>'

The solution is rather simple though. Add the current version of the responders gem above roar-rails in your Gemfile:

gem "responders", "~> 2.0.0"
gem "roar-rails"

Background: respond_with has been pulled into the separate gem responders. See here

I don't know if this is a good solution or something of the dependencies of roar-rails needs to be changed.

Unable to user roar-rails gem with rails-api gem.

When my base controller inherits from ActionController::API, roar doesn't seem to kick in. There's no errors, and my representers are called, yet the representer's output isn't used in the resulting response (rather the output of the model's native to_json method seems to be being used). If I change my ApplicationController to inherit from ActionController::Base everything magically begins working. I dug into the code a bit on both sides but was unable to figure out what part of ActionController::Base roar is depending on.

Does anyone have any idea what the problem is and how I can use both these gems together in my app?

Thanks!

`render` accepting status, (or any other attributes for that matter)

I've been using the include Roar::Rails::ControllerAdditions::Render helpers so that I can still make use of classic render, but apparently it doesn't accept extra params, such as status codeโ€ฆ I maybe mistaken but it looks like https://github.com/apotonick/roar-rails/blob/master/lib/roar/rails/controller_additions.rb#L80-L83 only cares about the format:

def render(options)
  format = options.keys.first
  super format => prepare_model_for(format, options.values.first, options)
end

I have a simple fix fork hereโ€ฆ https://github.com/christopherhein/roar-rails/blob/render-status-code/lib/roar/rails/controller_additions.rb#L80-L84 I've been trying to figure out if there are other params that should be passed correctlyโ€ฆ

Let me know what you thinkโ€ฆ I can always put a pull requestโ€ฆ
Thanks!

1.8 test fix?

one of 1.8.x the problems is, that Hashes in 1.8 are not order save so we get this problem:

https://github.com/apotonick/roar-rails/blob/master/test/representer_test.rb#L8

1) Failure:
test_representers_can_use_URL_helpers(RepresenterTest)
<"{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://http://roar.apotomo.de/singers/Bumi\"}]}"> expected but was
<"{\"name\":\"Bumi\",\"links\":[{\"href\":\"http://http://roar.apotomo.de/singers/Bumi\",\"rel\":\"self\"}]}">.

has any one an idea, how to fix that?

rendered models wrapped in body key

I am currently using the latest version of rails (4.2.0), responders (2.0.2) and roar-rails (1.0.0). My end to end test renders a json I find a little bit confusing. This is what is returned:

{
  "body": "{\"id\":33,\"alias\":\"yapi\",\"name\":\"new team\"}",
  "content_type": {
    "synonyms": [
      "text/x-json",
      "application/jsonrequest"
    ],
    "symbol": "json",
    "string": "application/json"
  }
}

But I expect only the content of "body". I used the "standard way" of rendering the Team model, described in roar and roar-rails.

With a little bit of debugging I found out, that somehow the display method of the responder does not render the model, but a wrapping object, that is defined in the Roar::Rails::Responder here I'm not quite sure if there is a workaround, but I'm pretty sure, that rendering the wrapper object is not what should happen.

accepts_nested_attributes_for

Is is possible to create a record using accepts_nested_attributes_for in Roar?

For example, I have a Person model and a Subscriber model.

Say I want to create a User when I make subscriber, typically in Rails it is done like this:

Subscriber.create(
    :region          => 'West',
    :referrer        => 'John Smith',
    :user_attributes => {
        :email    => '[email protected]',
        :name     => 'Jane Doe',
        :password => 'foobar',
    }
)

How can I accomplish this using Representers?

autoloading problem

Hi -- thanks for making roar, it's really valuable for us.

We recently starting experiencing a problem under JRuby. A particular test sometimes fails (depending on the randomization seed) like so under 0.1.6:

     Failure/Error: Unable to find matching line from backtrace
     NameError:
       uninitialized constant Bar::FoosController::BarRepresenter
     Shared Example Group: "bazing a thing" called from ./spec/features/baz_thing_spec.rb:36
     # ./app/controllers/bar/foos_controller.rb:10:in `show'

and like so under 0.1.5:

    Failure/Error: Unable to find matching line from backtrace
    RuntimeError:
      Circular dependency detected while autoloading constant BarRepresenter
    Shared Example Group: "bazing a thing" called from ./spec/features/baz_thing_spec.rb:36
    # ./app/controllers/bar/foos_controller.rb:10:in `show'

I looked through all the roar-rails source and I'm embarrassed to say I couldn't figure out where or how "app/representers/*" gets required or autoloaded, so I didn't make any headway figuring out what the problem could be.

/cc @dadadadave

errors when "Rendering from render"

the issue appears with the includes in the controller


class Api::V2::AppointmentsController < ApplicationController
  include ::Roar::Rails::ControllerAdditions
  include ::Roar::Rails::ControllerAdditions::Render

  # GET /api/v2/appointments/:id
  def show
    @appointment = Appointment.first
    if @appointment
      # render json: AppointmentRepresenter.new(@appointment).to_json, status: :ok
      render @appointment, status: :ok

in my rspecs, I consistently get:


Failure/Error: get "/api/v2/appointments/123"
     ActionController::RoutingError:
       undefined method `responder=' for Api::V2::AppointmentsController:Class
     # ./app/controllers/api/v2/appointments_controller.rb:2:in`include'

AND


 Failure/Error: get "/api/v2/appointments/#{@appointment.id}"
     AbstractController::ActionNotFound:
       The action 'show' could not be found for Api::V2::AppointmentsController

When remove those includes and use the alternative:


render json: AppointmentRepresenter.new(@appointment).to_json, status: :ok'

this works out as expected

Am I doing something wrong?

Ruby 2.1.2
Rails 4.1.5
Rspec 3.1.0

thanks

Upgrading from 0.1.6 to 1.0.0

I have a project using roar-rails 0.1.6 and everything is working great ๐Ÿ‘Œ

I want to upgrate to 1.0.0 but when I tried to do so I started receiving the following error in my specs:

Failure/Error: post v1_groups_path, json(:group), headers_for(:json)
     NoMethodError:
       undefined method `group_url' for #<Api::V1::GroupsController:0x007fcd83e7d5a8>
     # ./app/controllers/api/v1/groups_controller.rb:25:in `create'
     # ./spec/api/v1/groups_spec.rb:31:in `block (3 levels) in <top (required)>'

Everything except for the #create action is working fine. For some reason calling respond_with @group in #create throws the above error.

This is my GroupsController:

module Api
  module V1
    class GroupsController < ApplicationController
      include Roar::Rails::ControllerAdditions

      before_action :set_group, only: [:show, :update, :destroy]
      respond_to :json

      def index
        @groups = Group.paginate(page: params[:page], per_page: params[:per_page])

        respond_with @groups
      end

      def show
        respond_with @group
      end

      def create
        @group = consume! Group.new
        @group.save

        respond_with @group
      end

      def update
        consume! @group
        @group.save

        respond_with @group
      end

      def destroy
        @group.destroy

        head :no_content
      end

      private

      def set_group
        @group = Group.find params[:id]
      end
    end
  end
end

Any idea what's going on? Missing something simple maybe?

consume! doesn't work with Unicorn

I'm trying to setup a create action, I have the following code:

def create
  @partner = Partner.new
  consume! @partner
  respond_with @partner
end

However, when I run it I get an exception:

NoMethodError (undefined method `string' for #<Unicorn::TeeInput:0x007f9ff1041ee0>):
  app/controllers/api/partners_controller.rb:16:in `create'

I'm not sure, but it looks like in controller_additions.rb:40 it should call #read instead of #string on request.body.

I'm using roar-rails 0.0.8, roar 0.11.2 and unicorn 4.3.1.

Representer for index action

Apologies if I'm being incredibly dumb here, but I can't seem to find a way to change the representer for an index action.

First I assumed a convention that would work with a plural name, and then I tried by explicitly specifying the with_representer option on the respond_with, but I still can't get it to pick up. I had a look through the tests but I couldn't find any examples, is this not possible?

Cheers,
M

representer_name_for returns representer name based on controller path for will paginate collection object

So I have something like

class Api::V1::UsersController < Api::V1::ApiController

  def index
    users = User.paginate(:per_page=>20, :page=>params[:page] || 1).all
    respond_with users
  end

  def show
    user = User.find params[:id]
    respond_with user
  end
end

roar rails is trying to use Api::V1::UsersRepresenter instead of UsersRepresenter for the index action, but for show action it correctly uses UserRepresenter. It seems that I can work around this by adding this on the controller.

represents :json, User

but maybe it should by default try to use UsersRepresenter for consistency?

Pagination with Lonely Collections

I am following your pagination with roar post. However, total_entries is not valid within a lonely collection.

  1 module Api
  2   module V1
  3     class LandersRepresenter < Representable::Decorator
  4       include Representable::JSON::Collection
  5       property :total_entries
  6
  7       items decorator: LanderRepresenter
  8
  9       self.representation_wrap = :landers
 10     end
 11   end
 12 end

When I instead attempt to do the following, it expects a lander property on the lander active record relation: undefined method landers for #<Lander::ActiveRecord_Relation:0x007fd696127658>. Is this because I am using the Decorator class? However, I do get the total_entries property in this case.

 module Api
   module V1
     class LandersRepresenter < Representable::Decorator
       include Roar::Representer::JSON
       property :total_entries

       collection :landers, decorator: LanderRepresenter

       def landers
         self
       end
     end
   end
 end

What's a good way of handling pagination for index actions while using the decorator class?

Thanks for the help as always!

representation_wrap for collections versus members?

Hi. I've been having some trouble figuring out how to setup a smarter wrapper for my representers and wanted to see if this was a current feature or I was just going about it the wrong way. As this is an issue with a specific rails convention in using similar architecture for members and collections, I am filing this under the roar-rails repo specifically.

I essentially want a standardized data node for all responses. This is easy enough for collections with collection :properties, :as => :data but I require wrapping of individual member items as well (#index versus #show actions).

To demonstrate the issue I have a PropertiesRepresenter and a PropertyRepresenter.

module PropertiesRepresenter
  include Roar::Representer::JSON

  collection :properties,
    :extend => PropertyRepresenter,
    :class => Property,
    :as => :data

  def properties
    collect
  end
end
module PropertyRepresenter
  include Roar::Representer::JSON

  self.representation_wrap = :data

  property :id,
  property :created_at

end

At present this causes double-wrapping of the members in a collection like so:

{
  "data": [
    {
      "data": {...}
    },
    {
      "data": {...}
    }
  ]
}

I think what would be ideal is a conditional representation_wrap. Something like self.representation_wrap = :data if :root => true for PropertyRepresenter.

Is there any preexisting functionality similar to this or maybe a tip for getting it done? If not I can work on a PR of some sort but was hoping it was currently supported.

Warning when using include Roar::Rails::ControllerAdditions

This is very minor, but with the latest version of roar-rails (which installs the latest version of hooks by default), we get the following warning:

WARNING: Hooks::InheritableAttribute is deprecated, use Uber::InheritableAttr instead.

when we include Roar::Rails::ControllerAdditions.

Notes on our experience so far with roar-rails

Nick,

We've been using roar rails for about a week now, and we're liking it. Here are some of the things that we've notice which you may want to take under consideration

consuming could have better defaults

We find that so much of our code ends up like this

def create
  @song = Song.new
  consume! @foo
  respond_with @song
end

Since this is the SongsController, we know that we're working with the Song model, so why not make it implied:

def create
  @song = consume
  respond_with @song
end

Model named something else that can't be inferred? how about :

def create
  @track = consume :track # alternatively @track = consume Track
  respond_with @track
end

consume form url-encoded

We had to write an html representer in order to consume form parameters. Not sure if this is the right way, but we wanted to be able to use the exact same controller logic whether the request is coming as a form submission or an AJAX request posting JSON. It seems like this should come right out of the box.

It's kindof a hack because we don't want to use the representer for to_html, only for its from_html. Also, it isn't really 'html' per-se, just application/www-form-urlencoded. Not really sure what the best way is here, just wanted to make you aware of the shims we had to put in to make the app work.

module Roar
  module Representer
    module HTML

      def self.included(base)
        base.class_eval do
          include Representer
          extend ClassMethods
        end
      end

      module InstanceMethods
        def from_html(query_string, options = {})
          Rack::Utils.parse_nested_query(query_string).each do |k, v|
            self.instance_variable_set("@#{k}", v)
          end
        end
      end
    end
  end
end

generators and defaults

figuring out which representers you want to use can be tedious and it would be nice if you could have a ApplicationRepresenter where you could store your defaults, so that you could say:

module SongRepresenter
  include ApplicationRepresenter
  #...
end

roar:install could generate this for you in application_representer.rb, and roar:representation would generate a representation that included it by default.

What's the best practice to tweak representations based on authentication?

There are some attributes, and hypermedia that are exposed for different roles. Is the answer to use a different representer? that seems too heavy handed. It would be nice to have scopes or profiles or something that could be passed in as an option to the respond_with. Then you could mark it up in the representer DSL.

module SongReprestener
  property :name
  link :purchase { purchase_song_path(self) }
  scope(:admin) do
    link :upload { upload_song_path(self) }
  end
end

What's in a name?

personally I prefer FooRepresentation as opposed to FooRepresenter, but that's just me. It implies that "this defines what the representation is", as opposed to "This defines is how the representation is generated" A small opinionated nitpick, but worth mentioning nonetheless.

Conclusion

It's shaping up very nicely so far, is this a good forum to submit feedback? There doesn't seem to be a mailing list.

Collection Representer seem boiler plate, any way to auto generate them?

Hi, I've been playing around with roar and roar-rails. Great job by the way, exactly what I was looking for.

I'm interested to know if there is a way to represent collections without having to create the boilerplate representer code that just reuses the singular form and provides the "collect" call. Is that possible now and I'm missing something? If not, I think just have a template with the plural collection and helper method, then use the singular form as the class and representer.

Related to this, is there a way to auto-discover the property and collection representers too? That seems like boiler plate (i.e. just do the same search that is performed with the controller "represents" logic.

Let me know if I'm way off base here...

Including multiple representers overrides LinkCollection methods

It would be great to be able to include multiple representers with link definitions, currently this doesn't work, so for example:

class ArtistsController
    represents :json, Artist
    represents :xml, Artist
end

module ArtistRepresenter
    include Roar::Representer::JSON::HAL
    include Roar::Representer::XML

    link :self do |opts|
        artists_url
    end
end

returns

undefined method `to_hash' for #<Roar::Representer::Feature::Hypermedia::Hyperlink rel=:self, href="http://33.33.33.30:3000/artists">

I guess the Roar::Representer::XML call to Hyperlink is overriding the previously set to_hash method. Any simple resolution?
#8 seems to touch on this subject

Validation failures when modifying collections

In our API, one of the primary models has several sub-model collections. These sub models all have validations, and the primary model also validates various aspects of the relationship between the collections. (For example: you can't save the Order if there isn't at least one Item. You can't have more Items than item_limit. Etc.)

When creating a record using representers, everything goes smoothly. However, when updating a record, these validations don't really work: even when using the find_or_instantiate strategy, it seems like the core call (somewhere along the line) is:

order.items = [new array of items]

Unfortunately, on an existing record, this is an instant call-to-action by Rails -- it runs off and inserts and deletes records, all before save is ever called on the parent object.

This behavior by Rails is one reason all our web forms use nested_attributes_for: the web forms are submitting the "items_attributes" collection, which means Rails doesn't attempt any database modifications until all the models are validated.

I saw another ticket (#81) where you mentioned that you shouldn't need to use nested_attributes with roar. Do you have any patterns that would allow roar to behave the way way nested_attributes does? (That is: that will not make any database updates until all of the models being touched have been validated.)

properties/collections with a defined 'getter' block ignore represent_with parameter

We started using Roar::Decorator's a while ago so I forget if this was an issue on modules too, but definitely the case on Decorator's.... I've been burned by this a few times now when I forget about it, it feels like a bug to me, but perhaps it's documented/known? Seems like it would be nice to use it when it's specified though.

To provide an example:

class V3::AnswerRepresenter < Roar::Decorator

property :id
property :response, represent_with: V3::ResponseRepresenter, getter: ->(opts) { responses.first }, embedded: true

end

Normally this would work fine without the getter option, but when the getter is present, it's response is not passed through the specified representer and instead has to_json called on it directly or something.

consume! is based on the accept header instead of the content-type header

Trying out the consume! method, I received the following error message: "undefined method `from_html' for #<...> (NoMethodError)", despite setting the content-type header to "application/json".

It turns out that parsing is based on the accept header (https://github.com/apotonick/roar-rails/blob/master/lib/roar/rails/controller_additions.rb#L28). Setting the accept header to "application/json" instead of "/" (curl's default), causes the formats array to go from [:html, :text, :js, :css, :ics, :csv, :png, :jpeg, :gif, :bmp, :tiff, :mpeg, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip] to [:json]. And then my representer succeeds at parsing the json document.

Collection representor not using property constraints of representer

After some digging trying to find the best way to respond_with a collection of landers... in my app I have:

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

  property :name
  property :location

  link :self do
    v1_lander_url self
  end
end
module LanderCollectionRepresenter
  include Representable::JSON::Collection

  items extend: LanderRepresenter, class: Lander
end
module Api
  module V1
    class LandersController < ApplicationController
      include Roar::Rails::ControllerAdditions
      before_action :set_lander, only: [:show, :edit, :update, :destroy]
      respond_to :json

      def index
        @landers = Lander.all
        respond_with @landers, :represent_with => LanderCollectionRepresenter
      end

      def show
        respond_with @lander, :represent_with => LanderRepresenter
      end


      private

      def set_lander
        @lander = Lander.find params[:id]
      end
    end
  end
end

Is this the proper way to respond_with a collection? Rendering the LanderRepresenter works perfectly; however, when I render the collection, it spits out the entire object (without limiting the response to the property constraints on :name, :location, :link). i.e:

[{"id":1,"name":"Dr. Jeffery Skiles1","location":"http://schusterbeahan.com/eddie/1","default_offer_id":null,"created_at":"2014-09-22T20:06:26.296Z","updated_at":"2014-09-22T20:06:26.296Z"},{...},{...}]

Uninitialized constant Mongoid for index action

Hi,

I'm trying roar-rails for the first time (Rails 3.2.13, Mongoid 3.1.4), and I was able to get the show action to work just fine, but the index action throws an Uninitialized constant Mongoid error. What am I doing wrong?

Here is app/representers/programs_representer.rb:

require 'representable/json/collection'

module ProgramsRepresenter
  include Representable::JSON::Collection
  items extend: ProgramRepresenter, class: Program
end

Here is programs_controller.rb:

module Api
  module V1
    class ProgramsController < ApplicationController

      include Roar::Rails::ControllerAdditions
      respond_to :json

      def index
        programs = Program.all
        respond_with programs
      end

      def show
        program = Program.find(params[:id])
        respond_with program
      end

    end
  end
end

Here is program_representer.rb, which works with the show action:

require 'roar/representer/json'

module ProgramRepresenter
  include Roar::Representer::JSON

  property :id
  property :name
  property :description
end

Strange intermittent error /w multipart posts

ruby 1.9.3-p429
rails 3.2.13
representable 1.5.3
roar 0.11.18
roar-rails 0.1.0

I'm experiencing a strange intermittent error involving multipart posts. It happens rarely, but once it starts to happen, it keeps happening until the app server is restarted.

My question is, why is Roar::Representer::JSON::InstanceMethods::to_hash being called? It doesn't seem like roar should be involved here, in the processing of an options hash.

Additionally, considering that Rack::Multipart::Parser always calls Tempfile.new with no options, it seems very strange that the error only manifests some of the time.

Stack trace:

NoMethodError: undefined method `id' for nil:NilClass
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:77:in `block in get'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:93:in `represented_exec_for'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:76:in `get'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:36:in `block in compile_fragment'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:93:in `represented_exec_for'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/binding.rb:35:in `compile_fragment'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable.rb:95:in `compile_fragment'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/roar-0.11.18/lib/roar/representer/json/hal.rb:41:in `compile_fragment'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable.rb:62:in `serialize_property'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/feature/readable_writeable.rb:11:in `serialize_property'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable.rb:55:in `block in create_representation_with'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable.rb:54:in `each'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable.rb:54:in `create_representation_with'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/representable-1.5.3/lib/representable/hash.rb:33:in `to_hash'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/roar-0.11.18/lib/roar/representer/json.rb:21:in `to_hash'
  /usr/local/lib/ruby/1.9.1/tmpdir.rb:117:in `try_convert'
  /usr/local/lib/ruby/1.9.1/tmpdir.rb:117:in `create'
  /usr/local/lib/ruby/1.9.1/tempfile.rb:134:in `initialize'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart/parser.rb:104:in `new'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart/parser.rb:104:in `get_current_head_and_filename_and_content_type_and_name_and_body'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart/parser.rb:19:in `block in parse'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart/parser.rb:17:in `loop'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart/parser.rb:17:in `parse'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/multipart.rb:25:in `parse_multipart'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:336:in `parse_multipart'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:201:in `POST'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:26:in `method_override'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:14:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/runtime.rb:17:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/lock.rb:15:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:136:in `forward'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:143:in `pass'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:155:in `invalidate'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:71:in `call!'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:51:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/engine.rb:479:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/application.rb:223:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/railtie/configurable.rb:30:in `method_missing'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/lint.rb:48:in `_call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/lint.rb:36:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/showexceptions.rb:24:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/commonlogger.rb:33:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/chunked.rb:43:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:552:in `process_client'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:632:in `worker_loop'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:500:in `spawn_missing_workers'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:142:in `start'
  /mnt/app/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/bin/unicorn:126:in `<top (required)>'
  /mnt/app/shared/bundle/ruby/1.9.1/bin/unicorn:23:in `load'
  /mnt/app/shared/bundle/ruby/1.9.1/bin/unicorn:23:in `<main>'

curie/template support for JSON-HAL?

JSON-HAL spec now has specification for "curies", and specifically templates for curies to support queries and supported manipulations and transformations of links... I haven't dug into it yet, and maybe we at Amicus can help to add support for this if there's interest, but we're using roar JSON-HAL for our API now and would like to become conformant with the new JSON-HAL specs, including curies, which we need for supporting parameterized queries without breaking spec.

We also have some other internal extensions we've made to roar that might make sense to merge in or publish as an additional extension gem, perhaps we can discuss both at some point if it makes sense.

Problem inferring representer class name with namespaced controllers

Loving the gem so far, but I did run into a problem.

I have an endpoint named Api::V1::ShopsController. In my #show action, ControllerAdditions#representer_name_for will use the model name for infer the representer name. However, in the case of an array, it will use a camelized controller path.

This means that for name spaced controllers, you have to put your representers in the same namespace as the controller when dealing with collections, but not in a namespace when you're dealing with single objects.

For example, in my case, my representers are named:

Api::V1::ShopsPresenter
ShopPresenter

I can take a look at doing a patch / pull request later, but I'm not yet really familiar with the codebase

require "roar/representer/json/hal" fails with roar-rails, but not with roar alone

I've got an API client written with roar-rails and embedded in a rails application. I'm trying to factor it out to its own gem so I can reuse it in other apps (including non-rails apps & scripts). It uses Roar::Representer::JSON::HAL for its representers, so in the separate gem I require the appropriate file to make that mixin available:

require "roar/representer/json/hal"

When I try to use the gem in the original app, I get an exception like this:

/Users/rsutphin/.rvm/gems/ruby-2.0.0-p481/gems/roar-rails-0.1.6/lib/roar/rails/hal.rb:3:in `<top (required)>': uninitialized constant Roar::Representer::JSON::HAL (NameError)
    from /Users/rsutphin/.rvm/gems/ruby-2.0.0-p481/gems/roar-0.12.9/lib/roar/representer/json/hal.rb:44:in `<module:JSON>'
    from /Users/rsutphin/.rvm/gems/ruby-2.0.0-p481/gems/roar-0.12.9/lib/roar/representer/json/hal.rb:4:in `<module:Representer>'
    from /Users/rsutphin/.rvm/gems/ruby-2.0.0-p481/gems/roar-0.12.9/lib/roar/representer/json/hal.rb:3:in `<top (required)>'

... originating on the line in the gem that requires "roar/representer/json/hal". I've reproduced this in a clean rails app: reproduction script.

In the rails app, if you just reference the constant Roar::Representer::JSON::HAL, everything works fine (which is why the client was working when it was embedded in the app). But this doesn't work with plain Roar, so I can't use this approach in my shared gem.

The problem appears to be a conflict caused by an autoload definition in roar-rails. This autoload tells ruby to require 'roar/rails/hal' to get Roar::Representer::JSON::HAL. When roar/representer/json/hal attempts to define module HAL, the autoload is triggered, resulting in roar/rails/hal being evaluated from within roar/representer/json/hal, before Roar::Representer::JSON::HAL is defined. Since roar/representer/json/hal is already being required, the require for it from within roar/rails/hal is ignored, and the subsequent attempt to access Roar::Representer::JSON::HAL fails.

I'm trying to figure out how to work around this. I wonder if the best solution might just to be to define the to_hal and from_hal aliases in Roar instead, then remove roar/rails/hal from the picture.

Wrong status code for successful POST request when using respond_with

Seems to return a 200 status code rather than a 201 for successful post requests in the example below. Respond_with's normal behavior would be to set the status code based on the HTTP verb and whether the request was successful.

class PostsController < ApplicationController
  include Roar::Rails::ControllerAdditions

  def create
    post = Post.create(params[:post])

    respond_with post, represent_with: PostRepresenter
  end
end

You can override the status code by passing status: 201 but that's not ideal to do every time. I was going to take a shot at fixing this myself but had trouble getting any tests running locally :/

Collections are not wrapped with with the right root tag in XML

When serializing using XML, the root tag is incorrect. It is either or depending on the version of rails. I believe this ultimately tracks back to here: https://github.com/apotonick/representable/blob/master/lib/representable.rb#L58

The self.class.name does not properly track back to the model when it is a collection. A work-around for now is to place self.representation_wrap = :collection_name in the collection representer. Maybe this can be set by roar-rails, since I don't think representable is the right place to fix it.

Fallback to default responder when there are no representers defined

I'd like to propose a feature

When defining a controller-wide responder using:

  def self.responder
    Class.new(super).send :include, Roar::Rails::Responder
  end

I get uninitialized constant NilClassRepresenter whenever respond_with nil is called. Wouldn't it be nice if Roar::Rails::Responder fallback to default rails responder when there aren't representers defined for the resource? With that in mind, any call to respond_with nil would call nil.to_json.

When a more complex class is passed to respond_with, say Post, and there arent a PostRepresenter defined, the responder would fallback to the default rails responder calling post.to_json or post.to_xml on the resource instance.

respond_with funkiness

Hey again! ๐Ÿ˜„

So after messing around a bit, we noticed that when using respond_with @something only an empty string was returned. render :json however works flawlessly. Any idea what that might be about?

Add support for Rails 4.1

I created 2 new Rails apps with the standard settings, one with Rails v.4.0.4 and the other with Rails v.4.1.0. In both, I changed the Gemfile to use roar-rails instead of jbuilder and ran bundle. Then, I applied the changes detailed at https://gist.github.com/whittle/76df2f1799713977ea4d to both apps, ran bundle exec rails server and queried them with curl -H "Accept: application/hal+json" http://localhost:3000/site_faqs/1

The Rails v.4.0.4 app correctly responds with a JSON document. The Rails v.4.1.0 app throws the exception ActionController::MissingRenderer (No renderer defined for format: hal)

Fail to evaluate ActiveRecord::Relation

I have an action such as:

  class FriendshipsController < Api::ApiController
    def index
      user = User.find(params[:user_id])
      friendships = friendships.page(params[:page])

      respond_with(:api, friendships)
    end
  end

As you can see, respond_with receives an ActiveRecord::Relation. The problem is roar-rails fails to infer the collection representer name because when we issue model.kind_of?(Array) here it return false.

The error raised is:

NameError:
       uninitialized constant ActiveRecord::RelationRepresenter
     # ./app/controllers/api/friendships_controller.rb:5:in `index'

Collection representers not rendering json correctly

I have a collection and entity representer for a simple post model. Neither the collection nor the entities are being represented. Any help would be appreciated. Here is my setup:

class PostsController < ApplicationController
  include Roar::Rails::ControllerAdditions
  respond_to :json

  represents :json, :entity => Representers::PostRepresenter, :collection =>  Representers::PostCollectionRepresenter

  def index
    respond_with Post.all
  end
end
require 'roar/representer/json'
require 'roar/representer/json/hal'
require 'roar/representer/feature/hypermedia'

module Representers
  module PostCollectionRepresenter
    include Roar::Representer::JSON
    include Roar::Representer::JSON::HAL
    include Roar::Representer::Feature::Hypermedia

    collection :posts, :class => Post, :extend => Representers::PostRepresenter
  end
end
require 'roar/representer/json'
require 'roar/representer/json/hal'
require 'roar/representer/feature/hypermedia'

module Representers
  module PostRepresenter
    include Roar::Representer::JSON
    include Roar::Representer::JSON::HAL
    include Roar::Representer::Feature::Hypermedia

    property :id
    property :title
    property :body
    property :user_id
    property :created_at
    property :updated_at
  end
end

Validation errors

Doing a put

@event.put params[:id], 'application/json'

This is returned

{
    "errors": {
        "name": [
            "can't be blank"
        ]
    }
}

Which is what I expected, but the roar-rails gem does not capture this. I was hoping since @event/Event is a active model that that I could go @event.errors or have access to them some where.

With out having "builder.response :raise_error" it looks like it just works but does not update the record.

default_url_options as a configuration requirement

@apotonick just a quick one.. working on a new API and wondering about the reasons why the default_url_options config option is a requirement and you weren't able to delegate this to Rails by default?

also - you dropped hal from the readme! ๐Ÿ˜ข

Rails Generators - Coder Needed!

We could need generators for creating representers in Rails. Anyone?

Usage could be like

rails g roar:representer singer name band:Band albums:collection

This API is subject to discussion, what kind of properties are common and could be handy being generated from the command line?

Examples for writing and testing generators can be found here:

Access controller/helper methods

It would be great to be able to access controller and helper methods from within the responders. I tried including the helper modules I wanted, but of course that didn't work, because those are instance methods, and property some_method(:whatever) is going to try evaluating immediately when the responder file is loaded by ruby.

Am I going about this wrong, or is the functionality not there?

ControllerAdditions::Render#render doesn't pass options like :callback through usefully to super #render

This might be an expectation problem, but the following does not work as I thought it would. I want to use my representer to return an object as JSON, and following (old) conventions, if a callback is specified in my request, I assume I am to return a JSONP-suitable call of that function with my JSON-represented thing. However, I can't get the roar-rails rendering paths to do this. I have to resort to explicitly calling the representer and then using render rather than any of the respond_with fanciness that roar-rails patches.

class API::V1::PublicFeedsController < ApplicationController
  include Roar::Rails::ControllerAdditions
  # If this is included, #render is hijacked and won't go through normal ActionController processing
  #include Roar::Rails::ControllerAdditions::Render

  respond_to :json

  def show
    @thing = Thing.find(123)
    if params[:callback]
      # These do not work
      # option 1 -- respond_with @thing, represent_with: ::API::ThingRepresenter, callback: params['callback']
      # option 2 -- render json: @thing, represent_with: ::API::ThingRepresenter, callback: params['callback']
      #... but this does
      render json: @thing.extend(::API::ThingRepresenter), callback: params['callback']
    else
      # don't need callback so this works okay
      respond_with @thing, represent_with: ::API::ThingRepresenter
    end
end

I think the patched render needs to pass all the remaining options besides represent_with through to super, but my attempt to have it do so was not sufficient to combine the behaviors such that option 2 above was successful. If that's the right track, I can keep pursuing it.

Consuming URLs for Relationships

When updating or creating a resource in RESTful hypermedia APIs I have seen them designed in such a way where you relate resources via URLs instead of id's. To make up an example and follow your Fruit Bowl analogy, if I wanted to create a fruit as a part of a bowl I would do something along the lines of:

POST "http://fruits"
{ 
  "title": "Apple"
  "bowl": "http://bowls/1"
}

Behind the scenes, this would deserialize the passed in bowl url (http://bowls/1) to the bowl object with id 1 and relate the new fruit to the existing bowl.

Would really appreciate some insight on how I would go about this in roar-rails. :)

Collection representer example

Hi,

I'm trying to write a collection representer (for index action) but I think I'm doing something wrong because I get this error:

> undefined method `_id' for #<Array:0x00000004ccf868>

This sounds logical since my index action returns an array and not an model instance, but how should my representer handle this ?

For now, my representer just contains property :id

Using :represent_items_with works fine but the doc suggests this is rather deprecated...

I haven't seen any collection representer example. Could you please provide one or tell me what I'm doing wrong ?

Thanks !

Undefined method `merge'

Wohoo first issue...

lib/representers/user_representer.rb

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

 property :username

   link :self do
     api_user_url self
    end
end

controllers/api/users_controller.rb

class Api::UsersController < ApplicationController

def show
    @user = User.find(params[:id])
    @user.extend UserRepresenter
    respond_with @user
 end
end

api_user_url is a valid route

Undefined method `merge' for nil:NilClass thrown in api_user_url self . Actually I don't find this merge method...
More and more I think, these problems arise because I don't use ActiveResource.

Capybara test failing with undefined method `collect' for nil:NilClass thrown by representable-1.8.5/lib/representable/bindings/hash_bindings.rb:40:in 'serialize'

I have persistent intermittent undefined method 'collect' for nil:NilClass failures in rSpec/Capybara request specs. Different tests fail, but it is usually the failure to find a button or the failure to reach a page with a button click which causes them. Their are some other weird failures as well, but this one is the only stack trace I have right now.

I can point you to the app if it would help. Thanks!

Failures:

  1) User Pages & Sessions: new registration with invalid email and password 
     Failure/Error: page.find(:xpath, '//input[@name="sign_up_new_registration"]').click
     NoMethodError:
       undefined method `collect' for nil:NilClass
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/representable-1.8.5/lib/representable/bindings/hash_bindings.rb:40:in `serialize'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/representable-1.8.5/lib/representable/bindings/hash_bindings.rb:22:in `write'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/representable-1.8.5/lib/representable/hash/collection.rb:23:in `create_representation_with'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/representable-1.8.5/lib/representable/hash.rb:39:in `to_hash'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/roar-0.12.7/lib/roar/representer/json.rb:26:in `to_hash'
     # /Users/reconstructions/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/tmpdir.rb:126:in `try_convert'
     # /Users/reconstructions/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/tmpdir.rb:126:in `create'
     # /Users/reconstructions/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/tempfile.rb:136:in `initialize'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:8:in `new'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:8:in `initialize'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:42:in `new'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:42:in `block in params'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/nokogiri-1.6.2.1/lib/nokogiri/xml/node_set.rb:237:in `block in each'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/nokogiri-1.6.2.1/lib/nokogiri/xml/node_set.rb:236:in `upto'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/nokogiri-1.6.2.1/lib/nokogiri/xml/node_set.rb:236:in `each'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:27:in `map'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:27:in `params'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/form.rb:74:in `submit'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/rack_test/node.rb:56:in `click'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/node/element.rb:118:in `block in click'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/node/base.rb:81:in `synchronize'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/capybara-2.2.1/lib/capybara/node/element.rb:118:in `click'
     # ./spec/requests/users_pages_spec.rb:140:in `block (4 levels) in <top (required)>'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:237:in `instance_eval'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:237:in `instance_eval'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:21:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:85:in `block in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:85:in `each'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:85:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:446:in `run_hook'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:345:in `run_before_each_hooks'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:294:in `run_before_each'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:113:in `block in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:179:in `call'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:179:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_exec'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_eval_with_args'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:247:in `instance_eval_with_args'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:106:in `block (2 levels) in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:179:in `call'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:179:in `run'
     # ./spec/spec_helper.rb:36:in `block (2 levels) in <top (required)>'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_exec'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/extensions/instance_eval_with_args.rb:16:in `instance_eval_with_args'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:247:in `instance_eval_with_args'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:106:in `block (2 levels) in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:108:in `call'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:108:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/hooks.rb:446:in `run_hook'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:340:in `run_around_each_hooks'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:256:in `with_around_each_hooks'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example.rb:111:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:390:in `block in run_examples'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:386:in `map'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:386:in `run_examples'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:371:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `block in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `map'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `block in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `map'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/example_group.rb:372:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `map'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:28:in `block in run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/reporter.rb:58:in `report'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/command_line.rb:25:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/runner.rb:80:in `run'
     # /Users/reconstructions/.rvm/gems/ruby-2.1.0@sfrc/gems/rspec-core-2.14.8/lib/rspec/core/runner.rb:17:in `block in autorun'

Not compatible with Roar 1.0

Today I installed roar and roar-rails gems and I was going to generate a representer when I got this

/home/diego/.rvm/gems/ruby-2.1.2@sistema-mip/gems/roar-rails-0.1.6/lib/roar-rails.rb:10:in module:Representer': cannot load such file -- roar/representer/json (LoadError)`

Then I did some research and it turns out the last version (v0.12.x) before v1.0.0 release had another directory layout in which the faulty autoload call made sense.

Is there any workaround?

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.