let! in rspec association not accessing previously created let! variable - ruby

I am using RSpec 2 and trying to load a few instance variables at the top of a model spec. They are associated, but the associations aren't coming through.
The models are Column and Table -- this is not a problem the way we have it set up. Table has_many columns.
let!(:company) { Company.first }
let!(:events_table) { Table.create(name: "Events Test") }
let!(:text_column) { Column.create(title: "Title", name: "title", column_type: "text", table_id: events_table.id)}
let!(:start_date_column) { Column.create(title: "Start Date", name: "start_date", column_type: "date", table_id: events_table.id) }
The events_table is definitely getting created and returning an ID if I puts.attributes on it later on. I thought the lets! with the bang woudl allow it to get created in order so the association would exist.
I'm not sure that's happening though.
If I puts out the attributes for either of the columns, the table_id is nil.
How can I ensure it is not nil? What am I doing wrong?
Thanks in advance!!

Turns out that for some reason, maybe because I didn't have the inverse_of associations set up, that the relation just wasn't working.
When I add the inverse_of, and then change it to this, it works:
events_table.columns.create!(title: "Title Test")

Related

CouchDB 2.0 - How to autoincrement keys in a View?

In CouchDB 2.0, I'm trying to create an ordered list as the keys from a View, but it doesn't work.
My code for the View document:
var i = 0;
function (doc) {
if (doc.type === "comment") {
emit(i++, doc.webpages);
}
}
The result is that all keys are equal to 0. How can I make it so that each document gets an autoincremented key?
Thanks!
A sequential ID probably isn't the best choice for most real applications. For example, if you were to build a commenting system I would approach it like this (there's a similar example in the couch docs):
Comments would be docs with a structure like this:
{
"_id": "comment_id",
"parent":"comment_id, or article_id if a top level comment"
"timestamp" : "iso datetime populated by the server",
"user_id": "the person who wrote the comment",
"content": "content of the comment"
}
To display all the top level comments of a given parent (either article or parent comment), you could use a view like this:
def function(doc){
emit([doc.parent, doc.timestamp, doc.user_id], doc._id)
}
To query this efficiently, you'd could use the following query options to grab the first twenty:
{
"startkey": ["parent_id"],
"endkey": ["parent_id", {}],
"limit": 20,
"skip": 0,
"include_docs": true
}
The comments will automatically be sorted by the date they were posted because the view is ordered by [parent, datetime, and then user]. You don't have the pass a value for anything other than parent with your key for benefit from this.
Another thing of note is by not passing the content of the comment to the view and instead using include_docs, your index will remain as slim as possible.
To expand on this:
If you want to show replies to a base comment, you can just change
the start and end keys to that comment's id.
If you want to show the next 20 comments, just change skip to 20.
If you want more comments shown initially, just up the limit value.
In answer to your comment, if you had an array or parents in your document like:
"parents" : ["a100", "a101", "a102"]
Everything else would remain the same, except you would emit a row for each parent.
def function(doc){
doc.parents.map( function (parent){
emit([doc.parent, doc.timestamp, doc.user_id], doc._id)
});
}

Rails5: How can I use the ActiveRecord "OR" query with "includes"?

class Parent < ApplicationRecord
has_many :children
enum status: {
status1: 0,
status2: 1
}
end
class Child < ApplicationRecord
belongs_to :parent
end
# error
# "Relation passed to #or must be structurally compatible. Incompatible values: [:references]"
combination = Parent.status1.or(Parent.status2.includes(:children).where(children: {name: 'ABC'}))
I want to get the data "status1" or "status2 has children named 'ABC'", but error occurs.
The or method takes another relation that has a similar filter pattern, and combines it with the already-existing filters on the object being called.
For example, Parent.status1.or(Parent.status2) would give you a set of records that have either status: 1 or status: 2.
(In case someone is not familiar with it, the example in the question also uses enum, which allows filtering the enum's attribute value using the name of the value. #status1 and #status2 in this case correspond to { status: 0 } and {status: 1} respectively.)
In order to call more relation methods to modify the final result, you must call them on the result of calling #or, like this:
Parent.status1.or(Parent.status2).includes(:children).where(children: {name: 'ABC'})
Based on your comment I see now that you want records that either (have status1) or (have status2 and have a matching children record).
Note that in order to use a relation in a where (like where(children: { name: value }) you must join with the related table (joins(:children).where(children: { name: value }). It seems that ActiveRecord will infer the join if you use only includes, but that's not documented as far as I can tell. This is why or sees the two relations as incompatible: one has children in the references list, while the other does not.
If you write the where clause by hand as a string, it does not change the references list, so or does not see the relation as incompatible. When you write a where clause by hand, you must explicitly use joins:
Parent.status1.joins(:children).or(Parent.status2.joins(:children).where("children.name = 'ABC'"))
You are not calling "includes" on the final or result.
parent = Parent.status1.or(Parent.status2)
parent.includes(:chilren).where(children: {name: "ABC"})

How to get new mongoid indexes for a model

Let's say I have defined my model Person with a couple of indexes:
class Person
include Mongoid::Document
field :email
field :ssn
index({ email: 1 }, { unique: true })
index({ ssn: 1 }, { unique: true })
end
However, only the email index already exists in the database, so when I call
Person.collection.indexes.each {|i| puts i.inspect}
I get the following response:
{"v"=>1, "key"=>{"_id"=>1}, "name"=>"_id_", "ns"=>"x.person"}
{"v"=>1, "unique"=>true, "key"=>{"email"=>1}, "name"=>"email_1", "ns"=>"x.person"}
The question is, how can I get the list of defined indexes in the model, even if they are not already created in mongo ?
In my case, such list should include the definition for the field "ssn"
In other words...How to get those indexes that haven't been created yet ?
Person.index_specifications
shows the indexes defined in the model regardless of its existence in the database.
And
Person.collection.indexes
only shows the index that actually exists in the database.
So there is something else that is worth paying attention to:
rake db:mongoid:create_indexes
will create the indexes defined in the model in the database, and it uses the method 'index_specifications' in deed.
While this removes all the indexes other than the index of the primary key:
rake db:mongoid:remove_indexes
So when you want to only remove the indexes that exists in the database but no longer defined in the database, you should use this:
rake db:mongoid:remove_undefined_indexes
which use the method 'undefined_indexes' in deed.
I hope this can be helpful.
The docs are here:
https://mongoid.github.io/en/mongoid/docs/indexing.html
http://www.rubydoc.info/github/mongoid/mongoid/Mongoid/Tasks/Database#create_indexes-instance_method
Just found it...
We can get the list of all index definitions into the model as follows:
Person.index_specifications
This is an array populated when the application is loaded and is used by the "create_indexes" method as can be seen here:
https://github.com/mongodb/mongoid/blob/master/lib/mongoid/indexable.rb

How to write code to NOT include null value fields into mongodb

class State
include Mongoid::Document
embeds_many :cities
field :name
end
class City
include Mongoid::Document
embedded_in :state
field :name
field :population
field ...
end
I don't want to include the fields with nil value into mongodb,
nsw = State.new name: 'NSW'
if number_of_people
nsw.cities.create name: 'Syndey', population: number_of_people
else
nsw.cities.create name: 'Syndey'
end
so it is necessary to check whether or not that field is empty or null. But the problem is when there are many fields in City, the code looks ugly.
How to improve this and write smart code?
You need to define a custom class method in City model like the following:
def self.create_persistences(fields = {})
attributes = {}
fields.each do |key, value|
attributes[key] = value if value
end
create attributes
end
and in your controller, call this method without conditions hassle:
nsw.cities.create_persistences name: 'Syndey', population: number_of_people
note: you can also override create method on your model instead of defining new method but in my opinion, I don't prefer to override something you may use in other part of the code.
Now we know what you are doing your answer seems clear. But I think your question needs an edit to inform.
So what you have is data from some source that you are using to populate your new model. So at some stage here you are going to have a hash or at least some way of constructing a hash in some form from however your data is organized. Take the following [short form but the same thing]:
info = { name: "Sydney", population: 100 }
City.new( info );
info = { name: "Melbourne", population: 80, info: "fun" }
City.new( info )
info = { name: "Adelaide" }
City.new( info )
So (at least in my testing ), you are going to get each document, with only the specified fields created each time.
So dynamically using the hash (and hopefully you are even just reading in that way ) is going to be a lot smarter than testing each value in code.
If you have to do a lot of value testing to even "build up" a hash then you have problems that no-one here can fix. But building hashes should be easy.

don't understand complex Ruby code

I'm currently reading Rails 3 In Action. There is code that I was wondering if someone could explain to me. I'm having a hard time understanding it:
scope :readable_by, lambda { |user| joins(:permissions).where(permissions: { action: "view", user_id: user.id })}
thanks,
mike
It's called a Rails scope. It essentially creates a class method called .readable_by(user) that does a SQL join on the permissions table and returns records where the action column value is "view" and the user_id column value equals user.id.
It could be used like so (assuming it's defined in the Comments model):
readable_comments = Comments.readable_by(current_user)
A simple scope that does nothing is this:
scope :my_scope_name, lambda {}
A scope that accepts a parameter is this:
scope :my_scope_name, lambda { |my_parameter| }
And then the above scope uses some ActiveRecord finder methods, specifically joins and where.

Resources