I am completely new to RubyOnRails so please be careful for even simplest mistakes I could have done. I developed a simple app that displayes some data from a .csv file. Here my code:
class UsersController < ApplicationController
def index
#users = getUsers
end
private def getUsers
invoker = ProviderInvoker.new(UserProvider.new("app/data/user_data.csv"))
invoker.getData
end
end
My app displaying the data as I wanted. No problem so far but I want to create and use an internal API for providing data so I am changing my UsersController into this:
require "net/http"
class UsersController < ApplicationController
def index
uri = URI("http://localhost:3000/api/users")
response = Net::HTTP.get(uri)
#users = JSON.parse(response)
end
end
and created another UsersController under app/controllers/api
class Api::UsersController < ApplicationController
def index
invoker = ProviderInvoker.new(UserProvider.new("app/data/user_data.csv"))
data = invoker.getData
render json: data
end
end
And lastly I created a serializer under app/serializers:
class UsersSerializer < ActiveModel::Serializer
attributes :id, :first_name, :last_name, :email, :user_name
end
When I go to my index page I get an error like
No serializer found for resource: #< User:0x07aaebb0 #id="1", #first_name="Susan", #last_name="Gomez", #email="sgomez0#cpanel. net", #user_name="sgomez0">
Obviously there is something wrong with my serializer but I can't manage to find what.
It is a good practice to put serializer under Api module also, so you won't be confused which serializer you use.
As a try you may set serializer manually in controller, like this:
def index
invoker = ProviderInvoker.new(UserProvider.new("app/data/user_data.csv"))
data = invoker.getData
render json: data, each_serializer: UsersSerializer, adapter: :json
end
Related
How can I create an app in Rails that will let me input search parameters, which are then passed to an external API to perform the search, and then display those results in my app. I'm trying to use HTTParty to achieve this but I'm a bit lost. I've tried creating a class method in app/services and accessing it in my controller, and then calling the instance variable in my view. At the moment it's throwing a Routing Error uninitialized constant ResultsController::Api. Would be super grateful for some help.
services/Api.rb
class Api
include HTTParty
base_uri "search.example.com"
attr_accessor :name
def initialize(name)
self.name = name
end
def self.find(name)
response = get("/results&q=#{name}")
self.new(response["name"])
end
results_controller.rb
class ResultsController < ApplicationController
include Api
def index
#results = Api.find('test')
end
end
Routes:
Rails.application.routes.draw do
resources :results
root 'results#index'
end
You're almost right, just need some changes here. At first, rename Api.rb to api.rb - by convention, all files should be named in lower snake_case
class Api
include HTTParty
base_uri "http://search.spoonflower.com/searchv2"
def find(name)
self.class.get("/designs", query: { q: name }).parsed_response
end
end
class ResultsController < ApplicationController
def index
# here you get some json structure that you can display in the view
#results = Api.new.find('test')['results']
end
end
I'm trying to get my app to return in lowercase camelcase for eventual JSON API formatting.
I've installed gem 'active_model_serializers' and created a new initializer with the following code in it:
ActiveModelSerializers.config.adapter = :json_api
ActiveModelSerializers.config.key_transform = :camel_lower
Then I have a small API that returns json, as all of the best internet applications do:
class Api::V1::UsersController < API::V1::BaseController
def sky
#user = User.find_by_id(params[:user_id])
if #user
obj = {
sky: {
sectors: #user.sectors,
slots: #user.slots
}
}
render json: obj
else
raise "Unable to get Sky"
end
end
More on the API controller inheritance pattern: class API::V1::BaseController < ActionController::Base
The Problem
In my API response, things are still snake cased and I see this error in the console [active_model_serializers] Rendered ActiveModel::Serializer::Null but my research has led me to a dead end as to what to do.
Any suggestions would be very welcome. Thanks!
The problem is you're not calling an active record serializer in your controller, so those config settings aren't being picked up.
Solution:
Create a UserSerializer in "app/serializers/user_serializer.rb" that should look something like this:
class UserSerializer < ActiveModel::Serializer
attributes :id
has_many :sectors
has_many :slots
end
as well as similarly structured SectorSerializer and a SlotSerializer with all of the attributes you want from each (Here are the getting started docs and the general syntax docs for active record serializers)
Then in your controller:
class Api::V1::UsersController < API::V1::BaseController
def sky
#user = User.includes(:sectors, :slots).find_by_id(params[:user_id])
if #user
render json: #user
else
raise "Unable to get Sky"
end
end
end
Which will eager load :slots and :sectors with includes then calls your UserSerializer using your camel_case config options.
In your controller put respond_to :json
class Api::V1::UsersController < API::V1::BaseController
respond_to :json
and in the action put same that you have
def sky
...
render json: obj
...
end
and define in base controller
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
From this pull request (*) it looks like you should be able to configure key_format = :lower_camel in your ActiveModel::Serializers config.
(*) https://github.com/rails-api/active_model_serializers/pull/534
i think it helps you. in my case i use gem 'active_model_serializers', '~> 0.10.5' which depends on case_transform (>= 0.2)
and in rails console i can do
CaseTransform.camel_lower(initially_serialized_output)
=> {:name=>"My Company", :jsonThings=>{:rating=>8, :aKey=>{:aSubkey=>{:anotherKey=>"value"}}}}
my research was by steps:
https://github.com/rails-api/active_model_serializers/pull/1993 => https://github.com/NullVoxPopuli/case_transform-rust-extensions
did you find this?
I have api with version system.
My controller
module Api;module V1
class PlansController < ApplicationController
def show
#plan = Plan.find(params[:id])
render json: #plan
end
end
end;end
I have folder serializers/api/v1 where i have plan_serializer.rb
module Api;module V1
class PlanSerializer < ActiveModel::Serializer
attributes :name, :amount, :days
end
end;end
But its not serializing json response automatically.
Please tell me what wrong am I doing ?
I also tried adding
class ApplicationController < ActionController::API
include ActionController::Serialization
but still its not working.
If I am doing
render json: #plan, serializer: V1::PlanSerializer
then it is working but I want it to work without adding serializer in every render.
Please tell me solution.
It may work if you override render.
class ApplicationController < ActionController::API
include ActionController::Serialization
DEFAULT_SERIALIZER= V1::PlanSerializer
def render options, &block
options[:serializer]= DEFAULT_SERIALIZER unless options[:serializer]
super options, &block
end
end
I have the code that I need to do all the scraping and then printing the results to the console, but, I am confused about how to use it in an app.
The way it's supposed to work is through the list#new action I take user input for one parameter, :url. This URL is then passed to the scraping code, which obtains all the additional parameters and adds everything to Postgres tables. Using all of this newly acquired data, a new list is rendered.
The questions that I have:
the lists controller:
class UsersController < ApplicationController
.
.
.
def create
#list = List.new ( #what goes in here?
#only one param comes from the user
if #list.save
#how to set it up so that the save is successful
#only if the extra params have been scraped?
.
.
.
I assume this will go into the models/list.rb:
class List < ActiveRecord::Base
require 'open-uri'
url = #assuming that the url is proper and for something this code is supposed to scrape
#is it better to add the url to db first or send it straight from the input
#and how is that defined here
doc = Nokogiri::HTML(open(url))
.
.
.
Could you give me some guidance here, please?
The services file:
class ScrapingService
require 'open-uri'
require 'nokogiri'
def initialize(list)
#list = list
end
url = :url
doc = Nokogiri::HTML(open(url))
name = doc.at_css(".currentverylong").text
author = doc.at_css(".user").text
def scraped_successfully?
if name != nil && author != nil
true
else
false
end
end
private
attr_reader :list
end
Some questions that I have are:
How do I properly introduce :url into HTML(open...? The way I have it now throws no implicit conversion of Symbol into String error.
The part where :url along with :name and :author are supposed to be saved into one db entry is really murky.
Any article suggestions on this stuff are always welcome.
app/controllers/lists_controller.rb
class UsersController < ApplicationController
def create
#list = List.new(list_params)
if #list.save
redirect_to #list
else
render :new
end
private
#Assuming that you are using Rails 4 or the strong_params gem
def list_params
params.require(:list).permit(:url)
end
end
app/models/list.rb
class List < ActiveRecord::Base
# This runs only when you try to create a list. If you want to run this
# validation when the user updates it, the remove the on: :create
before_validation :ensure_website_is_scrapable, on: :create
private
def ensure_website_is_scrapable
if ScrapingService.new(self).scraped_successfully?
true
else
errors.add(:url, 'The website is not scrapable')
end
end
end
app/services/scraping_service.rb
class ScrapingService
def initialize(list)
#list = list
end
def scraped_successfully?
# Do the scraping logic here and return true if it was successful or false otherwise
# Of course split the implementation to smaller methods
end
private
attr_reader :list
end
Please find here is my controller and json file
//controller file
module Api
module V1
class CouponsController < ApplicationController
respond_to :json
def show
#coupon = Coupon.find(params[:id])
render "/coupons/show.json.jbuilder"
end
end
end
end
//show.json.jbuilder
json.extract! #coupon, :id, :category
Maybe you need to rewrite the head in controller like this:
module Api::V1::CouponsController < ApplicationController
Because your current write assumes that you have Api::V1::ApplicationController. And Api::V1::CouponsController is inherited from it, not from ApplicationController.