Add a model that uses money-rails - ruby-on-rails

I want to create a model named "Donations", which has an attribute named "amount" which will be utilizing money-rails gem. Please guide me how to initiate the process.
Will it be generated similarly, just need to add "monetize" in that model? Would be helpful if some pro could elaborate its working.

You can create a model in a similar way we create other models, inside the model use the monetize macro to set the attribute as a Money object.
make sure the column you want to use as a Money object must be postfix by _cents. This will add the amount attribute to each Donation object which is Money object
class Donation
monetize :amount_cents
end
If you want to use any other column like column name without _cents as postfix, you have to provide as argument with a string value, make sure column name and string value passed to as argument must be different.
class Donation
monetize :amount, as: 'contribution'
end
Read this ActiveRecord section of readme to understand it.

Related

Implementation for Rails model that might not need a database

I'm trying to construct a system in Rails where I've got a Project model with a "type" column, and I'm not sure whether if I should explicitly store the type as a string in the projects table, or if I should store a type_id instead. The thing is, I feel like it would be stupid to create a type model; Types cannot be created or destroyed, there are simply a fixed number of them. But if I don't create a model, the only other way I can think to do it would be to create a Type class in /lib which has a get_name(type_id) method, and this seems like total overkill.
If I decided to just store the string, I'd be using unnecessary space, and filtering by type would be weird.
Any thoughts?
If you're sure the types are a fixed set you can define some numeric constant in the Project model and just store these number in a column of your projects table.
Here an example (not tested of course) where I call the column category_id to avoid to use the type name that would cause problems as rjz said:
class Project < ActiveRecord::Base
# Project categories are constants
CHEAP_PROJECT = 1
SOUND_PROJECT = 2
GRAPHIC_PROJECT = 3
SECRET_PROJECT = 4
# Force project_category_id to be a valid category identifier
validates :category_id, :inclusion => {:in => 1..4}
# At this point you can use the constants
# Here an example of a scope to get the secret projects
scope :secret_projects, where(:category_id => SECRET_PROJECT)
end
Be sure to validate the category_id values to be one of which you defined.
Once you have these constans you can even use from other places using something like Project::SOUND_PROJECT.
I think is a solution pretty clear, but if your requirements change (they always change...) you have to create a model and insert these project categories maintaining these identifiers.

Rails gem/plugin for dynamic custom fields in model

Is there any gem/plugin for ruby on rails which gives the ability to define custom fields in a model at runtime with no need to change the model itself for every different field.
I'm looking for something like Redmine acts_as_customizable plugin which is packaged as a gem usable in the rails way, i.e.
gem 'gemname'
rails g something
rails db:migrate
class Model < ActiveRecord::Base
acts_as_something
end
Here are the CustomField and the CustomValue classes used in Redmine.
Edit:
Since my question is not clear I add a brief use case which explains my need better:
I want users to be able to design their own forms, and collect data
submitted on those forms. An important decision is the design of how
these custom dynamic records are stored and accessed.
Taken from here, in this article approach the problem with different ideas, but they all have drawbacks. For this reason I'm asking if the issue has been approached in some gem with no need to rethink the whole problem.
I'm not aware of a gem that does this, but serialize works quite well and it's a built-in. You get a NoSQL-ish document store backed by JSON/YAML.
If you allow user to create a custom form, you can pass nested arrays et cetera directly into the attribute. However, if you need to validate the structure, you're on your own.
I'm afraid it could be tricky and complicated to do it in ActiveRecoand (generally in standard relational database). Take a look at http://mongoid.org/docs/documents/dynamic.html - this mechanism is using nosql feature.
You can also may try the following trick:
1/ Serialize a hash with your custom fields in the database column, for example { :foo => 'bar', :fiz => 'biz' }
2/ After load a record from database do some metaprogramming and define corresponding methods on the record's singleton class, for instance (assume that custom fields are stored and serialized in custom_fields column):
after_initialize :define_custom_methods
# ..or other the most convinient callback
def define_custom_methods
# this trick will open record's singleton class
singleton_class = (class << self; self; end)
# iterate through custom values and define dynamic methods
custom_fields.each_with_key do |key, value|
singleton_class.send(:define_method, key) do
value
end
end
end
Since rails 3.2 you can use store method. Just include following in your model:
store :properties, accessors: [:property1, :property2, :property3...]
You only need to change your model once (to add properties field to db table). You can add more properties later without altering the schema.
The way this works is by serializing properties hash into YAML and saving it into database. It it suitable for most cases, but not if you'd like to use these values in db queries later.
I don't know a gem, but this can be accomplished be creating a table called custom_fields with a name column and possibly a datatype column if you wanted to restrict fields by datatype.
Then you create a join table for a custom field to your desired table and a value and do whatever validations you want.

Rails Best Practice for User-Configurable Global Attribute "Defaults"

Sorry about the awkward phrasing of the title -- not quite sure of the best way to title this but here's what I'm seeing assistance with:
In a Rails app, let's say we've got a model for a Product, and one of the attributes of the product is Price.
On the admin side of my app, I'd like to be able to set a "default" price that could be referred to if any new Product created isn't assigned a Price. If a Product does have a value for Price, then it would be used.
This is, of course, an example -- and I ask this question because I've got to imagine this is a common pattern. This can be applied to any resource that might have user-configurable global defaults or resource-specific values.
In pure Ruby, this would be solved, I think, with a class variable, so I'd be able to define ##default_price within the Product class and be able to refer to Product.default_price if the instantiated object's value doesn't exist.
My research here has pointed me towards the rails-settings-cached gem, which would allow for something like MyApp.default_price, but I'm wondering if there's a more elegant (non-plugin) way to accomplish this within the base Rails framework.
Note I'd like to setup this structure in code, but I want to be able to define the actual values through my app (i.e. config files aren't the solution I'm looking for).
Can someone enlighten me with the Rails way of handling this?
ActiveRecord picks up default attribute values from the database schema. However, these are baked into the migration and table schema and not configurable.
If you want configurability, the pattern that I've used is a before_validation callback method to set a value if the attribute is blank, e.g.:
class Product < ActiveRecord::Base
before_validation :set_price_if_blank
validates :price, :presence => true # sanity check in case the default is missing
has_one :price
private
def set_price_if_blank
self.price = Price.default if self.price.blank?
end
end
class Price < ActiveRecord::Base
def self.default
##default ||= Price.where(:default => true).first
end
end
This assumes that your price table is populated with a row that has a default flag. You could achieve this, e.g. through a seeds.rb file. I've added a validation rule to make sure that you still get an error if no default exists. It adds robustness to your application.
Also note that it's best to use Integers or Decimals for price data, not floats. See this answer.

Where do indexes go when using STI?

I am using Rails and postgres.
I have a couple of models using STI and i was wondering where i should put indexes on the tables and why?
For example lets say i have the following setup:
class Comment < AR; end
class MovieComment < Comment; end
class MagazineComment < Comment; end
# Fake Comment Table
id:integer
title:string
body:text
type:string
Thanks!
On the type field, if you want only one of MovieComment or MagazineComment. If you don't do that, you won't need the index here. I'm not sure if AR does use type every time, but just to make sure.
Because the id field is a primary key, an index should already be there.
If you want to query by both type and id make sure you have a combined index.
On the other fields: Depends what you query on, but I suppose you want to retrieve these only.
In general, you need indices in the columns that you will use when performing queries.
If you do MovieComment.find(10), the database will use the index in the id field that Rails will add automatically for you. Same if you do Comment.find(30): Rails will retrieve the commend with id 30 using the index, then it will read the type column and it will return a MovieComment or a MagazineComment.
If you are going to add a feature to search by title, for instance, you will have to create an index in this column as well. In this case, probably a :fulltext index.
An Index in the type column would make a query like MagazineComment.all faster because it is equivalent to Comment.where(type: 'MagazineComment').all, but it is probably not worth it.

Rails Datetime split setter method

I have a datetime column in a model and I want to display the form for this as separate text_fields for the time instead of the datetime_select helper drop down menus.
I know I will somehow have to validate and recreate the date to go back to the DB, but I am not sure how.
Should I use virtual attributes getter and setter methods? EG...
def virtual_minute=(minute)
...
end
def virtual_hour=(hour)
...
end
Or maybe in a callback?
A bit stucko!
You don't need to write these yourself if you're using an ActiveRecord model. Multi-parameter attributes can be submitted via forms in pieces. As an example look at how the datetime_select method you're not too fond of works:
http://apidock.com/rails/ActionView/Helpers/DateHelper/datetime_select
Internally the various parameters get submitted with order and type information so that they can be properly encoded. You can build off of this functionality by simply naming your fields the same way. It is not too hard to copy a helper method you like and put the adjusted version in your ApplicationHelper module.
The values here show up as parameters named something along the lines of:
model_type[attribute(1i)]
model_type[attribute(2i)]
model_type[attribute(3i)]
These are combined in the correct order, converted as required, and submitted to a constructor for that attributes particular type, such as DateTime.
This avoids having to create your own wrapper methods.
In any case, the easy way to create attributes is to use the attr_accessor method, such as:
class MyModel < ActiveRecord::Base
attr_accessor :some_date
end
That will create the appropriate accessor and mutator methods.
You can use a before_validation trigger to recombine the values in a single pass, or write a wrapper around the date retrieval method to reconsruct it as required.
Virtual attributes are good IMO since they store the user data in an instance variable.
Then in a before_save you construct a Time object assign it to the real variable.
I was trying to split the time from the date (i think this is your question) when I took a look at the source code and found the :ignore_date option:
time_select will also generate 3 input hidden tags, for the actual
year, month and day unless the option :ignore_date is set to
true.
If you set the :ignore_date to true, you must have a date_select on the same method within the form otherwise an
exception will be raised.
In other words, by setting it to true it's possible to split the date and the time.
See time_select and date_select
(it's also possible to use the datetime select with :discard_day, :discard_hour ...)

Resources