Implementing a Retweet function with an existing Tweet model - ruby-on-rails

I am trying to implement a retweet (repost) function for a Twitter clone. I initially thought I might need a separate retweet model, but I want to try and implement it using my existing tweet (micropost) model.
I'm having trouble wrapping my head around the schema and what migrations I would need to run. A Micropost would need a Repost column, but would a user need a repost_id? So I would add a Repost column to Micropost, and it would reference User? Or should I actually be adding the column to the User model?
Also if the repost function would work similar to create, would it not?
def repost
#micropost = Micropost.find(params[:id])
#repost = #micropost.repost by current_user
if #repost.save
flash[:success] = "Repost created!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
Is there any way to edit my current Micropost model, or do I need to utilize a specific Repost model?

The way I go about is asking if I need a one to many or a many to many relationship between reposts and users. A user can have (make) many reposts and a repost can be reposted by many users, so that would be a many to many relationship. Meaning you need a joint table between users and micropost - and the optimal name for it in this case is repost.
If you want to think about it the other way round - the way you proposed it: if you would reference the user id in the micropost table in a user_id column, there wouldonly be "enough space" to save one user id... so that would be a one to many association, which doesn't make that much sense for reposts.
For your question about the method, you will need to create a new repost instance once a user clicks on a button (submits a form) to repost. You should have a look at nested resources: https://guides.rubyonrails.org/routing.html#nested-resources

Related

rails revert a activerecord update?

Let's say I have a use case where a user can submit an order. This order contains some information of how many items in my inventory. However, a user can also cancel this order after the order is created. In that case, the inventory should be added back.
I'm wondering how shall I implement this in rails? I'm not sure the correct google key words to search for it
Any pointers would be appreciated!
I think it would be better to implement the logic explicitly. So have an action that deletes the Order and also sets the inventory back to what it should be.
I thinks its ok to go straight way to code
inventory has_many items
order has_many items
items belong_to order
items belong_to inventory
when order created deducts items_amount in inventory
when order cancel(remove, destroy) add items_amount in inventory
Elaborating on #user2280271's answer I'd build a service object to take care of the logic, and then just call it from the controller, here's a great post on service objects: http://brewhouse.io/blog/2014/04/30/gourmet-service-objects.html
So maybe you'll end up with something like this:
class OrdersController
def cancel
#order = Order.find(params[:id])
if CancelOrder.call(#order)
redirect_to order_path(#order), notice: 'Your order has been canceled'
else
redirect_to order_path(#order), alert: "There's been an error while canceling your order"
end
end
end

Setting custom permissions within a "dashboard" area in rails

I'm putting together a side project for a teacher/student type of website where the student will share a dashboard with the teacher. The teacher and student can both upload files and leave comments in the dashboard.
The part that I'm stuck on is the permission. For student, I've set up the index controller to this method
def index
#Homework = Homework.where(:user_id = current_user)
end
With this, I'm able to have the student only see the work that they have, but I'm confused on how to get the teacher to see each individual student's work?
Suggestions? Thanks
Here's a simple solution if you only ever need to support a single class in your application:
def index
if current_user.teacher?
#homeworks = Homework.all
else
#homeworks = Homework.where(user_id: current_user)
end
end
Otherwise, your Homework schema does not seem to be correctly designed. See your query below:
Homework.where(:user_id = <student_id>)
This works to retrieve a student's homeworks, but it does not work to retrieve a teacher's students' homeworks. You may need a class_id for a teacher to see each individual student's work:
Homework.where(:class_id = <class_id>, :user_id = <student_id>)
A Class has_many Teachers and has_many Students. This design will allow you to support multiple classes.
Some more guiding questions:
Is teacher/student both kept in the same User model?
How do you differentiate between teacher/student in your current User model?
Is there a "user_type" column somewhere in User?
What happens if the current_user is of the "teacher" user_type?
For complex user permissions, use CanCanCan: https://github.com/CanCanCommunity/cancancan
Don't use uppercase instance variables. ex: #Homework should be #homework
Check out the gem CanCan. Install it (follow the instructions, you should have to put something in application controller), Then, put in your ability file:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Homework, user_id: user.id
end
end
Then at the top of your StudentController put
load_and_authorize_resource
And the index action should look like:
#homework = #student.homework
Now, you didn't post your whole controller so this is a much as I can help.
I believe you may have a bigger underlying issue. You have students and teachers has_many homework i read in your comment. Then in your example you use user_id. You are likely overriding your students and teacher ownership of homework. You would need a has_and_belongs_to_many relationship OR you would need a student_id and teacher_id columns on the homework table
Cancan automatically generate a number of instance variables which can make it feel like magic. Watch the free railscasts on cancan the same guy who made the video wrote the CanCan library.

Trouble creating relationships using omniauth in rails 3

Alright so this is another try at asking my question. I did rather poorly the first time. I have a relationship set up in my models so that users have many submissions and submissions belong to a user. I have a reference column in my submissions table that references :user and an index:
add_index :submissions, :user_id
I am using omniauth so that people can sign in with twitter. I simply want to be able to have a signed-in user be able to submit a submission and then have rails understand that there is a relationship between the current_user and the submissions they just wrote. Problem is I can't seem to store current_user in an instance variable like so:
#user = current_user
which I would like to use in my submissions controller (create) like so:
#user = current_user
#submission = #user.submissions.create(params[:submission])
I need this so that I could have a user create a submission on views/submissions/index.html.rb page and rails would understand the relation. Thank you so much for looking at this. Also I apologize in advance if I just missed something really obvious or that is common knowledge.
Your post does not really contain a question or did I miss something? What seems strange to me is that you want to assign #user = current_user. There is no need to do so, current_user should be a user anyway, therefore you could just write #submission = current_user.submissions.create(params[:submission]).
Maybe you can edit your post and provide more details on your helper methods (current_user), the error message you get with the code above. What do you get if you add Rails.logger.info current_user (or current_user.name if you have this field)?
If you want you can follow the link in my profile here on Stack Overflow, I have a couple of Rails tutorials there, including one with Omniauth.

Is there a more elegant way to do this in Rails (has_one association)

Hey, I'm very new to Ruby and Rails. I was wondering if I was doing this the right way.
#user = User.new(params[:user])
#student.user = #user
#student.save
if #user.save
...rest of the code
In my app, a Student has one User, and each User belongs to a Student (think of a User as an Account). The association itself isn't my question though; is the block of code above the best way to save a new User that's associated to a Student?
I'm surprised nobody mentioned the right way.
#user = #student.build_user(params[:user])
if #user.save
# ... rest of the code ...
end
This way #user will be built already associated with #student.
If the correspondence between user and student is 1:1 may be this relationship is redundant.
(maybe I'm missing something).
A cooler approach would be perform all the operations you need to the user object and then the last one would be:
#student.user = #user
#student.save
If the Student has_one User, then you should put the foreign key on the User. As in, the User will have the student_id column in the database. A user_id column on the Student isn't necessary.
In which case you can just do this:
#user = User.new(params[:user])
#user.student = #student
if #user.save
...rest of the code
And you won't even need to modify #student.
I think you have #-overload here. Only use # if you want to make the variable an instance variable of the class, rather than a local variable that only exists inside your function. Anyway, here's one possible way to do it:
#student.user = User.create(params[:user])
#student.save
if #student.user.new_record?
# didn't get saved...
end
ActiveRecord::Base#create creates a new object, tries to save it to the database, and then returns the object, and is a useful shortcut for the new-save pattern. It always returns the object, though, so you need to ask whether or not it was successfully saved, hence new_record?.
The question that comes up for me is whether the relationship between Student and User is a has-a or an is-a relationship. It has to do with object modeling of the domain problem.
I suspect it may be is-a, in which case you don't want has_one and belongs_to--known as "composition"--but instead Single-Table Inheritance (STI)--known as "inheritance".
The questions to ask are:
1. Is a Student also a User, i.e. does a Student have the same attributes and methods as a User, plus more methods or restrictions? Since in this case the Student IS also a user, the question of whether a Student can have 0, 1 or more Users does not apply.
2. Does the Student "have" a User in the sense that it could also have 0 Users, or perhaps more than 1?

Why doesn't this associated create call work?

I've got a User model that has many Items. A Rating belongs to a User and an Item.
In the DB, I have set ratings.user_id to be not NULL.
when I am creating an Item, I would like to do this:
def create
current_user.items.create(params[:item]).ratings.create(params[:rating]
redirect_to items_path
end
However, this balks with an SQL error "user_id cannot be nil"
so I rewrote the create method as
def create
current_user.items.create(params[:item]).ratings.create(params[:rating].merge({:user_id => current_user}))
redirect_to items_path
end
which works fine.
However, I had thought that chaining the create methods off the current user's receiver would have populated the rating's user_id. Anyone know why not?
TIA.
I'd recommend you normalize this if possible in the database. Maybe take out the user_id attribute from the ratings table and if you need it in your model get it through a join using a :through method
class Rating
has_many :items
has_one :user, :through=>:items
If you created and saved the Item, then made a Rating from that item, it wouldn't pass the user along to the Rating, right? You'd just refer to it as #rating.item.user, right?
When you think about it like that, you wouldn't expect the Item created via the current_user to pass the user information along to the rating.
Make me wonder if you really need the user has_many ratings relationship.
Because Item has many Ratings and that association does not know about the user id. Given that association chain Item would have a user id because it belongs to a user. And Rating would have an item id because it belongs to an item. But the Item to Rating assocation doesn't know anything about a user unless you tell it.

Resources