I'm getting started with parsing data and getting some structure from user supplied strings (mostly pulling out digits and city names).
I've run a bit of code in the ruby interpreter, and now I want to use that same code in a web application.
I'm struggling as to where in the code my parsing should be, or how it is structured.
My initial instinct was that it belongs in the model, because it is data logic. For example, does the entry have an integer, does it have two integers, does it have a city name, etc. etc.
However, my model would need to inherit both ActiveRecord, and Parslet (for the parsing), and Ruby apparently doesn't allow multiple inheritance.
My current model is looking like this
#concert model
require 'parslet'
class concert < Parlset::Parser
attr_accessible :date, :time, :city_id, :band_id, :original_string
rule(:integer) {match('[0-9]').repeat(1)}
root(:integer)
end
Really not much there, but I think I'm stuck because I've got the structure wrong and don't know how to connect these two pieces.
I'm trying to store the original string, as well as components of the parsed data.
I think what you want is:
#concert model
require 'parslet'
class concert < ActiveRecord::Base
before_save :parse_fields
attr_accessible :date, :time, :city_id, :band_id, :original_string
rule(:integer) {match('[0-9]').repeat(1)}
root(:integer)
private
def parse_fields
date = Parlset::Parser.method_on_original_string_to_extract_date
time = Parlset::Parser.method_on_original_string_to_extract_time
city_id = Parlset::Parser.method_on_original_string_to_extract_city_id
band_id = Parlset::Parser.method_on_original_string_to_extract_band_id
end
end
It looks to me as though you need several parsers (one for city names, one for digits). I would suggest that you create an informal interface for such parsers, such as
class Parser
def parse(str) # returning result
end
end
Then you would create several Ruby classes that each do a parse task in ./lib.
Then in the model, you'd require all these ruby classes, and put them to the task, lets say in a before_save hook or such.
As the author of parslet, I might add that parsing digits or city names is probably not the sweet spot for parslet. Might want to consider regular expressions there.
Related
I am a bit new to the MVC framework and need to know what is the best practice when working with one.
I have a model class called NewsFeed that contains field like news_title, news_publish_date, news_url.
class NewsFeed < ActiveRecord::Base
attr_accessible :description, :feed_id, :link, :publish_date, :title
def save_news_info(feed_id, news_title, news_link, news_publish_date, news_description)
self.feed_id = feed_id
self.title = news_title
self.link = news_link
self.publish_date = news_publish_date
self.description = news_description
end
end
The task is to read an rss feed and gather all the news it contains, so i made a class called FeedReader and in that class I am using a gem feedzirra to parse the feed link
class FeedReader
attr_accessor :title, :url, :publish_date, :news_array
def initialize(feed_url)
#url=feed_url
end
def read
feed = Feedzirra::Feed.fetch_and_parse(#url)
#title = feed.title
#url = feed.feed_url
#publish_date = feed.last_modified
end
end
My question is if this is a good practice to have a seperate class just like the model(NewsFeed) class and have a read function there, or should I declare the read function in my model class and delete FeedReader? (cauze I have been reading that putting too much functionality in model classes is frowned upon!!) and in future all the functionality (like sanitize news description, strip particular tags etc) gets coded in the model class which in turn grows bigger and bigger.
There's several opinion around that. Here's mine : you're doing the right thing. There's a thing called "principle of simple responsability", which is kind of a buzz word these days, but still has value : your objects should do one "thing", and do it well. So, having one class for handling the news feeds, and one that handles the retrieval of news, it makes complete sense to me.
Bonus point : it is (supposedly) easier to test.
The method should stay in FeedReader, but you might benefit from passing the NewsFeed instance (or a collection, which can handle a list of NewsFeed instances) to the method which extracts information from the feed (I am not sure if name read is so good in this context).
You mist keep in mind that there are both Atom and RSS feeds. And you should not weld your code to one of the formats. Instead you should be able to work with both (if required) by utilizing polymorphism.
As for SRP: actually the best explanation for it , that i have heard is following - class should have only one reason to change.
I've got one model with about 50 columns of measurement data, each with a different unit of measurement (ie. grams, ounces, etc.). What is a good way to associate units of measurement with columns in my database? The primary use for this is simply for display purposes. (Ruby on Rails)
EDIT: To clarify, my model is an object, and the attributes are different measurements of that object. So, an example would be if I had the model Car and the attribute columns :power, :torque, :weight, :wheelbase, etc. I would want car.power.unit to return hp and car.weight.unit to return lbs., etc. This way, I would be able to do something like this:
<%= car.power + car.power.unit %>
and it would return
400hp
Updated Answer
Since you're storing many columns of data, but each column is only one type, and your concern is strictly presentational, I would just use a decorator to accomplish what you need. See this railscast for an example of a great way to do this using Draper.
Basically, a decorator wraps your model with presentation specific methods, so instead of:
#CarsController.rb
def show
#car = Car.find(params[:id])
end
You would use
#CarsController.rb
def show
#car = CarDecorator.find(params[:id])
end
You would define a decorator like so:
class CarDecorator < ApplicationDecorator
decorates :car
def horsepower
model.power.to_s + "hp" #call to_s just in case
end
end
Then in your view any time you called #car.horsepower you would get 123hp instead of 123. In this way you can build a big long reusable list of presentation methods. You can share methods between objects using inheritance, and you can allow methods from the original model to be called as well. See the railscast and the docs etc. You can use Draper or you could roll your own presenter class if you don't want to use a library.
Previous Answer (Abridged):
I can see two nice, easy ways to do this:
1) Just add a text column for units to your data model. IE: to get "400hp" use [data.value,data.units].join
2) You could get a little richer association by having a Units model, perhaps with help from something like ActiveEnum.
You could add a unit model with a for attribute, where you save the attribute in the messurement, you want to apply the unit to. Example:
def Unit < ActiveRecord::Base
scope :for, lambda{|messurement| find_by_for( messurement.to_s ) }
end
This allows you stuff like:
<%= #car.torque + Unit.for(:torque).symbol %>
I do not know if this is of so much advantage, but its a way to solve your problem...
I'm trying to figure out the best way to design a rails model. For purposes of the example, let's say I'm building a database of characters, which may have several different fixed attributes. For instance:
Character
- Morality (may be "Good" or "Evil")
- Genre (may be "Action", "Suspense", or "Western")
- Hair Color (may be "Blond", "Brown", or "Black")
... and so on.
So, for the Character model there are several attributes where I want to basically have a fixed list of possible selections.
I want users to be able to create a character, and in the form I want them to pick one from each of the available options. I also want to be able to let users search using each of these attributes... ( ie, "Show me Characters which are 'Good', from the 'Suspense' genre, and have 'Brown' hair).
I can think of a couple ways to do this...
1: Create a string for each attribute and validate limited input.
In this case I would define an string column "Morality" on the character table, then have a class constant with the options specified in it, and then validate against that class constant.
Finding good characters would be like Character.where(:morality=>'Good').
This is nice and simple, the downside is if I wanted to add some more detail to the attribute, for instance to have a description of "Good" and "Evil", and a page where users could view all the characters for a given morality.
2: Create a model for each attribute
In this case Character belongs_to Morality, there would be a Morality model and a moralities table with two records in it: Morality id:1, name:Good etc.
Finding good characters would be like Morality.find_by_name('Good').characters... or
Character.where(:morality=> Morality.find(1).
This works fine, but it means you have several tables that exist only to hold a small number of predefined attributes.
3: Create a STI model for attributes
In this case I could do the same as #2, except create a general "CharacterAttributes" table and then subclass it for "MoralityAttribute" and "GenreAttribute" etc. This makes only one table for the many attributes, otherwise it seems about the same as idea #2.
So, those are the three ways I can think of to solve this problem.
My question is, how would you implement this, and why?
Would you use one of the approaches above, and if so which one? Would you do something different? I'd especially be interested to hear performance considerations for the approach you would take. I know this is a broad question, thank you for any input.
EDIT:
I'm adding a Bounty of 250 (more than 10% of my reputation!!) on this question because I could really use some more extended discussion of pros / cons / options. I'll give upvotes to anyone who weighs in with something constructive, and if someone can give me a really solid example of which approach they take and WHY it'll be worth +250.
I'm really agonizing over the design of this aspect of my app and it's now time to implement it. Thanks in advance for any helpful discussion!!
FINAL NOTE:
Thank you all for your thoughtful and interesting answers, all of them are good and were very helpful to me. In the end (coming in right before the bounty expired!) I really appreciated Blackbird07's answer. While everyone offered good suggestions, for me personally his was the most useful. I wasn't really aware of the idea of an enum before, and since looking into it I find it solves many of the issues I've been having in my app. I would encourage everyone who discovers this question to read all the answers, there are many good approaches offered.
I assume that you are going to have more than a few of these multiple-choice attributes, and would like to keep things tidy.
I would recommend the store it in the database approach only if you want to modify the choices at runtime, otherwise it would quickly become a performance hit; If a model has three such attributes, it would take four database calls instead of one to retreive it.
Hardcoding the choices into validations is a fast way, but it becomes tedious to maintain. You have to make sure that every similar validator and drop-down list etc. use matching values. And it becomes quite hard and cumbersome if the list becomes long. It's only practical if you have 2-5 choices that really won't change much, like male, female, unspecified
What I'd recommend is that you use a configuration YAML file. This way you can have a single tidy document for all your choices
# config/choices.yml
morality:
- Good
- Evil
genre:
- Action
- Suspense
- Western
hair_color:
- Blond
- Brown
- Black
Then you can load this file into a constant as a Hash
# config/initializers/load_choices.rb
Choices = YAML.load_file("#{Rails.root}/config/choices.yml")
Use it in your models;
# app/models/character.rb
class Character < ActiveRecord::Base
validates_inclusion_of :morality, in: Choices['morality']
validates_inclusion_of :genre, in: Choices['genre']
# etc…
end
Use them in views;
<%= select #character, :genre, Choices['genre'] %>
etc…
Put simply, you're asking how to enumerate ActiveRecord attributes. There are a lot of discussions around the web and even on SO for using enums in rails applications, e.g. here, here or here to name a few.
I never used one of the many gems there are for enums, but active_enum gem sounds particularly suited for your use case. It doesn't have the downsides of an activerecord-backed attribute set and makes maintenance of attribute values a piece of cake. It even comes with form helpers for formtastic or simple form (which I assume could help you for attribute selection in your character search).
If a change in any of these attributes would be strongly tied to a change in the code (ie: When a new Hair Color is introduced, a new page is created or a new action is implemented), then I'd say add them as a string hash (option 1). You could store them in the Character model as a finalized hashes with other meta-data.
class Character < ActiveRecord::Base
MORALITY = {:good => ['Good' => 'Person is being good'], :evil => ['Evil' => 'Person is being Evil']}
...
end
Character.where(:morality => Character::MORALITY[:good][0])
Edit to add the code from comment:
Given Character::MORALITY = {:good => {:name => 'Good', :icon => 'good.png'}, ...
- Character::MORALITY.each do |k,v|
= check_box_tag('morality', k.to_s)
= image_tag(v[:icon], :title => v[:name])
= Character::MORALITY[#a_character.morality.to_sym][:name]
My suggestion is to use a NoSQL database such as MongoDB.
MongoDB support embedded documents. An embedded document is saved in the same entry as the parent. So it is very fast for retrieval, it is like accessing a common field. But embed documents can be very rich.
class Character
include Mongoid::Document
embeds_one :morality
embeds_many :genres
embeds_one :hair_colour
index 'morality._type'
index 'genres._type'
end
class Morality
include Mongoid::Document
field :name, default: 'undefined'
field :description, default: ''
embedded_in :character
end
class Evil < Morality
include Mongoid::Document
field :name, default: 'Evil'
field :description,
default: 'Evil characters try to harm people when they can'
field :another_field
end
class Good < Morality
include Mongoid::Document
field :name, default: 'Good'
field :description,
default: 'Good characters try to help people when they can'
field :a_different_another_field
end
Operations:
character = Character.create(
morality: Evil.new,
genres: [Action.new, Suspense.new],
hair_colour: Yellow.new )
# very very fast operations because it is accessing an embed document
character.morality.name
character.morality.description
# Very fast operation because you can build an index on the _type field.
Character.where('morality._type' => 'Evil').execute.each { |doc| p doc.morality }
# Matches all characters that have a genre of type Western.
Character.where('genres._type' => 'Western')
# Matches all characters that have a genre of type Western or Suspense.
Character.any_in('genres._type' => ['Western','Suspense'])
This approach has the advantage that adding a new type of Morality is just adding a new Model that inherits from Morality. You don't need to change anything else.
Adding new Morality types do not have any performance penalty. The index take care of maintaing fast query operations.
Accessing the embed fields is very fast. It is like accessing a common field.
The advantage of this approach over just a YML file is that you can have very rich embed documents. Each of these documents can perfectly grow to your needs. Need a description field? add it.
But I would combine the two options. The YML file could be very useful for having a reference that you can use in Select boxes for example. While having embeds document gives you the desired flexibility.
I'll follow 2 principles: DRY, developers happiness over code complicate.
First of all, the predefined Character data will be in the model as a constant.
The second is about validation, we will do a bit metaprogramming here, as well as searching with scopes.
#models/character.rb
class Character < ActiveRecord::Base
DEFAULT_VALUES = {:morality => ['Good', 'Evil'], :genre => ['Action', 'Suspense', 'Western'], :hair_color => ['Blond', 'Brown', 'Black']}
include CharacterScopes
end
#models/character_scopes.rb
module CharacterScopes
def self.included(base)
base.class_eval do
DEFAULT_VALUES.each do |k,v|
validates_inclusion_of k.to_sym, :in => v
define_method k do
where(k.to_sym).in(v)
end
# OR
scope k.to_sym, lambda {:where(k.to_sym).in(v)}
end
end
end
end
#app/views/characters/form.html
<% Character::DEFAULT_VALUES.each do |k,v] %>
<%= select_tag :k, options_from_collection_for_select(v) %>
<% end %>
For the multiple values case, one option is to use bit fields as implemented in the FlagShihTzu gem. This stores a number of flags in a single integer field.
Let's say you have several models that contain fields for address, postal code, province/country, phone number, etc.
These are rather common fields that have specific regular expression validations. If you put the same validations and regular expressions in each model, it is duplicated. Also, the tests are duplicated. This is a smell ;)
What is the best approach using ruby and rails to refactor these types of things? A module?
In Java with Hibernate, we'd use a Component class to store the address, and then we'd put the validation logic there. Each model that wanted to use an address would simply contain one, and it will get all the address validation logic.
What is the approach to achieve the same thing in rails? Thanks!
Build custom validators for the various types of validations you need, then invoke them in your model classes.
For example:
class PostalCodeValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
unless value =~ /^\d{5}$/
record.errors[attr_name] << "must be a 5-digit postal code"
end
end
Now use that validation in each model class and for each attribute that is a postal code. For instance, if you Customer has an attribute postal_code:
class Customer < ActiveRecord::Base
validates :postal_code, :postal_code => true
end
There's more detail and lots of fancy options, so I suggest a Google search on rails custom validators.
I am always reading about keeping Controllers thin and doing all logic in models. While this makes senses to me for interacting with databases, what about situations where there is no need to for database interactions?
I have a fairly complex module in my app that interact with several different third party APIs. I use ajax calls to my controller, where all the data is gathered from the APIs and then organized. Then it is displayed via the corresponding .js.erb or .html.erb files.
Is this the proper way to handle this kind of situation? I'm new to rails and don't want to get into habit of doing things wrong.
Models are not just for dealing with database, but for working with data in principle.
As far as we don't know what situations you mean I can just present some situations.
Ajax call for big Math calculating. It is not touching database and even it can be calculating in tableless model.
# in your controller
def calculating
Calculator.get_integral_log_and_furie params[:data]
end
# in your model
class Calculator
def self.get_integral_log_and_furie(data)
... # multi line code
end
end
So you can see that you can calculate it right in your controller, but it should be calculated in your model, so it is reusable and clean solution.
Another example is using some virtual attributes. Names. You can store first, second and third name in saparate columns, so you need to join it. You can create privae method in controler, but of course it is bad idea.
class User < AR::Base
def full_name
[first_name, second_name, third_name].compact.join(" ")
end
end
So you can call it everywhere in your project:
#user.full_name
# Peter Jhonson, or mu is too short
And so on and so on
Do model logic in models.
Maintain associations.
Maintain complex attributes.
Maintain validations.
Represent concepts from the business/industry.
Do controller logic in controllers.
Check that a user is authorized to modify a resource.
Pull and aggregate data to pass into a view template.
Find the right view template.
Compose json for the API response.
Retry failed saves.
Models do not need to be ActiveRecords. You can do a whole lot with models - the "core" of your appliation - that has nothing to do with persistence. Just don't put controller logic into these models.
That's a good question.
Even if you don't need to use a database, you can still take an OOP / MVC approach to organise your code and wrap your data, logic and behaviour in models.
Code organisation and encapsulation within model objects is still useful & important!
In Rails 3, you can make non-persisting models by including just some of the ActiveModel modules that ActiveRecord contains. For example:
# This class exists as a fairly simple DTO that is not expected to be persisted, but
# it does have validation, attributes & a hash constructor, just like ActiveRecord models
class MyProduct
include ActiveModel::Conversion
include ActiveModel::Naming
include ActiveModel::Validations
attr_accessor :title, :quantity
validates :title, :quantity, :presence => true
validates :quantity, :numericality => {:greater_than_or_equal_to => 1}
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end