For example, I have this array:
tags_array=['<code>','<span>','<div>', '<label>','<a>', '<br>', '<p>' '<b>','<i>', '<del>', '<strike>', '<u>', '<img>', '<video>', '<audio>', '<iframe>', '<object>', '<embed>', '<param>', '<blockquote>', '<mark>', '<cite>', '<small>', '<ul>', '<ol>', '<li>', '<hr>', '<dl>', '<dt>', '<dd>', '<sup>', '<sub>', '<big>', '<pre>', '<code>', '<figure>', '<figcaption>', '<strong>', '<em>', '<table>', '<tr>', '<td>', '<th>', '<tbody>', '<thead>', '<tfoot>', '<h1>', '<h2>', '<h3>', '<h4>', '<h5>','<h6>']
I don't want to define it in each model method where I use it, it seems not very smart copy-pasting.
And of course I don't want separate DB row for that.
How can I define it once, so it would be visible for each model method, like instance initialize method.
Like
class MyModel<ActiveRecord::Base
#...
#tags_array=['','',...]
def onemethod
#tags_array.split!
#...
end
def twomethod
#tags_array.capitalize!
#...
end
end
You could define it as a module and mix it in.
module TagsArray
tags_array=['','',...]
def self.onemethod
tags_array.split
#...
end
def self.twomethod
tags_array.capitalize
#...
end
end
Then you can just mix it in to your Models.
class MyModel<ActiveRecord::Base
include TagsArray
end
Hope this helps.
Also you can drop the ! from the end of split and capitalize unless you want to alter the tags_array permanently each time you call the method.
Related
I use a gem to manage certain attributes of a gmail api integration, and I'm pretty happy with the way it works.
I want to add some local methods to act on the Gmail::Message class that is used in that gem.
i.e. I want to do something like this.
models/GmailMessage.rb
class GmailMessage < Gmail::Message
def initialize(gmail)
#create a Gmail::Message instance as a GmailMessage instance
self = gmail
end
def something_clever
#do something clever utilising the Gmail::Message methods
end
end
I don't want to persist it. But obviously I can't define self in that way.
To clarify, I want to take an instance of Gmail::Message and create a GmailMessage instance which is a straight copy of that other message.
I can then run methods like #gmail.subject and #gmail.html, but also run #gmail.something_clever... and save local attributes if necessary.
Am I completely crazy?
You can use concept of mixin, wherein you include a Module in another class to enhance it with additional functions.
Here is how to do it. To create a complete working example, I have created modules that resemble what you may have in your code base.
# Assumed to be present in 3rd party gem, dummy implementation used for demonstration
module Gmail
class Message
def initialize
#some_var = "there"
end
def subject
"Hi"
end
end
end
# Your code
module GmailMessage
# You can code this method assuming as if it is an instance method
# of Gmail::Message. Once we include this module in that class, it
# will be able to call instance methods and access instance variables.
def something_clever
puts "Subject is #{subject} and #some_var = #{#some_var}"
end
end
# Enhance 3rd party class with your code by including your module
Gmail::Message.include(GmailMessage)
# Below gmail object will actually be obtained by reading the user inbox
# Lets create it explicitly for demonstration purposes.
gmail = Gmail::Message.new
# Method can access methods and instance variables of gmail object
p gmail.something_clever
#=> Subject is Hi and #some_var = there
# You can call the methods of original class as well on same object
p gmail.subject
#=> "Hi"
Following should work:
class GmailMessage < Gmail::Message
def initialize(extra)
super
# some additional stuff
#extra = extra
end
def something_clever
#do something clever utilising the Gmail::Message methods
end
end
GmailMessage.new # => will call first the initializer of Gmail::Message class..
Building upon what the other posters have said, you can use built-in class SimpleDelegator in ruby to wrap an existing message:
require 'delegate'
class MyMessage < SimpleDelegator
def my_clever_method
some_method_on_the_original_message + "woohoo"
end
end
class OriginalMessage
def some_method_on_the_original_message
"hey"
end
def another_original_method
"zoink"
end
end
original = OriginalMessage.new
wrapper = MyMessage.new(original)
puts wrapper.my_clever_method
# => "heywoohoo"
puts wrapper.another_original_method
# => "zoink"
As you can see, the wrapper automatically forwards method calls to the wrapped object.
I'm not sure why you can't just have a simple wrapper class...
class GmailMessage
def initialize(message)
#message = message
end
def something_clever
# do something clever here
end
def method_missing(m, *args, &block)
if #message.class.instance_methods.include?(m)
#message.send(m, *args, &block)
else
super
end
end
end
Then you can do...
#my_message = GmailMessage.new(#original_message)
#my_message will correctly respond to all the methods that were supported with #original_message and you can add your own methods to the class.
EDIT - changed thanks to #jeeper's observations in the comments
It's not the prettiest, but it works...
class GmailMessage < Gmail::Message
def initialize(message)
message.instance_variables.each do |variable|
self.instance_variable_set(
variable,
message.instance_variable_get(variable)
)
end
end
def something_clever
# do something clever here
end
end
Thanks for all your help guys.
I am facing a design decision I cannot solve. In the application a user will have the ability to create a campaign from a set of different campaign types available to them.
Originally, I implemented this by creating a Campaign and CampaignType model where a campaign has a campaign_type_id attribute to know which type of campaign it was.
I seeded the database with the possible CampaignType models. This allows me to fetch all CampaignType's and display them as options to users when creating a Campaign.
I was looking to refactor because in this solution I am stuck using switch or if/else blocks to check what type a campaign is before performing logic (no subclasses).
The alternative is to get rid of CampaignType table and use a simple type attribute on the Campaign model. This allows me to create Subclasses of Campaign and get rid of the switch and if/else blocks.
The problem with this approach is I still need to be able to list all available campaign types to my users. This means I need to iterate Campaign.subclasses to get the classes. This works except it also means I need to add a bunch of attributes to each subclass as methods for displaying in UI.
Original
CampaignType.create! :fa_icon => "fa-line-chart", :avatar=> "spend.png", :name => "Spend Based", :short_description => "Spend X Get Y"
In STI
class SpendBasedCampaign < Campaign
def name
"Spend Based"
end
def fa_icon
"fa-line-chart"
end
def avatar
"spend.png"
end
end
Neither way feels right to me. What is the best approach to this problem?
A not very performant solution using phantom methods. This technique only works with Ruby >= 2.0, because since 2.0, unbound methods from modules can be bound to any object, while in earlier versions, any unbound method can only be bound to the objects kind_of? the class defining that method.
# app/models/campaign.rb
class Campaign < ActiveRecord::Base
enum :campaign_type => [:spend_based, ...]
def method_missing(name, *args, &block)
campaign_type_module.instance_method(name).bind(self).call
rescue NameError
super
end
def respond_to_missing?(name, include_private=false)
super || campaign_type_module.instance_methods(include_private).include?(name)
end
private
def campaign_type_module
Campaigns.const_get(campaign_type.camelize)
end
end
# app/models/campaigns/spend_based.rb
module Campaigns
module SpendBased
def name
"Spend Based"
end
def fa_icon
"fa-line-chart"
end
def avatar
"spend.png"
end
end
end
Update
Use class macros to improve performance, and keep your models as clean as possible by hiding nasty things to concerns and builder.
This is your model class:
# app/models/campaign.rb
class Campaign < ActiveRecord::Base
include CampaignAttributes
enum :campaign_type => [:spend_based, ...]
campaign_attr :name, :fa_icon, :avatar, ...
end
And this is your campaign type definition:
# app/models/campaigns/spend_based.rb
Campaigns.build 'SpendBased' do
name 'Spend Based'
fa_icon 'fa-line-chart'
avatar 'spend.png'
end
A concern providing campaign_attr to your model class:
# app/models/concerns/campaign_attributes.rb
module CampaignAttributes
extend ActiveSupport::Concern
module ClassMethods
private
def campaign_attr(*names)
names.each do |name|
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{name}
Campaigns.const_get(campaign_type.camelize).instance_method(:#{name}).bind(self).call
end
EOS
end
end
end
end
And finally, the module builder:
# app/models/campaigns/builder.rb
module Campaigns
class Builder < BasicObject
def initialize
#mod = ::Module.new
end
def method_missing(name, *args)
value = args.shift
#mod.send(:define_method, name) { value }
end
def build(&block)
instance_eval &block
#mod
end
end
def self.build(module_name, &block)
const_set module_name, Builder.new.build(&block)
end
end
I have a very big function in my model and I want to store it somewhere else in order to keep my model dry. I read that storing methods in ApplicationHelper and then calling them from a model is a bad idea. What is a good idea then?
I want to have a separate file with my big methods and call them from a model.
You can create a "plain old ruby object (PORO)" to do your work for you. let's say you had a method that calculates the amount overdue for a user.
So, you can create app/services/calculates_overages.rb
class CalculatesOverages
def initialize(user)
#user = user
end
def calculate
# your method goes here
end
end
Then, you can:
class User < ActiveRecord::Base
def overage_amount
CaluclatesOverage.new(self).calculate
end
end
Or, in a controller you could:
def show
#amount = CaluclatesOverage.new(current_user).calculate
end
The app/services directory could also be app/models, or the lib directory. There's no set convention for this (yet).
Use a Concern. https://gist.github.com/1014971
It's simple. In app/models/concerns create a file your_functionality.rb as follows:
module YourFunctionality
extend ActiveSupport::Concern
def your_fat_method
# insert...
end
end
And in your model simply:
include YourFunctionality
I want to incorporate the strategy pattern in my application.
I have stored under lib the following classes.
class Network
def search
raise "NO"
end
def w_read
raise "NO"
end
#...
end
AND
class FacebookClass < Network
def search
# FacebookClass specific...
end
def w_read
raise OneError.new("...")
end
end
AND
class TwitterClass < Network
def search
# TwitterClass specific...
end
def w_read
# TwitterClass specific...
end
def write
# TwitterClass specific...
end
end
Now I want to call the method search of TwitterClass from app/model/network_searcher.rb. How can I do that? Did I implemented the strategy pattern here successfully?
Going by the example in the Wikipedia, I think your app/model/network_searcher should be something like this
class NetworkSearcher
def initialize(search_class)
#search_class = search_class
end
def search_social
#search_class.search
end
def w_read_social
#search_class.w_read
end
def write_social
#search_class.write
end
end
Then in controller or where you want to invoke it, you can call like this:
search_class = TwitterClass.new # or FacebookClass.new
network_searcher = NetworkSearch.new(search_class)
network_searcher.search_social # or network_searcher.w_read_social or network_searcher.write_social
Also if you are keeping these classes in lib, for Rails 3, inorder to get these classes autoloaded, you need to add this line to config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
and also follow the naming convention for the filenames in Rails (for example TwitterClass should be named twitter_class.rb). Otherwise you will have to require these files wherever you are using these classes.
The strategy pattern is used to allow the algorithm to use to be selected at runtime. Without more details it's hard to say if this is appropriate to your problem. Assuming that it is then what you need is a way to set the search on your model and you can then use the selected algorithm elsewhere in your model. e.g.
class TheInformation
attr_writer :searcher
def other_method
..
# can use the selected searcher here
#searcher.search
..
end
end
Does that help?
I'm not sure if macro is even the correct term. Basically, I want to be able to configure ActiveRecord columns easily (using the familiar AR syntax) so that before_save they will always be formatted a certain way by calling an instance method.
I'd like to make all of this accessable from a mixin.
For example:
class MyClass < ActiveRecord::Base
happy_columns :col1, :col2 # I really want this type of convenient syntax
# dynamically created stuff below from a mixin.
before_save :make_col1_happy
before_save :make_col2_happy
def make_col1_happy; self.col1 += " is happy"; end
def make_col2_happy; self.col2 += " is happy"; end
end
try to extend ActiveRecord , a.e.
#in lib/happy_columns.rb
module HappyColumns
def happy_columns(cols)
cols.each do |c|
before_filter "make_#{c}_happy".to_sym
#here you could define your instance methot using define_method
define_method "make_#{c}_happy" do
#your code
end
end
include InstanceMethods
end
module InstanceMethods
#here you could define other your instancemethod
end
end
ActiveRecord::Base.extend HappyColumns
be sure of include the extensions in your load path , then you could use happy_cols in your model.
sorry if there is some mistake , for define_method look at this .
hope this could help.