Building a database with Rails + Postgresql + Heroku - ruby

I'm building a web app where the user's need to choose from a database of exercises. They should be able to see the exercise name, description, and skill level. What is the best practice for building a database table for this purpose?
I'm thinking I could write each exercise and its attributes in a CSV file, then write a ruby script that would parse it and create an exercise object in the database table for each exercise that it parses. However, I have never done this before and would appreciate feedback.
And how would I migrate this database table from development to production on Heroku?
Thanks so much for any info.

Sounds like an Exercise model with name, description, skill level, and something to store the actual exercise. It is up to you to figure out whether you want to read it in with csv or yaml - I agree that yaml would be easier. Also how to store the user's responses - hstore, json, serialized yaml...

I ended up creating an array of hashes and iterating over the array in seeds.rb:
[
{
:name => "",
:description => "",
:skill_level => ""
},
# Input more hashes below
].each do |exercise|
ExerciseDb.create(
name: exercise[:name]
description: exercise[:description]
skill_level: exercise[:skill_level]
)
end

Related

Ruby mongoDB and large documents

I have a populated mongoDB.
Now I need to add huge amounts of additional data to my documents (log file data). This data exceeds the BSON size limit.
Document too large: This BSON document is limited to 16777216 bytes. (BSON::InvalidDocument)
A simplified example of my situation would look like this:
cli = MongoClient.new("localhost", MongoClient::DEFAULT_PORT)
db = cli.db("testdb")
coll = db.collection("test")
data = {:name => "Customer1", :data1 => "some value", :log_file => "A" * 17_000_000}
coll.save data
What is the best way to add this huge amount of data?
Could I use GridFS to store those files and link the GridFS-file-handle to the correct document?
Could I then access the GridFS-file during queries?
I would suggest two approaches:
GridFS with instructions here https://github.com/mongodb/mongo-ruby-driver/wiki/GridFS
Advantages: uses already existing service(mongodb) to store files so presumably easiest to implement/ cheapest since you already have the infrastructure.
Disadvantage: Not necesarilly the best use of an in-memory DB, especially if it's used for other storage as well.
S3 - Store links to a hosted data service (such as Amazon S3) which is designed for file storage (redundant, replicated and highly available). In this case you just upload the files and store a pointer to their S3 location in your DB.
Advantage Keeps your DB leaner, probably cheaper since you keep your mongo machines optimised for doing mongo things (i.e. high-memory) and take advantage of the really cheap file storage on S3 as well as the near-infinite scalability.
Disadvantage Harder to implement since you need to design your own code to do this. Though there may be off the shelf solutions somewhere.
Some more useful discussion on this SO post
Maybe you can split up your document and reference them. See this SO post: syntax for linking documents in mongodb
The paragraph about document growth finally solved my question. (Found by following Konrad's link.)
http://docs.mongodb.org/manual/core/data-model-operations/#data-model-document-growth
What I am now basically doing is this:
cli = MongoClient.new("localhost", MongoClient::DEFAULT_PORT)
db = cli.db("testdb")
coll = db.collection("test")
grid = Grid.new db
#store data
id = grid.put "A"*17_000_000
data = {:name => "Customer1", :data1 => "some value", :log_file => id}
coll.save data
#access data
cust = coll.find({:name => "Customer1"})
id = cust.first["log_file"]
data = grid.get id

Rails ActiveRecords with own attributes + associated objects' IDs

I have a rather simple ActiveRecords associations like such (specifically in Rails 4):
An organization has many users
A user belongs to an organization
But in terms of ActiveReocord queries, what's an optimal way to construct a query to return an array of Organizations each with its own array of user ids associated with itself? Basically, I'd like to return the following data structure:
#<ActiveRecord::Relation [#<Organization id: 1, name: "org name",.... user_ids: [1,2,3]>, <Organization id: 2...>]>
... or to distill it even further in JSON:
[{id: 1, name: 'org name', ... user_ids: [1,2,3]}, {...}]
where users is not part of the Organizations table but simply an attribute constructed on the fly by ActiveRecord.
Thanks in advance.
EDIT: After trying a few things out, I came up with something that returned the result in the format I was looking for. But I'm still not sure (nor convinced) if this is the most optimal query:
Organization.joins(:users).select("organizations.*, '[#{User.joins(:organization).pluck(:id).join(',')}]' as user_ids").group('organizations.id')
Alternatively, the JBuilder/Rabl approach #Kien Thanh suggested seem very reasonable and approachable. Is that considered current best practice nowadays for Rails-based API development (the app has the back-end and front-end pieces completely de-coupled)?
The only thing to be aware of with a library solution such as JBuilder or Rabl is to watch the performance when they build the json.
As for your query use includes instead of joins to pull the data back.
orgs = Organization.includes(:users)
You should not have to group your results this way (unless the group was for some aggregate value).
ActiveRecord::Relation gives you some automatic helper methods, one of which is association_ids.
So if you create your own JSON from a hash you can do
orgs.map! {|o| o.attributes.merge(user_ids: o.user_ids).to_json }
EDIT: Forgot to add the reference for has_many http://guides.rubyonrails.org/association_basics.html#has-many-association-reference

How to best create sorted sets with Redis

I'm still a bit lost when it comes to Sorted Sets and how to best construct them. Currently I have a simple set of activity on my site. Normally it will display things like User Followed, User liked, User Post etc. The JSON looks something like...
id: 2808697,
activity_type: "created_follower",
description: "Bob followed this profile",
body: null,
user: "Bob",
user_id: 99384,
user_profile_id: 233007,
user_channel_id: 2165811,
user_cube_url: "bob-anerson",
user_action: "followed this profile",
buddy: "http://s3.amazonaws.com/stuff/ju-logo.jpg",
affected: "Bill Anerson is following Jon Denver.",
created_at: "2014-06-24T20:34:11-05:00",
created_ms: 1403660051902,
profile_id: 232811,
channel_id: 2165604,
cube_url: "jondenver",
type: "profiles",
So if the activity type can be multiple things (IE Created Follow, Liked Event, Posted News, ETC) how would I go about putting this all in a sorted set? I'm already sure I want the score to be the created_ms but the question is, can I do multiple values in a sorted set that all have keys as fields? Should most of this be in a hash? I realize this is a fairly open question but after trying to wrap my head around all the tutorials Im just concerned about setting up the data structure before had so I dont get caught to deep in the weeds.
A sorted set is useful if you want to... keep stuff sorted! ;)
So, I assume you're interested in keeping the activities sorted by their creation time (ms). As for storing the actual data, you have two options:
Use the sorted set itself to store the data, even in native JSON format. Note that with this approach you'll only be able to fetch the entire JSON and you'll have to parse it at the client.
Alternatively, use the sorted to store "pointers" to hashes - i.e. the values will be key names in which you'll store the data. From your description, this appears the preferable approach.

Twitter Ruby Gem - get friends names and ids, nothing more

Is it possible to simply get the people you are following with just an id and full name? I do not need any of the additional data, it's a waste of bandwidth.
Currently the only solution I have is:
twitter_client = Twitter::Client.new
friend_ids = twitter_client.friend_ids['ids']
friends = twitter_client.users(friend_ids).map { |f| {:twitter_id => f.id, :name => f.name} }
is there anyway to just have users returned be an array of ids and full names? better way of doing it than the way depicted above? preferably a way to not filter on the client side.
The users method uses the users/lookup API call. As you can see on the page, the only param available is include_entities. The only other method which helps you find users has the same limitation. So you cannot download only the needed attributes.
The only other thing I'd like to say is that you could directly use the friends variable, I don't see any benefit of running the map on it.

Querying embedded documents on a document with MongoMapper

What is a good pattern for querying embedded documents on a document? For instance, my User document has an embedded Alerts document. If I want to see if a given User has an alert with name I can do it two ways as far as I can tell -- in memory a la
alert = current_user.alerts.select{|a| a.name == params[:name]}.first
or via the actual document interface a la (note that I'm not 100% sure this is semantically valid but you get the point):
User.where('alerts.name' => params[:name], :id => current_user.id).first
There MUST be a better way, something like
current_user.alerts.where(:name => params[:name])
perhaps? Or maybe I'm just not thinking about the problem right?
Nope. And I think this is the motivation:
In MongoMapper, queries on the database always return a root object. Allowing queries to return an embedded doc without its parent would be a break with that and make a lot of things more complicated (what if I call .parent inside that embedded doc?) so MongoMappers errs on the side of simplicity and doesn't pretend that things are something they aren't. Embedded docs are stored in an array inside the root doc in MongoDB, so MongoMapper gives you an array in Ruby.
So your two ways of doing it are the intended ways of doing it.
If you need some syntactic suger, it shouldn't be too hard to code up. You could extend Array or you could code a plugin to expand upon MongoMapper's proxy for embedded docs.
I think Mongoid supports this, see "Finding" in the manual for embedded docs.
You can do either:
User.where('alerts.name' => params[:name], :id => current_user.id).fields(:alerts).first.alerts.select{|u| u.name == params[:name]}
or
User.where('alerts.name' => params[:name], :id => current_user.id).fields(:alerts).alerts.select{|u| u.name == params[:name]}.first

Resources