host and port in a grape entity - ruby

Im trying to get the host and port in a grape-entity when generating an url
class Person < Grape::Entity
expose :url do |person,options|
"http://#{host_somehow}/somepath/#{person.id}"
end
end
I´ve tried examining the options hash but the 'env' hash is empty.

Following works for me, Grape 0.6.0, Grape-Entity 0.3.0, Ruby 2.0.0:
require 'grape'
require 'grape-entity'
# in reality this would be Active Record, Data Mapper, whatever
module Model
class Person
attr_accessor :identity, :name
def initialize i, n
#identity = i
#name = n
end
end
end
module APIView
class Person < Grape::Entity
expose :name
expose(:url) do |person,opts|
"http://#{opts[:env]['HTTP_HOST']}" +
"/api/v1/people/id/#{person.identity}"
end
end
end
class MyApp < Grape::API
prefix 'api'
version 'v1'
format :json
resource :people do
get "id/:identity" do
person = Model::Person.new( params['identity'], "Fred" )
present person, :with => APIView::Person
end
end
end
Quick test:
curl http://127.0.0.1:8090/api/v1/people/id/90
=> {"name":"Fred","url":"http://127.0.0.1:8090/api/v1/people/id/90"}

Finally ended up with sending the host as a option to the entity
class Person < Grape::Entity
expose :url do |person,options|
"http://#{options[:host]}/somepath/#{person.id}"
end
end
get '/' do
#persons = Person.all
present #persons, with: Person, host: request.host_with_port
end

Related

How to filter by foreign id and local attribute via belongs_to?

The following models are linked via belongs_to:
require 'mongoid'
class Sensor
include Mongoid::Document
field :sensor_id, type: String
validates_uniqueness_of :sensor_id
end
...
require 'mongoid'
require_relative 'sensor.rb'
class SensorData
include Mongoid::Document
belongs_to :sensor
field :date, type: Date
field :ozonMax1h, type: Float
field :ozonMax8hMittel, type: Float
index({ date: 1, sensor_id: 1 }, { unique: true })
end
Here is a Sinatra app which provides a few API paths based on these models:
require 'sinatra'
require 'csv'
require_relative './models/sensor.rb'
require_relative './models/sensor_data.rb'
configure do
Mongoid.load!('./mongoid.yml')
end
def prepare_for_export(sensor_data)
converted_data = sensor_data.asc(:date).map do |e|
{
sensor_id: e.sensor.nil? ? :null : e.sensor.sensor_id,
date: e.date,
ozonMax1h: e.ozonMax1h,
ozonMax8hMittel: e.ozonMax8hMittel
}
end
converted_data
end
def convert_to_json(sensor_data)
prepare_for_export(sensor_data).to_json
end
def convert_to_csv(sensor_data)
data = prepare_for_export sensor_data
csv_string = CSV.generate do |csv|
csv << data.first.keys
data.each do |hash|
csv << hash.values
end
end
csv_string
end
def get_recent
max_date = SensorData.max(:date)
SensorData.where(date: max_date)
end
def get_for_year(year)
SensorData.where(:date.gte => Date.new(year, 1, 1)).where(:date.lte => Date.new(year, 12, 31))
end
def get_for_sensor(sensor)
foo = SensorData.where(sensor_id: sensor)
puts "hallo"
return foo
end
get '/api/v1/stations' do
content_type :json
Sensor.all.map { |e| {sensor_id: e.sensor_id} }.to_json
end
get '/api/v1/sensordata/:year' do
content_type :json
convert_to_json get_for_year(params[:year].to_i)
end
get '/api/v1/sensordata/:year/csv' do
convert_to_csv get_for_year(params[:year].to_i)
end
get '/api/v1/recent' do
content_type :json
convert_to_json get_recent
end
I would like to output the SensorData for a particular sensor such as here:
/api/v1/stations/:sensor_id/sensordata/:year/csv
I am not sure what you are trying to do or even if you are still looking for an answer but here it goes. Something seems wrong with the models in the example you have here. Sounds like part of what you are doing would work if Sensor knows about sensor_data. So might need to add this to Sensor class:
has_many :sensor_data
Though the singular of data is datum. The class would be expected to be SensorDatum. If you can't change it, you need to tell Mongoid the class_name to expect in the has_many is actuall SensorData.
You CAN specify foreign_key in Mongoid with belongs_to.
You CANNOT filter with the belongs_to like you can with ActiveRecord, but you can use scopes outside of the belongs_to to get the same effect. Exampe:
belongs_to :sensor
scope :for_year, -> (year) { where(:date.gte => Date.new(2015,1,1)).where(:date.lte => Date.new(2015, 12, 31))}
or
belongs_to :sensor
def self.for_year year
where(:date.gte => Date.new(year,1,1)).where(:date.lte => Date.new(year, 12, 31))
end
So your query would become something like this:
sensor = Sensor.find_by(sensor_id: params[:sensor_id])
sensor.sensor_data.for_year(2015)

How to achieve strong parameter protection without Rails?

I am developing a boilerplate web application with Goliath + Grape + Active Record 4.2 + Active Record Migrations. Here is my migration file
# db/migrate/20150519063210_create_albums.rb
class CreateAlbums < ActiveRecord::Migration
def change
create_table :albums do |t|
t.string :name
t.string :artist
t.string :genre
t.date :published_at
end
end
end
And my model
# app/models/Album
class Album < ActiveRecord::Base
end
And the Grape API
class ApiV1 < Grape::API
version 'v1', using: :path
format :json
resource 'albums' do
get '/' do
Album.all
end
post '/' do
Album.create(params[:album]) # <-- raises ActiveModel::ForbiddenAttributesError
end
end
end
When I call POST /v1/albums/ with some parameters, the application always raises ActiveModel::ForbiddenAttributesError. It seem that ActiveRecord wants ActionController::Parameters to be the arguments, but Grape gives it Hashie::Mash.
I've tried implementing a simple Rack middleware to convert env['params'] from a Hash to a ActionController::Parameters and using it after Goliath::Rack::Params, but Grape just sanitizes it out when the helper method params is called. I also tried implementing and using a Grape middleware to do the same thing and got the same result.
Is there any solution on this or I just have to down grade to ActiveRecord 3?
You could create a helper to generate an instance of ActionController::Parameters with your parameters:
require 'action_controller/metal/strong_parameters'
class ApiV1 < Grape::API
version 'v1', using: :path
format :json
helpers do
def albums_params
ActionController::Parameters.new(params).require(:album).permit(:attr1, :attr2)
end
end
resource 'albums' do
get '/' do
Album.all
end
post '/' do
Album.create(albums_params)
end
end
end
Or you can use the hashie-forbidden_attributes gem.

rails 3.1.0 belongs_to ActiveResource no longer working

I am upgrading from rails 3.0.7 to 3.1 and am having trouble getting my tests to pass. The problem occurs when I try to use a stubbed active resource object in a factory.
#employee.rb
class Employee < ActiveResource::Base; end
#task.rb
class Task < ActiveRecord::Base
belongs_to :employee
end
#factories.rb
Factory.define :employee do |e|
e.name "name"
end
Factory.define :task do |t|
t.employee { Factory.stub(:employee) }
end
On the console and in the spec stubbing an employee works. Referencing the stubbed employee object in a new task gives the following error.
Factory.create( :task, :employee => Factory.stub(:employee) )
NoMethodError:
undefined method `[]' for #<Employee:0x007fc06b1c7798>
EDIT
This is not a factory girl issue. I get the same error if I do the following in the console.
Task.new( :employee => Employee.first )
It must be related to how belongs_to maps the id column.
I didn't like the monkey patch so I created a module that I will include at initialization to extend ActiveRecord
module BelongsToActiveResource
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def ar_belongs_to( name, options = {} )
class_eval %(
def #{name}
##{name} ||= #{options[:class_name] || name.to_s.classify }.find( #{options[:foreign_key] || name.to_s + "_id" } )
end
def #{name}=(obj)
##{name} ||= obj
self.#{ options[:foreign_key] || name.to_s + "_id" } = ##{name}.#{ options[:primary_key ] || 'id' }
end
)
end
end
end
ActiveRecord::Base.class_eval { include BelongsToActiveResource }
Then in each ActiveRecord model would look like:
#task.rb
class Task < ActiveRecord::Base
ar_belongs_to :employee
end
Hope this helps someone

Testing before_create method in rspec and rails 3

I've looked into some tutes and all I saw were old posts on how to test before_create. Also it seems like they're all just testing that before_create was called i.e.:
#user = User.new
#user.should_receive(:method_name_called_by_before_create)
#user.send(:before_create) (sometimes they just do #user.save)
I want to actually test that my method worked and that it had assigned(and saved the variables) after creating the record.
Here are my models:
user.rb
class User < ActiveRecord::Base
has_one :character, :dependent => :destroy
after_create :generate_character
private
def generate_character
self.create_character(:name => "#{email}'s avatar")
end
end
and character.rb
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
response = api_call
#API CALL HERE
#set object attributes here
self.stat1 = calculate_stat1(response) + 5
self.stat2 = calculate_stat2(response) + 5
self.stat3 = calculate_stat3(response) + 5
end
def api_call
return api_call_response
end
end
I want to test that generate character indeed set the attributes without going online and calling the API call. Is this possible with rspec? I have a fixture of a json response so I was hoping I can stub out generate character and then use the fake response for testing.
Here's my character.spec:
describe Character do
before(:each) do
Character.any_instance.stub!(:api_call).and_return(fake_response.read)
#user = Factory(:user)
#character = #user.character
puts #character.inspect
end
def fake_response
File.open("spec/fixtures/api_response.json")
end
It prints out only 5 for each of the character's stats. Also I did a puts response in the generate_character method in character.rb and it still prints out the "real" api call.
I managed to do a puts in fake_response and it does goes through there but it also goes through the "real" api_call after, which makes the stub obsolete. How do I get through this?
A good approach here is extracting your api call into a self contained method. Something like this:
class Character < ActiveRecord::Base
belongs_to :user
before_create :generate_character
private
def generate_character
data = api_call
#set object attributes from data
end
def api_call
# returns a data structure
# resulting from the call
end
end
Then use RSpec's any_instance to stub the api_call method to return a fixed data structure
Character.any_instance.stub!(:api_call).and_return { {:id => 1, :attribute_one => "foo"} }
#user = User.create
#user.character.attribute_one.should == "foo"
for more info on any_instance check this commit

Sequel Model set_schema not found

Can any one volunteer why the class below fails?
... src/model/user.rb:18: undefined method `set_schema' for User:Class (NoMethodError)
I've looked in the Sequel-3.0 lib/ folder and the set_schema method is defined in a ClassMethods module.
I'm sure the solution is simple. I was thinking it should work "as is":
require 'sequel'
class User < Sequel::Model(:user)
set_schema do
set_primary_key :id
String :name
end
end
Recommended way ...
LOGGER = Object.new()
def LOGGER.method_missing( name, args )
puts "[#{name}] #{args}"
end
Sequel::Model.plugin(:schema) # I worked this out, but I can't find it documented
DB = Sequel.sqlite('sql_test.db', :loggers => [LOGGER] )
unless DB.table_exists?( :user )
DB.create_table :user do
set_primary_key :id
String :name
String :password
String :eMail
end #create_table
end #table exists
class User < Sequel::Model(:user)
The answer is to call up the plug-in for schema managing. Viz.
require 'sequel'
require 'logger'
LOGGER = Object.new()
def LOGGER.method_missing( name, args )
puts "[#{name}] #{args}"
end
**Sequel::Model.plugin(:schema)** # I still didn't find this documented
DB = Sequel.sqlite('sql_test.db', :loggers => [LOGGER] )
class User < Sequel::Model(:user)
set_schema do
set_primary_key :id
String :name
end
end
Yep Sequel::Model.plugin(:schema) worked for me too. Can't see it in the docs and I'm perplexed as to why, since I have another working project that uses set_schema without fuss.

Resources