I want to use the ActiveRecord to manupulate my tables data, but what if my table name is variable. This code, will generate an error. Any suggestion?
name = "Posts"
class name < ActiveRecord::Base
end
I believe you can do something like the following:
name = 'Post' # Dynamically generate the class name
klass = Class.new(ActiveRecord::Base) do
# This is the base class.
# You can pass a block with new
# methods here if you'd like.
end
Object.const_set name, klass
name.constantize.new # Call your class dynamically :)
You can do something like this to set the table_name from a variable:
class Post < ActiveRecord::Base
def self.table_name
# Some method to determine and set the table name.
# Must return a string with a table name. E.g. the
# following would set the table name to `posts_2015-11-15`
"posts_#{Date.today}"
end
# ...
end
Related
I have a parent class that looks like this:
class Record
attr_accessor :id, :url, :votes, :title, :first_name, :last_name, :selfdb
def initialize(args = {})
args.each { |name, value| instance_variable_set("##{name}", value) }
#selfdb = "#{self.class.name.downcase}s"
end
def self.find(id)
DB.results_as_hash = true
hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", #selfdb, id)
hasharray.empty? ? nil : new(hasharray[0].transform_keys(&:to_sym))
end
end
Each child class of Record has a matching database table whose name is "#{name of the class}s", so the class "Post" is connected to a table named "posts".
My goal is to have self.find(id) to work on any children of this class. The solution I tried was to save the class' name into a string variable with an "s" at the end (so that class Post -> "posts", for example), to match the name of the database, as I tried in the instance variable #selfdb, but this does not work.
Calling #selfdb on the children classes confirms that it does correctly create the string for different classes, but running the sqlite with it inserted as the table name just returns nil.
This might be a very roundabout way of doing it, any suggestions are welcome. I am still learning and this is just a bootcamp assignment.
Edit: i realized one mistake I made: since self.find(id) is a class method, it can't use an instance variable. However, when I change the class method to work like this:
def self.find(id)
selfdb = "#{self.name.downcase}s"
DB.results_as_hash = true
hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", selfdb, id)
hasharray.empty? ? nil : new(hasharray[0].transform_keys(&:to_sym))
end
... it still does not properly insert into the sqlite string.
You define #selfdb in the initialize method which means it is only available in on the instance level. But your self.find method is a class method and therefore #selfdb is undefined on the class level.
I would suggest adding a class method that returns the table name like this
def self.table_name
"#{name.downcase}s"
end
which you can then be used in the find class method like this
def self.find(id)
# ...
hasharray = DB.execute("SELECT * FROM ? WHERE id = ?", table_name, id)
# ...
end
and in instance methods (for example to save a record) you need to use self.class.table_name or you could add a delegator to forward a table_name instance method call to the class method:
extend Forwardable
def_delegators :"self.class", :table_name
The purpose is to store preferences of TestApp model into MobileConfiguration model with same name using migration.
Here is a migration:
TestApp.where.not(business_id: nil).each do |test_app|
configuration = MobileConfiguration.find_or_initialize_by(business_id: test_app.business_id)
MobileConfiguration::DISCLAIMERS.each { |disclaimer| test_app.public_send(disclaimer) }
configuration.max_store_distance_radius = test_app.max_store_distance_radius
configuration.save
end
MobileConfiguration::DISCLAIMERS.each { |disclaimer| test_app.public_send(disclaimer) } should store the test_app data into mobile_configuration.
DISCLAIMERS is an array of Model MobileConfiguration.
MobileConfiguration::DISCLAIMERS = [:app_disclaimer, :upgrade_disclaimer, :game_disclaimer]
:app_disclaimer, :upgrade_disclaimer, :game_disclaimer are preferences in MobileConfiguration Model
preferences(:configurations) do
preference_group "Disclaimer" do
string :app_disclaimer
string :game_disclaimer
string :upgrade_disclaimer
end
end
Using public_send with one attribute just calls that method. You need to use another attribute to pass some data into the method. Also you need to call the setter to pass data in. So:
class Foo
attr_accessor :bar
end
foo = Foo.new
foo.public_send 'bar' # Just returns the current value of #bar - so nil.
foo.public_send 'bar=', 'something' # calls the setter and passes in 'something'
foo.public_send 'bar' # Now returns 'something'
[MobileConfiguration::DISCLAIMERS].each do |property|
configuration.public_send("#{property}=",test_app.public_send(property))
end
can be used to copy values from one model to another for preferences.
I trying to do like
#array_object.factets
this #array_object belongs to Class A
class A
factets = ['c','d','e']
end
Class A is not inheriting any Active record
if not necessary calling variable then you can easily do:
class A
def factets
['c','d','e']
end
end
and then:
#array_object = A.new
#array_object.factets
OR for single line of code:
#array_object = A.new.factets
When creating a class method dynamically is there a way to bake-in some arguments known at the moment of creation?
I made a module for myself that is generating :slug attribute from given :title when I create models. https://gist.github.com/firedev/4943289
However I would want to refactor this part:
def create_slug(title_attr, slug_attr, &block) # generated method
self.send(slug_attr) # I have to pass slug_attr from outside
...
end
So I can get rid of all the params here:
self.send :after_initialize do
create_slug(title_attr, slug_attr, &block) # I pass attributes from here
end
And have parameters baked-in in the created method instead:
def create_slug # this method is generated automatically
self.send(:slug) # I want :slug to be baked in
...
end
self.send :after_initialize, :create_slug # no attributes
Take a look at this code: https://github.com/oleander/to_param-rb/blob/master/lib/to_param/base.rb
I think it does what you want.
Usage:
class User < ActiveRecord::Base
to_param :name
end
User.create(name: "Linus").to_param # => "1-linus"
In Ruby, isn't an instance variable like #foo and a class variable, ##bar?
In some code, I see a few
self.user_name = #name
or even
a += 1 if name != user_name # this time, without the "self."
# and it is the first line of a method so
# it doesn't look like it is a local variable
what is the self for? I thought it might be an accessor, but then can't it be just user_name instead of self.user_name? And I don't even see any code to make it an accessor, like attr_accessor, and not in the base class either.
In Ruby:
#foo is an instance variable
##bar is a class variable
Instance and class variables are private by default. It means, you can't set or get the value outside the class or the module itself.
If you want to set a value for foo, you need an attribute accessor.
class Model
def foo
#foo
end
def foo=(value)
#foo = value
end
end
For convenience (and performance reason), this is the same of
class Model
attr_accessor :foo
end
Then you can do
m = Model.new
m.foo = "value"
And within the class you can doo
class Model
# ...
def something
self.foo = "value"
# equivalent to
#foo = value
end
end
what is the self for? I thought it might be an accessor
It's a method at least - probably an accessor. self.name = something will call the method name= and self.name, or if no local variable called name exists, just name will call the method name.
but then can't it be just user_name instead of self.user_name?
When invoking the name= method you need the self because name = something would just create a local variable called name. When invoking the name method, it doesn't matter whether you write name or self.name unless there is also a local variable called name.
And I don't even see any code to make it an accessor, like attr_accessor, and not in the base class either.
If there is no call to attr_accessor and no explicit definition of name and name= anywhere, they might be handled by method_missing or defined by a different method than attr_accessor.
isn't self.user_name the same as #user_name
Only if it's defined to be. If you define user_name and user_name=? usingattr_accessorthey will get and set#user_name`. However if you define them through other means (or manually), they can do whatever you want.
For example ActiveRecord uses method_missing to "define" getter and setter methods that correspond to data base columns. So if your ActiveRecord class belongs to a table with a user_name column, you'll have user_name and user_name= methods without defining them anywhere. The values returned by user_name and set by user_name = will also not correspond to instance variables. I.e. there will be no instance variable named #user_name.
Another way to automatically define user_name and user_name= without using instance variables is Struct:
MyClass = Struct.new(:user_name)
Here MyClass will have the methods user_name and user_name=, but no instance variable #user_name will be set at any point.
Welcome in the "scope" hell!
Of course, if we have the full code, it should be easier to explain what you see.
Let me try:
class Woot
attr_accessor :name
def initialize
self.name = 'Bistromathic Drive'
end
def foo
puts name # hum… I don't know name. An attr? Oh Yes!
name = '42' # New local var named 'name'
puts name # Yeah! I know a local named 'name'
puts self.name # I know the attr named 'name'
name = 'Improbability Drive' # Change the local
puts name
puts self.name # attr didn't move
self.name = 'What was the question?'
puts name
puts self.name
end
end
w = Woot.new
w.foo
# => Bistromathic Drive
42
Bistromathic Drive
Improbability Drive
Bistromathic Drive
Improbability Drive
What was the question?
The partial code you show is "suspect" to me, and I'd prefer to explain the base of var scope.