What does ActiveRecord code scope code result in? - ruby-on-rails

I am trying to implement Push notifications in my Ruby project. I found this code, but cannot tell what it does. In my project I am not using ActiveRecord as I am using MongoDB. The two lines I cannot understand are as follows
scope :android, -> { where(device_type: 'android') }
I am not familair with ActiveRecord so is the "-> { where(device_type: 'android') }" a SQL search? What is the result of this code? Wondering how I would implement since I am using MongoDB?
registration_ids= Device.android.map(&:registration_id)
The "Device.android.map" where and how is the .android coming from? I am guessing there is a subclass of Device name android?
CODE:
rails g model Device registration_id:string device_type:string
class User < ActiveRecord::Base
scope :android, -> { where(device_type: 'android') }
...
def self.notify_android(data, collapse_key = nil)
require 'gcm'
gcm = GCM.new(ENV['API_KEY']) # an api key from prerequisites
registration_ids= Device.android.map(&:registration_id) # an array of one or more client registration IDs
options = {
data: data,
collapse_key: collapse_key || 'my_app'
}
response = gcm.send(registration_ids, options)
end
...
end
UPDATE:
Ok, this Android is a commonly used query that can be used as method calls. So now I know what scope does, but not sure how to implement with MongoDB and MongoID.
I think this is correct for rails 4 and MongoID?
scope :android, where(:device_type => 'android')
So if the above is correct then it leaves one question of what does this mean?
&:registration_id

Please read about active record active record to understand what it does. Also have a look at ORM and scope, android is not any subclass it basically
add a class method for retrieving and querying objects.
so you basically have class method called android on device class
When you call Device.android it turns into query SELECT "devices".* FROM "devices" WHERE "devices"."device_type" = 'android' and returns all the records matching where clause.
Before understanding Device.android.map(&:registration_id) please have a look at map.
Map returns a new array with the results of running block once for every element in enum.
So basically you are calling registration_id on every object which matches filter device_type = 'android'. and returns an array of registration ids or in other words we are looping over all objects and collecting registration_id of each object and returning all the collected registration ids.

Read about scope https://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/scope
A scope represents a narrowing of a database query
In your case
registration_ids= Device.android.map(&:registration_id)
It will map registration_id of Device whose device_type is 'android'
In &:registration_id & symbol is used to denote that the following argument should be treated as the block given to the method. these is not a proc method so it called it's to_proc method
Device.android.map(&:registration_id.to_proc)
which is same as
Device.android.map { |x| x.registration_id }

Related

How do I retrieve a record by ID from an Active Record record collection?

If I have a collection of records returned from an Active Record query, how can I access one of those records if I know its ID?
There are two ways to do this. Assuming you have an ActiveRecord relation called records and are searching for id:
You can use the ActiveRecord::FinderMethods#find method, but this will result in another call to the database:
records.find(id)
You can search through the collection without using the database by calling Ruby's Enumerable#find method as follows:
records.find { |r| r.id == id }
Although they share the same name, we are invoking two different find methods in the above example. The first will use more database resources, while the latter will use more application server resources. I am not sure which one is faster in the end.
TableName.find(id)
should do it.
Or if you already have a bunch of records in an Active Record query you can pull out the one with the known id like:
active_record_query = User.where(first_name: "Jim")
record = active_record_query.where(id: record_id).first
Supposing I have a result set of users and I want to find the user with ID = 3 in that set:
foo = User.all
user3 = foo.find {|x| x.id == 3}
Of course if you know the ID, you could just find it. Supposing I have a users table and I know the ID of the user I want is 3:
user3 = User.find(3)
You can find records with from the database with ActiveRecord::FinderMethods#find.
Thing.find(1) will query the database for SELECT things.* FROM things WHERE things.id = 1. And raise an ActiveRecord::NotFoundError if the record cannot be found. Find can also be used with a list of ids.
If you have a relation that is already loaded from the database you would use Enumerable#find or its alias #detect. ActiveRecord::Relation includes Enumerable so it works even if you don't explicitly cast to an array with .to_a.
Passes each entry in enum to block. Returns the first for which block
is not false. If no object matches, calls ifnone and returns its
result when it is specified, or returns nil otherwise.
Example:
[1,2,3,4].find(&:even?) # => 2
User.all.load.find { |u| u.id == 1 }
Yes. You can use the find method.
# Assuming you were retrieving an ActiveRecordRelation s
s = MyRecord.where(name: "test")
s.find(id)
For further information see: http://api.rubyonrails.org/v5.0/classes/ActiveRecord/FinderMethods.html#method-i-find

filter active record results in ruby

Im completely new to Ruby on Rails. Im working on an existing API and I have a sql query which is executed with:
results = ActiveRecord::Base.connection.select_all(query)
The output of the results is:
{"availability"=>"In Stock", "available_to_purchase"=>1}
{"availability"=>"Void", "available_to_purchase"=>0}
{"availability"=>"Out of Stock", "available_to_purchase"=>0}
{"availability"=>"In Stores Only", "available_to_purchase"=>0}
I know want to get all values for availability based on available_to_purchase being 0. I have tried:
res = results.where(available_to_purchase: 0)
But I get an error saying:
undefined method `where'
I also tried:
res = results.select { |result| result.available_to_purchase == 0 }
But i end up with the error:
undefined method `available_to_purchase'
I know I can loop through and check available_to_purchase is 0, then add to a new array. But is there any method to quickly filter active record results based on value of a column?
Going directly through the connection avoids nearly everything ActiveRecord is intended to provide you. First thing, read Active Record Basics. The Rails guides are really well put together and should contain everything you need for your first few weeks using the framework.
The reason you aren't getting the where method you expected is that it's a method on ActiveRecord::Relation, and select_all just returns you a list of hashes. By the time you call results.where, it's too late. You need to instantiate a Product model (or whatever would match your table name), which is as simple as:
class Product < ActiveRecord::Base
end
Rails will look at the products table (again, I'm making up your table name) for the attributes a Product has, and then you'll be able to query based on them with where:
results = Product.where(available_to_purchase: 0)
The model will also have the accessor methods you were trying for, so you can do things like results[0].available_to_purchase == 0 or results[0].availability = 'Already in your house'.
In order to call .where on the results, you need an instance of an active record relation. Regular ActiveRecord queries will return this. select_all, however, returns an array of hashes. Array does not have the method where.
res = results.select { |result| result.available_to_purchase == 0 }
Your second error is because you are attempting to access ruby hash keys incorrectly. Properties must be accessed asresult['available_to_purchase']
Here is the answer to your question. I do agree with others though that you should be using ActiveRecord.
results.select { |result| result['available_to_purchase'] == 0 }

Where should method go (model?, somewhere else)

I've got a method in one of my models which returns data which will be fed into a charting gem.
class MyModel < ActiveRecord::Base
def ownership_data
format_data(item_ownerships.group(:owned).count)
end
end
I need to guarantee that the data return always has 2 values in the result. Something like this:
{ "yes" => 4, "no" => 2 }
In order to do this, I've written another method which is used in the first method:
def format_data(values)
values[false].nil? ? values = values.merge({ "no" => 0 }) : true
values[true].nil? ? values = values.merge({ "yes" => 0 }) : true
return values
end
My question is, where should this method go and how can I unit test it using rspec? I've currently got it in the model, however in trying to test it with rspec, my specs look like this:
let(:values) { { "yes" =>2 } }
it "it will return 2 values" do
result = MyModel.new.format_data(values)
expect(result.keys.count).to eq(2)
end
I'm not too happy about having to instantiate an instance of the model to test this. Any guidance is appreciated.
As AJ mentioned in the comment, we probably need more information to go on to give more specific advice. I'll give some anyway...
If you have a object that isn't necessarily depending on the model itself, you should consider moving it to a plain old ruby object or a service object. Those can live in a separate folder (lib/services) or in your models folder without inheriting from ActiveRecord::Base.
Your method could also be a class method def self.method_name(values, values_2) instead of a instance method (if you don't need specific values from the model).
When it comes to data manipulation for reporting for my projects, I've built specific folder of ruby service objects for those under 'lib/reports' and they take raw data (usually in init method) and return formatted data from a method call (allowing me to have multiple calls if the same data can be formatted in different output options). This makes them decoupled from the model. Also, this makes testing easy (pass in known values in Class.new expect specific values in method outputs.

Ember.js find single item without ID

I’m building an Ember.js application, using Ember data, ActiveModel serializer, and Ember Simple Auth Devise, connecting to a Rails API and trying to understand how I could build a route that loads a single resource, in this case for a "my account" page for the current user.
From the Rails perspective I don't need an ID, but on the Ember side I’m not sure how to accomplish this. My workaround has been to supply a placeholder ID, which Rails ignores. Is there a better way to accomplish this?
Ember.js:
MyAccountRoute = Ember.Route.extend(model: -> #store.find 'account', '1')
Rails:
def show
#item = #current_user.account
end
Ember Data has a very specific implementation when you use find
find called with the type only expects a collection of that type, this maps to findAll
find called with the type and a primitive type (non object) will expect a single object response of that type, this maps to findById
find called with the type and an object will expect a collection (possibly filtered server side by the parameters sent in), this maps to findByQuery
So using Ember Data there is no way to do this, unless you want to hack it into one of your other implementations, or use ajax to call back and then sideload the store. I prefer using the pattern you're using, I do this.store.find('user', 'me'); And then ignore the parameter.
The way I am tackling this is by returning an array/collection of records that only contains a single record.
Then in Ember you can access this single result using .get('firstObject') like this
export default Ember.Route.extend({
model: function() {
return this.store.find('user').then(function (users) {
return users.get('firstObject');
});
}
});
This feels more like an Ember way of doing things and also avoids an issue you may notice if you use the Ember developer tools plugin; That the returned data actually creates a duplicate record - you end up with an empty record with an id of me or 1 and a complete record with the ID of the single record returned.
An alternative approach is continue using me or 1 and to set or modify the ID of the returned record to match. In this case you would return a single object and not an array/collection.
Ember data has queryRecord method.
This method makes a request for one record, where the id is not known beforehand
http://emberjs.com/api/data/classes/DS.Store.html#method_queryRecord
I combined the two answers and used queryRecord with a parameter ignored by server.
return this.store.queryRecord('user_settings', {id: 'me'});
thanks Binarytales and antulik

Active record where query for value inside of an array

Question: Is it possible to build a class method scope that can query objects based on values inside an array in a table? If yes, how can I do this?
In my example, I have a “wells” table that has an array field called “well_tags”. I want to build a query that returns all objects that have a specified value (such as “ceramic”) in the wells_tags array. The basic query would be something like this:
#well = Well.all
#query = #well.where(“well_tags contains ceramic”)
And then the class method scope would look something like this, with the “well_tag_search” param passed in from the controller:
class Well < ActiveRecord::Base
def self.well_tag_filter(well_tag_search)
if well_tag_search.present?
where(“well_tags contains ceramic")
else
Well.all
end
end
I found another post that asks a similar question (see link below), but I cannot get the answer to work for me...the result is always 'nil' when I know there should be at least 1 object. I am a beginner using sqlite (for now) as my database and rails 4.0.
Active Record Query where value in array field
Thanks!
UPDATE: some progress
I figured out how to create an array of all the objects I want using the ‘select’ method. But I still need to return the results as an Active Record object so I create a class method scope.
#well = Well.select
{ |well| if well.well_tags.present?
then well.well_tags.include? ‘ceramic' end }
#well.class #=> array
Not sure where Show is coming from.
Can you try doing Well.all instead of Show.all?

Resources