:include based on condition on included table - ruby-on-rails

I have a model Client which has many :tours, association in it. I am using this code to provide all the clients with their tours,
render :json => Client.all(:include => :tours)
Now, the requirement has changed such that this object representation loads only tours of a user. Each tour is associated with an user with relation User has many :tours. I tried
render :json => Client.all(:include => :tours, :conditions => ["tours.user_id = ?", params[:user_id]])
This gives me the clients having tours of that user, but lists all the tours of these clients. But, I want only tours of that user only to be listed under each client. Can I do that using :includes?
client.rb
has_many :tours, :dependent => :destroy
user.rb
has_may :tours, :dependent => :destroy
Update
I thought it would be better to add an example to explain my problem. Suppose, there are 3 clients A, B, C.
Client A has 3 tours (all of user user1)
Client B has 4 tours (none of user user1)
Client C has 3 tours (1 of user user1, others of other users)
Now if we use my method to get the response for user1, it will be like this:
[
{
"name": "Client A",
....
"tours": [
{
"name": "Tour1",
....
},
{
"name": "Tour2",
......
},
{
"name": "Tour3",
.....
}
]
},
{
"name": "Client C",
....
"tours": [
{
"name": "Tour4",
...
},
{
"name": "Tour5",
...
},
{
"name": "Tour6",
...
}
]
}
]
You can see that Client B has been omitted, and Client A and Client C has been included, which is correct. But, for Client C, only Tour5 belongs to user user1. But, it has all its tours included. I want my response to omit Tour5 and Tour6, like this:
[
{
"name": "Client A",
....
"tours": [
{
"name": "Tour1",
....
},
{
"name": "Tour2",
......
},
{
"name": "Tour3",
.....
}
]
},
{
"name": "Client C",
....
"tours": [
{
"name": "Tour4",
...
}
]
}
]

I'm only guessing but have you tried something along the lines of:
render :json => Client.all(:include => {:tours => :user}, :conditions => ["users.id = ?", params[:user_id]])

Related

Rails scope conditions

Here are my scopes in the News model:
scope :category, -> (category_names) { joins(:category).where('categories.name IN (?)', category_names)}
scope :tag, -> (tag_name) { joins(:tags).where('tags.name = ?', tag_name)}
Here's the GET request:
localhost:3000/news?category[]=sit&tag_list=ipsum
[
{
"id": 8,
"tag_list": "ipsum",
"category": "sit"
},
{
"id": 9,
"tag_list": "",
"category": "sit"
}
]
My index action:
news = News.filter(params.slice(:tag_list, :category, :days_ago))
I want to get News which satisfy BOTH of the conditions - only those news should be displayed that have category="sit" AND tag_list="ipsum".
What's the best way to achieve this?
You should simply chain the scopes together. In your news controller:
News.category(params['categori']).tag(params['tag_list'])
You can simply link the scopes as so:
News.category(params[:category]).tag(params[:tag_list])

Rails permit nested array

I am trying to use accepts_nested_attributes_for in conjunction with a has_many association and having a lot of trouble...
Here is a simplified version of my user.rb:
class User < ActiveRecord::Base
...
has_many :user_permissions
accepts_nested_attributes_for :user_permissions
...
end
My user_permission.rb:
class UserPermission < ActiveRecord::Base
belongs_to :user
...
end
And my users_controller.rb:
class UsersController < ApiController
...
def update
#user.assign_attributes user_params
if #user.save
render partial: 'user', locals: { user: #user }
else
render json: {errors: #user.errors}.to_json, status: 500
end
end
...
private
def user_params
params.require(:user).permit(:first_name, :last_name, user_permissions_attributes: [ :user_id, :resource_id, :can_read, :can_update, :can_create, :can_delete ])
end
end
I am referencing this rails documentation on how to use accepts_nested_attributes_for with Strong Parameters.
However, when I 'puts user_params' from inside the users_controller this is all I see (no reference to the user_permissions):
{"first_name"=>"Joe", "last_name"=>"Shmoe"}
Here is an example of JSON I am submitting to the server (via angular $resource):
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": [
{
"organization_resource_id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
},
{
"organization_resource_id": 21,
"user_id": 10,
"can_create": true,
"can_read": true
}
],
}
Which returns this JSON:
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": [],
}
I am fairly confident this is an issue in my rails layer, but just for reference here is the angular User.js service I created to perform this RESTful interaction with the server:
angular.module('slics').service('User', [
'$resource', function($resource) {
return $resource('/api/users/:id', {
id: '#id'
}, {
update: {
method: 'PUT',
isArray: false
}
});
}
]);
Really not sure what I am missing here. It does not seem like it should be this difficult to submit nested attributes... but the more research I do the more I realize this does seem to be a pretty common Rails frustration.
Please feel free to comment if any additional context/information would be useful to include in my problem description to help troubleshoot this problem and I would be happy to provide it!
Strong params expects user_permissions_attributes, and you're submitting user_permissions.
Strong params is separate from accepts_nested_attributes_for (in fact, it has nothing to do with it), so however you define your require!/permit calls is exactly how your attributes should be submitted.
ProTip: To save you some future frustration, if you plan on updating through accepts nested attributes, you probably want to permit :id as well.
Well, you post an array of hashes, not a hash.
So this code
user_permissions_attributes: [ :user_id, :resource_id, :can_read, :can_update, :can_create, :can_delete ]
will permit such structure
{
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions_attributes": [
"organization_resource_id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
]
}
Try to whitelist all params at "user_permissions"
user_permissions_attributes: []
Or check out this article, to learn how to build advanced whitelists with StrongParams
http://patshaughnessy.net/2014/6/16/a-rule-of-thumb-for-strong-parameters
user_permissions_attributes: [ :user_id, :id, :can_read, :can_update, :can_create, :can_delete ]) permit :id and submitting hashes with index value..
JSON format submitting to the serve
"user": {
"id": 10,
"first_name": "Joe",
"last_name": "Shmoe",
"user_permissions": {
"0": {
"id": 20,
"user_id": 10,
"can_update": true,
"can_read": true
},
"1": {
"id": 21,
"user_id": 10,
"can_create": true,
"can_read": true
}
}
}

Google Analytics Referral - Ruby

I am using ruby, and attempting to get referrals from google analytics api. Here is what I have set up:
sa_referral = client.execute(:api_method => analytics.data.ga.get, :parameters => {
'ids' => "ga:" + saprofileID,
'dimensions' => "ga:fullreferrer",
'metrics' => "ga:users",
'sort' => "-ga:users",
'filters' => "ga:source!=(direct);",
'start-date' => startDate,
'end-date' => endDate,
})
sa_referral_data = sa_referral do |row|
row = {
:referral => row['0'],
:members => row['1'],
}
end
send_event('sa_top_referrals', current: sa_referral_data)
This returns no data when called in the widget using sa_top_referrals. Below is the data the API is returning.
"columnHeaders": [
{
"name": "ga:fullreferrer",
"columnType": "DIMENSION",
"dataType": "STRING"
},
{
"name": "ga:users",
"columnType": "METRIC",
"dataType": "INTEGER"
}
],
"totalsForAllResults": {
"ga:users": "35638"
},
"rows": [
[
"m.facebook.com/",
"1009"
],
[
"baidu",
"912"
],
[
"usasexguide.info/forum/showthread.php",
"613"
],
Ideally the information I am looking to pull down is the URL ex: m.facebook.com/ and the user count or "613". Those are the two items I am looking to pull. My question is how do I know what row those are equal to. Above i'm sending it using: :referral => row['0'], I'd assume the issue is that its not actually row 0, is there a way I can confirm this?
This should do it:
sa_referral_data = sa_referral['rows'] do |row|
rows.map{|r| { referrals:r[0], members:r[1] }}
end

Keep unveiled credentials in Elasticsearch (using jdbc-river)

I use the jdbc-river to fill my Elasticsearch instance from a PostgreSQL database. The river's record is created with the following ruby's code (since I query ES from a Rails app):
require 'elasticsearch'
client = Elasticsearch::Client.new
client.create :index => "_river", :type => "ldi", :id => "_meta", :body =>
{
:type => :jdbc,
:jdbc => {
:driver => "org.postgresql.Driver",
:url => "jdbc:postgresql://localhost:5432/" + ENV['DB_NAME'],
:user => ENV['DB_USER'],
:password => ENV['DB_PASS'],
:index => ENV['DB_NAME'],
:type => "ldi",
:sql => "select id as _id, * from ldis"
}
}
I'm using envirnoment variables for the database credentials to avoid showing the actual ones. The problem is that once the record is added to ES, actual credentials are unveiled. Thus, you can query ES and obtain something like this:
"hits": {
"total": 6,
"max_score": 1,
"hits": [
{
"_index": "_river",
"_type": "ldi",
"_id": "_meta",
"_score": 1,
"_source": {
"type": "jdbc",
"jdbc": {
"driver": "org.postgresql.Driver",
"url": "jdbc:postgresql://localhost:5432/any_dbname",
"user": "any_dbuser",
"password": "any_dbpass",
"index": "any_index",
"type": "ldi",
"sql": "select id as _id, * from ldis"
}
}
}
....
Is there any way to keep them in secret?

Customizing output of JSON

In the controller I have a respond_with like this:
respond_with(#layer1 , #layer2)
The JSON output I need is like this:
{
"LayerOne": [
{
"name": "haha",
"number":"44"
}, // more ....
],
"LayerTwo": [
{
"name": "James Bond",
"score": 20
} // , ....
]
}
So to get the first section I write the serializer like this:
class Layer1Serializer < ActiveModel::Serializer
attributes :number, :name
def name
object.person.name
end
end
And I change the controller to be like this, so I can pass a ROOT so it shows in the JSON as "LayerOne"
respond_with(#Layer1, root: 'LayerOne')
but remember at the beginning I had two things to pass to controller, so now I can't figure our how to do this for the second section of JSON that says "Layer2"
You can create the following intermediate class:
class BothLayers
include ActiveModel
def initialize(layer1,layer2)
#layer1 = layer1
#layer2 = layer2
end
attr_accessor :layer1, :layer2
end
and the following serializer:
class BothLayersSerializer < ActiveModel::Serializer
root false
has_many :layer1, key: "LayerOne"
has_many :layer2, key: "LayerTwo"
end
Then in your controller:
both_layers = BothLayers.new(#layer1,#layer2)
respond_with( both_layers, serializer: BothLayersSerializer )
Using the JBuilder DSL is an excellent way to solve your problem.
https://github.com/rails/jbuilder
The JSON response you want is implemented as a view, giving you complete control over how it renders.
create a new hash and pass your array values into it.
respond_with({:LayerOne => #layer1.as_json(:only => [:name, :percentage]), :LayerTwo => #layer2.as_json(:only => [:name, :off_target_by])})
i got this json :
{
"LayerOne": [
{
"name": "layer1",
"percentage": "10.11"
},
{
"name": "layer 1 bis",
"percentage": "1212.0"
}
],
"LayerTwo": [
{
"name": "layer 2",
"off_target_by": 2
},
{
"name": "layer 2 bis",
"off_target_by": 9
}
]
}
hope it helps :)
EDIT 2 :
You can create an array serializer to pass your variables :
class LayerArraySerializer < ActiveModel::ArraySerializer
self.root = false
end
and in your view :
respond_with({:LayerOne => #layer1 , :LayerTwo => #layer2}, :serializer => LayerArraySerializer)
json print :
[
[
"LayerOne",
[
{
"percentage": "10.11",
"name": "layer1"
},
{
"percentage": "1212.0",
"name": "layer 1 bis"
}
]
],
[
"LayerTwo",
[
{
"off_target_by": 2,
"name": "layer 2"
},
{
"off_target_by": 9,
"name": "layer 2 bis"
}
]
]
]
Railcasts has an excellent video+text tutorial on AR Serializer, I'm sure you'll find your answer there
http://railscasts.com/episodes/409-active-model-serializers

Resources