I am loving using the blueprinter gem for my API, but i'm finding using it with the acts_as_tenant gem kinda cumbersome with associations. I have to pass in the account as options, which gets annoying when you have more than one association or nested associations. Is there an easier way?
class UserBlueprint < Blueprinter::Base
fields :uuid, :name, :plan
association :posts, blueprint: PostBlueprint do |user, options|
ActsAsTenant.with_tenant(options[:account]) do
user.posts
end
end
end
Related
I'm somewhat of a Rails newbie, and am not quite sure what the convention is when tying users to their objects, such as documents. (And I now know how important it is to go with conventions in Rails)
So far, I have come up with 3 potential solutions in my head:
Making every document essentially a child of every user, using has_many and belongs_to
Giving every document an integer (user_id, or something along the lines of that) that ties it to the user. That way, whenever the user wants an index of their documents, I can identify all those that have the right ID.
It seems like there's some plugins that do user management completely in their own way that doesn't fit into any of the above.
Additionally, I plan on using Devise for user authentication and management. From my understanding, that seems to be the most popular/widely used authentication gem -- please correct me if I'm wrong.
I'm somewhat of a Rails newbie
Welcome to the club, although some people bite, most are totally cool.
tying users to their objects
You've already answered your own question...
#app/models/user.rb
class User < ActiveRecord::Base
has_many :documents
end
#app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :user
end
If a user creates a document, surely that would mean that you'd have to associate documents to users via a has_many / belongs_to relationship...
--
To give you some context, you'll have to add the user_id foreign_key to your documents table:
$ rails g migration AddUserForeignKeyToDocuments
#db/migrate/add_user_foreign_key_to_documents______.rb
class AddUserForeignKeyToDocuments < ActiveRecord::Migration
def change
change_table :documents do |t|
t.references :user, index: true
end
end
end
$ rake db:migrate
This would allow something like the following (using the current_user helper from Devise):
#config/routes.rb
resources :documents
#app/controllers/documents_controller
class DocumentsController < ApplicationController
def create
#document = current_user.documents.new document_params
#document.save
end
private
def document_params
params.require(:document).permit(.....)
end
end
If you're unsure about user authentication, you should go with Devise, although there are other authentication gems such as sorcery
Setting up Devise is simple:
#Gemfile
gem 'devise', '~> 3.5', '>= 3.5.3'
$ rails generate devise:install
$ rails generate devise User
$ rake db:migrate
#config/routes
devise_for :users
#app/controllers/documents_controller.rb
class DocumentsController < ApplicationController
before_action :authenticate_user!
end
This will make it so only logged-in users can access /documents :)
This is primarily an opinion-based question however I tend to see most projects using ActiveRecord's relations (e.g has_many, belongs_to).
you can use devise for authentification (or build an own login system there is a railscast and use cancancan for authorization. some defining of abilities you will find in the documentation
just for example
can :update, Article
or if user can just delete own stuff
can :read, Project, active: true, user_id: user.id
i would also use the ActiveRecord relation with has_many e.g. and has_many through
I have a common set of models extracted into a gem with namespaced models as such:
module Gemifive
class Activity < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :activities
end
end
That's in the gem "gemifive". So far, so good.
In an app where I use this gem's models, I can do the following: Gemifive::Activity.where(user_id: user.id). This works fine because that table has a user_id column:
SELECT "gemifive_activities".* FROM "gemifive_activities" WHERE "gemifive_activities"."user_id" = 18`
However, the following does not work work: Gemifive::Activity.where(user: user). This generates the following SQL, which is invalid:
SELECT "gemifive_activities".* FROM "gemifive_activities" WHERE "gemifive_activities"."user" = 18
I can access Gemifive::Activity.first.user just fine, so I know the belongs_to association is working. Why can't I use this ActiveRecord convention?
Gemifive::Activity.where(user: user)
This is invalid ARel, plain and simple. It has nothing to do with your models being namespaced. In the above code, ARel is using the key of the Hash as the column name, and it will be used verbatim. What you can do is
Gemifive::Activity.where(user_id: user)
I have the following models
class Business < ActiveRecord::Base
has_and_belongs_to_many :categories
validates_presence_of :category_ids
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :businesses
end
I am setting the relationship through the business creation form, using the category_ids attribute.
I tried using validates_presence_of, however, this is not validating the existence of a category.
I can manipulate the form through the browser, give a non-existing ID for a category. After submitting the form, I get an error:
Couldn't find Category with id=181723
Edit:
Added the following custom validation method, but I am still getting the same error, as if the validation was not being run.
class Business < ActiveRecord::Base
has_and_belongs_to_many :categories
validate :categories_exist
def categories_exist
category_ids.each do |c|
errors.add(:category_ids, :category_doesnt_exist) unless Category.exists? c
end
end
end
There's probably a variety of ways you could achieve this but I'd recommend looking at Custom Validations and ActiveRecord Callbacks.
You can check out the validates_existence gem. This gem has been very useful to me for validating if foreign keys correspond to legitimate parent records. As described in the readme:
This plugin library adds ActiveRecord models a way to check if a
:belongs_to association actually exists upon saving.
I'm extracting part of my app into a Gem, which involves creating some new ActiveRecord models. At the moment these models look something like this:
Class Wordcount < ActiveRecord::Base
belongs_to :keyword
belongs_to :article
end
Class Keyword < ActiveRecord::Base
has_many :wordcounts
has_many :articles, :through => :wordcounts
end
The gem will hook into any ActiveRecord model with String or Text fields, not just my defined Article model in the existing Rails app this code comes from.
What do I need to do in order to generate the relevant AR associations based on whichever model the gem's functionality is being applied to? I'm hoping to end up with:
has_my_gem_functionality :on => [:field1, :field2, ...]
And for the rest to be safely encapsulated within the gem.
I'm assuming I'll also have to metaprogram the Migrations as well. I think what I'm trying to do is relatively simple, it's just slightly too far out of my comfort zone.
Use the source, luke! :)
Just take a look at the source of some other gems that do similar things. For example:
acts-as-taggable-on (includes a migration)
activerecord-embedding (a very small gem that does some associations)
I have a class that looks something like this:
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies
end
For plain users, I'd like user.companies to refer to the standard association method, but when a user is an admin, I want User.all (i.e., admins have access to all companies). The simplest way I can think of to implement this (and what I've always done in the past) is use a scope on the Company class, such as:
scope :accessible_by, lambda { |user| ... }
The only problem is that this just doesn't feel right. Instead of writing a controller action that includes:
#companies = Company.accessible_by(current_user)
I'd feel more comfortable writing
#companies = current_user.companies
Is there a good way to override the User#companies method to accommodate this kind of behavior? Or, should I be happy with using a scope on Company?
I'm wrestling with a similar problem. The only acceptable solution I can devise is an association extension, which overrides the query for admin users and passes normal users' queries, unmolested.
# this works for me in rails 3.1
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies do
def visible
if proxy_association.owner.admin?
UsersCompany.scoped
else
self
end
end
end
end
User.where(:admin => true).first.companies.visible == UsersCompany.all
I'm fairly new to Rails, but this is an interesting question so I figured I'd toss in my two cents. It seems that you should be able to extend your association in User with a companies method that checks self.is_admin? (or similar) and returns what you need. See http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many#461-User-a-block-to-extend-your-associations
Nice question. I was wondering if something like the following is an option you would consider
class User < ActiveRecord:Base
has_many :users_companies
has_many :companies, :through => :users_companies
def viewable_companies
admin? ? Company.all : self.companies
end
end
I know the naming is horrible but, you know, naming things is serious stuff :)