We are using thinkingtank gem and having trouble indexing model associations, even simple ones. For example, a profile belongs to an institution, which has a name – we would like to do something like:
class Profile < ActiveRecord::Base
#model associations
define_index do
indexes institution(:name), :as => :institution_name
end
end
but that doesn't work. This must be very simple – what am I doing wrong?
a possible solution to this issue would be adding a method returning the element to index. For the profile.institution.name case:
# profile.rb
# ...
belongs_to :institution
# ...
define_index do
indexes institution_name
end
def institution_name
self.institution.name
end
# ...
Also the ", :as => ..." syntax is not supported on thinkingtank.
I would also recommend giving a try to Tanker: https://github.com/kidpollo/tanker
Regards.
Adrian
Related
I have been working on an App called CtrlPanel for the company I work for.
This app was originally running on Ruby v2.2.2 and rails v4.2.1. I could not get that environment to work on ANYTHING; I tried both PC and Linux. Since I couldn't get that environment running and since it needed to be updated to the newest version anyway I figured I would just get it working on the latest version.
I had no idea what I was in for, that was a little over a month ago. I am happy to report I now have everything in the program working with one exception. There is a catalog that displays all of the items and it uses a scope in the model with a lambda expression that is rather complicated (at least to me). I have had to update the syntax ALL over this application due to the older version of Rails and now being on the newest version and this is the only one I can't seem to figure out. I am pretty sure again that it is just a Syntax problem from Rails v4.2.1 to Rails v6.1.3.1 but I just can't seem to figure it out and I am sure people who are more experienced than myself will know what it is.
Here is the model in question:
category.rb
# == Schema Information
#
# Table name: categories
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Category < ActiveRecord::Base
attr_accessible :name
# accepts_nested_attributes_for :product_categories
has_many :products, through: :product_categories
has_many :product_categories, dependent: :destroy
default_scope { order("id") }
scope :with_published_products, -> { joins{product_categories.product}.where{products.status.eq "published"}.uniq }
scope :with_matched_search_terms, ->(search_terms) { joins{product_categories.product}.where{products.name.like search_terms}.uniq }
end
The line with the issue is:
scope :with_published_products, -> { joins{product_categories.product}.where{products.status.eq "published"}.uniq }
It is giving the error:
The method .joins() must contain arguments
I had other joins statements that I had to fix (numerous). It was due to syntax for a very BASIC example from joins{arguments} to joins(arguments). I assume that is the same case here. In this case there are many more elements and that lambda expression thrown into the mix which is making it much harder (for me at least) to get it corrected. I have tried every version of changing that line around I can think of and probably many that do not make sense. I have one version where it will get past the line but then when that "with_published_products" is called later in the view it errors out with a bad PG statement and when I traced it back it was due to the very same query. Here is the view involved:
- if current_user
.pull-left{style: "position:absolute; top: 10px; left: 20px;"}
= link_to products_path do
%button.btn{style: "margin-top: 0px; "}
%i.icon-arrow-left.icon-large
%b back to products
.row-fluid
.span12
%h1.bigred DPF Product Catalog
%hr
.row-fluid
.span12
- unless #categories.empty?
.tabbable.tabbable-left
%ul.nav.nav-tabs{style: "text-align:right;"}
%li.active
%a{"data-toggle" => "tab", :href => "#tab-#{#categories.first.id}"} #{#categories.first.name}
- #categories.each do |category|
-unless category == #categories.first
%li
%a{"data-toggle" => "tab", :href => "#tab-#{category.id}"} #{category.name}
.tab-content
- #categories.each_with_index do |category, i|
.tab-pane.fade{id: "tab-#{category.id}", class: "#{ 'in active' if i.zero? }"}
%h1.bigred.professional{style: "text-align:left;"} #{category.name}
%hr
- #params_name.blank? ? #products = category.products.published.includes(:pictures).order("name asc") : #products = category.products.searched(#params_name).published.includes(:pictures).order("name asc")
= render partial: 'product', collection: #products, cache: true
- else
%h2
There were no products matching
%b #{#params_name.gsub(/%/, '')}
Here is the Controller:
class StaticPagesController < ApplicationController
#skip_before_filter :authenticate_user!, :only => [:catalog, :roi]
skip_before_action :authenticate_user!, :only => [:catalog, :roi]
#skip_authorization_check
before_action
def home
end
def help
end
def catalog
if params[:name].present?
#params_name = "%#{params[:name]}%"
#categories = Category.with_published_products.with_matched_search_terms(#params_name)
else
#categories = Category.with_published_products
end
render layout: "catalog"
end
def roi
render layout: "roi"
end
end
Best I can tell the original author is trying to display product_catagoires that have the status of "published". I might be over simplfying the statement but I beleive that is what I understand it to be doing. I know from other joins I had to fix I had to eliminate the .eq and put => in its place which I tried many versions of and it always complained about the syntax. As an example of what I mean this was another section I fixed:
FROM:
cds_item = item.variants.joins{vendor}.where{ vendor.code.eq 'cds' }.first
TO:
cds_item = item.variants.joins(:vendor).where( :vendor_id => 'cds' ).first
I tried to change the line I am having the problem with to a similar format and every combination I could think of I just can't seem to speak to Rails the way it wants.
If there are any other files I need to attach please let me know.
I appreciate any input anyone has to offer.
I am willing to make the scopes less complicated and more verbose if it solves the problem I just am too new to figure this last piece out.
Thank You,
Scott
Thank You #engineersmnky,
I am now getting the following error:
Cannot have a has_many :through association 'Category#products' which goes through 'Category#product_categories' before the through association is defined.
It is referencing this line in the view:
- #params_name.blank? ? #products = category.products.published.includes(:pictures).order("name asc") : #products = category.products.searched(#params_name).published.includes(:pictures).order("name asc")
Sorry if this is not the right way to add more information, I couldn't post the line dealing with the code in a comment.
My guess is that your legacy app used a gem called Squeel which monkeypatched core methods in ActiveRecord like where, joins etc to take a block argument:
Person.joins(:articles => {:comments => :person})
Becomes:
Person.joins{articles.comments.person}
The authors ambition was that Squeel would be incorporated into ActiveRecord but that didn't pass and the fact that the monkeypatches broke with every Rails release burned out the maintainers. Squeel was abandoned after Rails 4.2. But parts of it live on in the baby_squeel gem.
Some of the easier cases are going to be relatively straight forward to unfurl into modern ActiveRecord code. Others like a LIKE query will require some Arel:
class Category < ActiveRecord::Base
# Do not use attr_accessible
# - model level whitelisting was replaced by strong params in Rails 4
# attr_accessible :name
# accepts_nested_attributes_for :product_categories
# You need to declare the relations you are joining through first
has_many :product_categories, dependent: :destroy
has_many :products, through: :product_categories
# default scope is evil!
# default_scope { order("id") }
# use `lambda do` for multi-line lambdas to keep reasonable line lengths
# or just write regular class methods
scope :with_published_products, lambda do
joins(product_categories: :product)
.where( products: { status: "published" } )
.distinct
end
scope :with_matched_search_terms, lambda do |search_terms|
joins(product_categories: :product)
.where(
Product.arel_attribute(:name)
.lower
.matches("%#{search_terms.downcase}%")
.distinct
)
end
end
I'm not sure what #uniq was supposed to do in the Squeel - but I believe it may have added a distinct clause - like ActiveRecord::Relation#uniq which was deprechiated in Rails 5.0.
Rails 3.1, ActiveAdmin 0.3.4.
My question is somewhat similar to this one but different enough in terms of data modeling that I think it warrants its own response. Models:
class CheckoutRequest < ActiveRecord::Base
has_one :request_common_data, :as => :requestable, :dependent => :destroy
end
class RequestCommonData < ActiveRecord::Base
belongs_to :requestable, :polymorphic => true
end
The RequestCommonData model has a completed field (boolean) that I'd like to be able to filter in ActiveAdmin's CheckoutRequest index page. I've tried a few different approaches to no avail, including the following:
filter :completed, :collection => proc { CheckoutRequest.all.map { |cr| cr.request_common_data.completed }.uniq }
which results in no filter being displayed. Adding :as => :select to the line, as follows:
filter :completed, :as => :select, :collection => proc { CheckoutRequest.all.map { |cr| cr.request_common_data.completed }.uniq }
results in the following MetaSearch error message:
undefined method `completed_eq' for #<MetaSearch::Searches::CheckoutRequest:0x007fa4d8faa558>
That same proc returns [true, false] in the console.
Any suggestions would be quite welcome. Thanks!
From the meta_search gem page you can see that for boolean values the 'Wheres' are:
is_true - Is true. Useful for a checkbox like “only show admin users”.
is_false - The complement of is_true.
so what you need is to change the generate input name from 'completed_eq' to be 'completed_is_true' or 'completed_is_false'.
The only way I have found this possible to do is with Javascript, since by looking at the Active Admin code, the 'Wheres' are hardcoded for each data type.
I would usually have a line like this in my activeadmin.js file (using jQuery)
$('#q_completed_eq').attr('name', 'q[completed_is_true]');
or
$('#q_completed_eq').attr('name', 'q[completed_is_false]');
Terrible and ugly hack but have found no other solution myself.
Be careful to enable this only in the pages you want.
--- NEW FOR VERSION 0.4.2 and newer ---
Now Active Admin uses separate modules for each :as => ... option in the filters.
So for example you can place the code below inside an initializer file
module ActiveAdmin
module Inputs
class FilterCustomBooleanInput < ::Formtastic::Inputs::SelectInput
include FilterBase
def input_name
"#{#method}_is_true"
end
def input_options
super.merge(:include_blank => I18n.t('active_admin.any'))
end
def method
super.to_s.sub(/_id$/,'').to_sym
end
def extra_input_html_options
{}
end
end
end
end
and the use
:as => :custom_boolean
where you specify your filter.
I'm having some issues in RoR with some model methods I am setting. I'm trying to build a method on one model, with an argument that gets supplied a default value (nil). The ideal is that if a value is passed to the method, it will do something other than the default behavior. Here is the setup:
I currently have four models: Market, Deal, Merchant, and BusinessType
Associations look like this:
class Deal
belongs_to :market
belongs_to :merchant
end
class Market
has_many :deals
has_many :merchants
end
class Merchant
has_many :deals
belongs_to :market
belongs_to :business_type
end
class BusinessType
has_many :merchants
has_many :deals, :through => :merchants
end
I am trying to pull some data based on Business Type (I have greatly simplified the return, for the sake of brevity):
class BusinessType
def revenue(market=nil)
if market.nil?
return self.deals.sum('price')
else
return self.deals(:conditions => ['market_id = ?',market]).sum('price')
end
end
end
So, if I do something like:
puts BusinessType.first.revenue
I get the expected result, that is the sum of the price of all deals associated with that business type. However, when I do this:
puts BusinessType.first.revenue(1)
It still returns the sum price of all deals, NOT the sum price of all deals from market 1. I've also tried:
puts BusinessType.first.revenue(market=1)
Also with no luck.
What am I missing?
Thanks!
Try this:
class BusinessType
def revenue(market=nil)
if market.nil?
return self.deals.all.sum(&:price)
else
return self.deals.find(:all, :conditions => ['market_id = ?',market]).sum(&:price)
end
end
end
That should work for you, or at least it did for some basic testing I did first.
As I have gathered, this is because the sum method being called is on enumerable, not the sum method from ActiveRecord as you might have expected.
Note:
I just looked a bit further, and noticed you can still use your old code with a smaller tweak than the one I noted:
class BusinessType
def revenue(market=nil)
if market.nil?
return self.deals.sum('price')
else
return self.deals.sum('price', :conditions => ['market_id = ?', market])
end
end
end
Try this!
class BusinessType
def revenue(market=nil)
if market.nil?
return self.deals.sum(:price)
else
return self.deals.sum(:price,:conditions => ['market_id = ?',market])
end
end
end
You can refer this link for other functions. http://en.wikibooks.org/wiki/Ruby_on_Rails/ActiveRecord/Calculations
I have problem with mongomapper associations. I have one class names User and other named Model. User has many models but...
user = User.first
=> <User ...
user.models
=> []
Model.find_by_user_id(user.id.to_s)
=> <Model ...
Model.find_by_user_id(user.id.to_s).user == user
=> true
Class code (simplified):
class User
include MongoMapper::Document
# some keys definition
many :models
end
class Model
include MongoMapper::Document
# some keys definitions
belongs_to :user
end
What I am doing wrong?
It appears that MM no longer uses String format for the FK column, so
Model.find_by_user_id(user.id.to_s)
should be
Model.find_by_user_id(user.id)
Furthermore, the datatype of the Model.user_id column should be set to
key :user_id, Mongo::ObjectID
When I ran into this problem, I had to delete and recreate my collection to get it to work- in other words I used to have user_id as a String, but it would only "take" when I switched it when I rebuilt my database. Luckily I am working with test data so that was easy enough.
What kind of errors or exceptions are you getting? The code you posted looks fine.
ah, this is poorly documented in the mm docs. You need to do this here:
class User
include MongoMapper::Document
# some keys definition
many :models, :in => :model_ids
end
class Model
include MongoMapper::Document
# some keys definitions
# no belongs_to necessary here
end
You can then add models to your user via:
# use an existing object
u = User.create ...
m = Model.create ...
# and add the model to the user
u.models << m
# don't forget to save
u.save
# you can then check if it worked like so:
# u.model_ids => [ BSON::ID 'your user id']
Hope that helped.
I'm working on a legacy database that is complete non-sense. I have a table called movie that contains columns with names like c00, c01, c02 and so on. The table also uses non-standard primary_keys. So I've created a class called movie like this:
class Movie < ActiveRecord::Base
set_table_name "movie"
set_primary_key "idMovie"
belongs_to :media_file, :foreign_key => "idFile"
def title
self.c00
end
def plot
self.c01
end
end
I'd like to be able to do something like Movie.find_by_title("Die Hard") and have it return the right result. Also I'd like to be able to say Movie.create(:title => "Die Hard"). How do I do this?
I think you want alias_attribute. Check out Brian Hogan's excellent presentation from RailsConf this year.
You really just need a combination of Sarah's answer and Ben's answer:
class Movie < ActiveRecord::Base
# gives you Movie.find_by_title
# and lets you chain with other named scopes
named_scope :find_by_title, lambda { |title| { :conditions => { :c00 => title } } }
# gives you
# movie.title
# movie.title=(title)
# and
# Movie.new(:title => title)
alias_attribute :title, :c00
end
The find_by_* methods use reflection, so it just isn't going to happen with Rails out-of-the-box. You can, of course, define your own methods:
def self.find_by_title(title)
first(:conditions => { :c00 => title })
end
The next step would be to iterate over a hash of column_aliases => real_columns to use as fodder for calls to alias_attribute and define_method.