Padrino model from json data - ruby

I have been looking at Padrino for a project I am working on, and it seems a great fit, as I would ideally be wanting to support data being sent and received as json.
However I am wondering if there is any automated helper or functionality built in to take data from a post request (or other request) and put that data into the model without having to write custom logic for each model to process the data?
In the Blog example they briefly skim over this but just seem to pass the parameter data into the initilizer of their Post model, making me assume that it just magically knows what to do with everything... Not sure if this is the case, and if so is it Padrino functionality or ActiveRecord (as thats what they seem to use in the example).
I know I can use ActiveSupport for JSON based encoding/decoding but this just gives me a raw object, and as the storage concerns for each model reside within the main model class I would need to use a mixin or something to achieve this, which seems nasty.
Are there any good patterns/functionality around doing this already?

Yep, you can use provides and each response object will call to_json i.e:
get :action, :provides => :json do
#colletion = MyCollection.all
render #collection # will call #collection.to_json
end
Here an example of an ugly code that fills certain models.
# Gemfile
gem 'json' # note that there are better and faster gems like yajl
# controller
post "/update/:model/:id", :provides => :json do
if %w(Account Post Category).include?(params[:model])
klass = params[:model].constantize
klass.find(params[:id])
klass.update_attributes(JSON.parse(params[:attributes]))
end
end
Finally if you POST a request like:
attributes = { :name => "Foo", :category_id => 2 }.to_json
http://localhost:3000/Account/12?attributes=#{attributes}
You'll be able to update record 12 of the Account Model.

Related

Validation & External APIs - Rescue in Controller or Fatten Model validations?

I am using Restforce to query records from a remote salesforce instance. The user simply has to put in a valid UID for the record they want to query.
Restforce uses Faraday middleware to deal with http requests - and raises a Faraday::ResourceNotFound error if I request something that cannot be located in the remote salesforce database.
Question
Where should I validate user input?
I have two ideas but i'm not sure of the consequences to each... and I'm trying to work out how to stick best to the fat model - skinny controller best practice.
Check for successful query at the application controller level
Requests save the UID to a simple ActiveRecord model #record_request. My create method can fire a query, check for an error and flash/redirect the user if needed.
# app/controllers/record_requests_controller
def create
#record_request = current_user.record_requests.new(record_request_params)
# Check to see if CHAIN number exists
if #record_request.restforce.find("Contact", #record_request.chain_number, 'ClientID__c')
# If it gets past that do standard validation checks
if #record_request.save
flash[:success] = 'Record request was successfully created.'
redirect_to record_requests_path
else
render :new
end
end
end
Then over in the ApplicationController I've got a rescue method setup
# app/controllers/application_controller
rescue_from Faraday::ResourceNotFound, with: :resource_not_found
private
def resource_not_found
flash[:alert] = 'Cannot find resource on Salesforce'
redirect_to(:back)
end
This works! And seems fine... but...
Model level validation?
My gut tells me this is a validation and should be validated on the model level, what if there's a bug and something sneaks into my database? Should this all just be checked at the if #record_request.save moment?
If so... how would i get model level code to handle validation AND be able to fire off an external (OAuth authenticated) API request without breaking the MVC.
What are the implications to either, and how might I do better?
I think the best way to use model level validation something like this:
validate :something
def something
errors.add(:field, 'error message') unless RestClient.check_something
end
Where RestClient is singleton object in /lib folder. This will allow to keep controller clean.

Render a view's output later via a delayed_job

If I render html I get html to the browser which works great. However, how can I get a route's response (the html) when being called in a module or class.
I need to do this because I'm sending documents to DocRaptor and rather than store the markup/html in a db column I would like to instead store record IDs and create the markup when the job executes.
A possible solution is using Ruby's HTTP library, Httparty or wget or something and open up the route and use the response.body. Before doing so I thought I'd ask around.
Thanks!
-- Update --
Here's something like what I ended up going with:
Quick tip - in case anyone does this and need their helper methods you need to extend AV with ApplicationHelper:
Here's something like what I ended up doing:
av = ActionView::Base.new()
av.view_paths = ActionController::Base.view_paths
av.extend ApplicationHelper #or any other helpers your template may need
body = av.render(:template => "orders/receipt.html.erb",:locals => {:order => order})
Link:
http://www.rigelgroupllc.com/blog/2011/09/22/render-rails3-views-outside-of-your-controllers/
check this question out, it contains the code probably want in an answer:
Rails 3 > Rendering views in rake task

Mocking methods in google ruby api client

I am trying to mock some methods that use the google-api-ruby-client to make some testing without actually calling the api. Authentication and client and activities methods are taken from the example found on the github page (see link above), which is why I skipped it here.
The method from the example is the following:
def activities
result = client.execute(
:api_method => plus.activities.list,
:parameters => {'collection' => 'public', 'userId' => 'me'}
)
return result.data
end
I previously tried to stub the client (even chained with the execute) methods, however this results in authorization requests for oauth, which the gem uses underneath followed by mocks for the plus.activities.list methods. Is there a way to directly mock client.exectute to return something useful while skipping the whole chain?
I am not sure that I understand your problem correctly, but maybe something a little bit crazy will work
I assume that your method is in Client model so maybe something like that will work
Client.stub_chain(:client, :execute).and_return(true)
Of course if you model have different name you have to adjust. I am not sure but you can give it a try
Checkout their spec helper:
https://github.com/google/google-api-ruby-client/blob/master/spec/spec_helper.rb
And how they do the tests:
https://github.com/google/google-api-ruby-client/blob/master/spec/google/api_client_spec.rb

How can I render an image with Padrino?

I need to create image files on-the-fly in my controller with RMagick and send them to browser. Looks like it's very simple, but I can't find a way. I've tried just simply render them, but it fails due to data is binary. I've also tried to use send_data, but Padrino says it doesn't know about such method.
So, what have I missed? How can I solve this problem?
Researching how to send files through Padrino controller I've found this question and it helps me to reach my goal.
The send_data method is a Sinatra request-method which has been removed in the version 1.0: https://github.com/sinatra/sinatra/blob/1.0/CHANGES#L108
I'm using the Padrino version 0.10.7 and my action have became into this:
get :screenshot, :provides => :jpg do
...
File.open("path/to/file", "r").readlines
end
according to sinatra api you don't need this anymore.
get :image, with: id, provides: :png do
img = Image.find(params[:id])
img.binary_data_or_so
end
basically is the same of:
get '/send_binarydata' do
content_type 'image/png'
\x01\x02\x03
end

Trouble creating custom routes in Ruby on Rails 3.1

I can't seem to set up a custom URL. All the RESTful routes work fine, but I can't figure out how to simply add /:unique_url to the existing routes, which I create in the model (a simple 4 character random string) and will serve as the "permalink" of sorts.
Routes.rb
resources :treks
match ':unique_url' => 'treks#mobile'
Controller
.
.
def mobile
#trek = trek.find(params[:id])
end
Is this because I'm trying to define a custom action on an existing resource? Can I not create custom methods on the same controller as one with a resource?
By the way, when I change routes.rb to match 'treks/:id/:unique_url' => treks#mobile it works fine, but I just want the url to simply be /:unique_url
Update It seems like find_by_[parameter] is the way to go...
I've been playing in console and I can't seem to get any methods to come forward...I can run Trek.last.fullname for example, but cannot run #trek = Trek.last...and then call...#trek.lastname for example. Any clues why? I think this is my issue.
So is there a field on Trek which stores its unique url? If so you should be doing something like this:
#trek = Trek.find_by_url(params[:unique_url])
trek.find_by_unique_url( params[:unique_url] ) # should do the trick
#pruett no, the find_by_XXX methods are generated on-the-fly via Ruby's method_missing call! So instead of XXX you can use any of the attributes which you defined in a model.
You can even go as far as listing multiple attributes, such as:
find_by_name_and_unique_url( the_name, the_unigue_url)
Check these pages:
http://guides.rubyonrails.org/active_record_querying.html
http://m.onkey.org/active-record-query-interface
if you get a undefined method ... for nil:NilClass , it means that the object you are trying to call that method on does not exist, e.g. is nil.
You probably just missed to put an if-statement before that line to make sure the object is non-nil
Hmm. I usually would do something like this:
map.connect "/:unique_url", :controller => "treks", :action => "mobile"
Then in that controller the ID isn't going to be applicable.. you'd need to change it to something like this:
def mobile
#trek = trek.find_by_unique_url(params[:unique_url])
end
(that's if unique_url is the column to search under)

Resources