convert string to model in Rails rake task - ruby-on-rails

I'm trying to run a quick rake task on all my Rails models but haven't been able to call them because this piece of code tells me that I can't call the method columns on a string.
I tried classify instead of camelize and it hasn't worked either, tried inserting a class_eval in there as well, but that dosen't seem to work here / don't know too much about it.
task :collect_models_and_field_names => :environment do
models = Dir.glob("#{models_path}/*").map do |m|
m.capitalize.camelize.columns.each { |n| puts n.name }
end
I do know that this worked so I would have manual access to the model if I needed, but I don't really want to do that...
Model.columns.each { |c| puts c.name }

Try
Kernel.const_get(m.classify).columns

classify just changes the string to look like a class -- i.e. with a capital letter and in camelcase, singular.
after using classify to make the string look like a class/model, you need to use constantize, which actually takes the string and converts it into a class.
See:
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize

You can use something like this:
models = Dir[Rails.root.join("app", "models", "*.rb")].map do |m|
model = File.basename(m, ".rb").classify.constantize
model.columns.each { |n| puts n.name }
end

Related

rubocop string interpolation and size condition

Before I except these two methods I wanted to see if anyone in the community had a better idea to structure these and make the cops pass. The first one with to_s seems a bit crazy too. I was thinking of refactoring the other method but that would be a single line or two.
Thoughts?
Code Examples One:
def destroy(resource_name, id)
delete "#{resource_name.to_s.pluralize}/#{id}"
end
Code Examples Two:
def all_products
products_map = fetch(:products).map { |x| [x['id'], x] }.to_h
variants = fetch :variants
variants.group_by { |x| x['product']['resource']['id'] }.to_a.map do |product_id, product_variants|
product.merge 'variants' => product_variants if product == products_map[product_id]
end.compact
end
For Code example One, maybe this can be used:
delete [resource_name.to_s.pluralize, id].join('/')
For Code example Two, yes you definitely need to refactor it.
Maybe you need to create a separate method that does all the grouping and merging, etc. for the variants part.
I am not sure if this is a good practice, but you can create a private method for it.

Alias find_by queries of often used objects

I have a Mood model which contains several moods which are seeded to the database, then they are never changed.
So, I was wondering, instead of always doing Mood.find_by(name: 'happy'), can't I somehow alias this to something like happy, and have it available everywhere where I would have the model available? Is there something in terms of Rails/ActiveRecord that allows this?
You can use Ruby's define_singleton_method in moods.rb. I didn't load ActiveRecord, so just replace the puts "Call Mood..." with the actual call:
class Mood
##moods = ['angry', 'calm', 'happy']
##moods.each { |mood| define_singleton_method(mood.to_sym) { puts "Call Mood.find_by(name: #{mood}) here" } }
end
Here's the output:
2.2.1 :003 > load 'moods.rb'
Call Mood.find_by(name: calm) here
Call Mood.find_by(name: angry) here
=> true
Defining a [] method is done sometimes for this.
class Mood < ActiveRecord::Base
def self.[](mood_name)
find_by_name(mood_name)
end
end
Then you can refer to it as Mood[:happy].

how to delete specific characters in ruby?

There is already created record, like
Company "Life"
How to make this record to the species
сompany-life
I used parameterize, but it turns:
company-quot-life-quot
As I understand, .gsub(""", "") is not suitable for implementation, since to create too large list of exceptions
Is there may be a way to make record in raw format? (to parameterize later)
thanks in advance!
Here is a non-Rails approach:
require 'cgi'
str = 'Company "Life"'
puts CGI.unescape_html(str).gsub(/"/, '').gsub(/\s+/, '-').downcase
# => company-life
And a pure regex solution:
puts str.gsub(/&\w+;/, '').gsub(/\s+/, '-').downcase
# => company-life
And if you are inside Rails(thanks to #nzifnab):
str.gsub(/&\w+;/, '').parameterize
As #meager said, you shouldn't be storing the html-encoded entities in the database to begin with, how did it get in there with "? Theoretically this would work:
class Page < ActiveRecord::Base
before_validation :unescape_entities
private
def unescape_entities
self.name = CGI.unescape_html(name)
end
end
But I'm still curious how name would be getting there in the first place with html entities in it. What's your action/form look like?
"Company "Life"".html_safe.parameterize
"Company "Life"".gsub(/&[^;]+;/, "-").parameterize.downcase
# => "company-life"
Firstly, gsub gets rid of html entities, then parameterize gets rid from all but Ascii alphanumeric (and replaces them with dash), then downcase. Note that "_" will be preserved too, if you don't like them, another gsub('_', '-') is needed.

How do I make one line tests in Rspec without Shoulda?

I have a bunch of very repetitive rspec tests that all have the same format:
it "inserts the correct ATTRIBUTE_NAME" do
#o.ATTRIBUTE_NAME.should eql(VALUE)
end
It would be nice if I could just make one line tests like:
compare_value(ATTRIBUTE_NAME, VALUE)
But shoulda doesn't seem to be geared toward these types of tests. Are there other alternatives?
Sometimes I regret exposing subject as an end-user device. It was introduced to support extensions (like shoulda matchers), so you can write examples like:
it { should do_something }
Examples like this, however, do not read well:
it { subject.attribute.should do_something }
If you're going to use subject explicitly, and then reference it explicitly in the example, I recommend using specify instead of it:
specify { subject.attribute.should do_something }
The underlying semantics are the same, but this ^^ can be read aloud.
I would write a custom RSpec helper if you want it to read more clearly and be only 1 line. Suppose we have the following class we want to test:
class MyObject
attr_accessor :first, :last, :phone
def initialize first = nil, last = nil, phone = nil
self.first = first
self.last = last
self.phone = phone
end
end
We could write the following matcher:
RSpec::Matchers.define :have_value do |attribute, expected|
match do |obj|
obj.send(attribute) == expected
end
description do
"have value #{expected} for attribute #{attribute}"
end
end
Then to write the tests we could do something like:
describe MyObject do
h = {:first => 'wes', :last => 'bailey', :phone => '111.111.1111'}
subject { MyObject.new h[:first], h[:last], h[:phone] }
h.each do |k,v|
it { should have_value k, v}
end
end
If you put all of this in a file call matcher.rb and run it the following is output:
> rspec -cfn matcher.rb
MyObject
should have value wes for attribute first
should have value bailey for attribute last
should have value 111.111.1111 for attribute phone
Finished in 0.00143 seconds
3 examples, 0 failures
I found this which works great:
specify { #o.attribute.should eql(val) }
subject { #o }
it { attribute.should == value }

How do you iterate over active record objects in Ruby On Rails?

This question is quite simple but I have run into the problem a few times.
Let's say you do something like:
cars = Vehicle.find_by_num_wheels(4)
cars.each do |c|
puts "#{c.inspect}"
end
This works fine if cars is an array but fails if there is only one car in the database. Obviously I could do something like "if !cars.length.nil?" or check some other way if the cars object is an array before calling .each, but that is a bit annoying to do every time.
Is there something similar to .each that handles this check for you? Or is there an easy way to force the query result into an array regardless of the size?
You might be looking for
cars = Vehicle.find_all_by_num_wheels(4)
The dynamic find_by_ methods only return one element and you have to use find_all_by_ to return multiple.
If you always want all of the cars, you should use find_all instead:
cars = Vehicle.find_all_by_num_wheels(4)
You could also turn a single Vehicle into an array with:
cars = [cars] unless cars.respond_to?(:each)
Named scoped version for your problem
Vehicle.scoped(:conditions => { :num_wheels => 4 } ).each { |car| car.inspect }
You can do this to get arrays everytimes :
cars = Vehicle.find(:all, :conditions => {num_wheels => 4})
I don't think that you have a loop that will check if the object is an array.
Another solution could be:
for i in (1..cars.lenght)
puts cars[i].inspect
end
(haven't tested, it might break to test the lenght on a string. Let me know if it does)

Resources