I am new to rails and am figuring out the best ways to write certain methods.
I am trying to write a method that belongs to a certain model and can be accessed without initiating an instance. So far I have this under my class PaymentNotification < ActiveRecord::Base
def url
url_for(:controller => 'payment_notifications', :only_path => false)
end
The problem here is that I need to do this to access the url
n = PaymentNotification.new
n.url
In my code, I want to be able to write PaymentNotification.url to access the method relevant to that model.
Maybe I am thinking about this the wrong way and someone can guide me. Basically, what I am trying to achieve is that each model can have its set of methods and attributes so that they all are organized and I know from the code which file each method is declared in, instead of just calling a
payment_notification_url
which may be located in any of the irrelevant initialization files. I saw helper methods but it seems like i still won't be able to use a dot syntax and will have to write something like "payment_notification_url" to access my url
Any ideas on the best way to go about doing this?
You need to define a class method via the self keyword.
def self.url
url_for(:controller => 'payment_notifications', :only_path => false)
end
Then you can use PaymentNotification.url
class A
def self.a
p "Class method"
end
def b
p "Instance Method"
end
end
A.a #Class method
#A.b #NoMethodError
a = A.new
a.b #Instance Method
#a.a #NoMethodError
Related
I would like to do something like:
class TestController < InheritedResources::Base
def test_method
self.var1 + self.var2
end
private
def test_params
params.require(:test).permit(:var1, :var2)
end
end
Where in the view I could call from the built in controller index:
test.test_method
I've tried adding a create method to the controller as follows:
def create
Test.create!(require(:test).permit(:var1, :var2, :test_method))
end
I've also tried updating the params directly:
private
def test_params
params.require(:test).permit(:var1, :var2, :test_method)
end
I've also tried making a helper method, but I knew that was doomed to fail because it wouldn't have access to var1 and var2.
I guess I just don't understand two things: one how to make my var1 and var2 white-listed so I can use them, and more importantly how to add a method to my model using strong parameters, because attr_accessible doesn't work in my models anymore.
EDIT:
Let me rephrase a little, maybe it will help. I can get access to individual Test objects in my view with a simple call to tests.each |test| in the view. I just want to make methods that act on my already defined active record variables for that object, hence var1 and var2. The problem is when I define a new method in my controller it is private to the object and I won't have access to it with a call from an instance of the object. Better yet, I would like to just be able to define a new variable, local to the object, that is created after it has propagated its other fields from the db.
EDIT2: I'm aware I'm probably missing the design pattern here. It can be hard to describe that I want X, when really I need Z. Thanks for the patience.
Thanks for the help.
There's no reason for white-listing parameters that you'll directly use.
White-listing with strong parameters is useful only when you call function like ActiveRecord#update that simply take every key from the dictionary, so you can control with key you want to allow and which not.
In this case, just do:
class TestController < InheritedResources::Base
def test_method
#result = params[:var1] + params[:var2]
end
end
And in your view, just print the #result variable wherever you want
<%= #result %>
This is the Rails way. You can of course call the variable as you want.
Helper methods are useful only for more complex cases.
class Temp1
def add(s)
match = 'test'
self.class.class_eval do
define_method(s) do
puts match
end
end
#match ='haha'
end
end
As i thinks of it, 'match' is a local variable, so i don't understand how it can see it from another method, plus if uncomment #match ='haha', method will print 'haha' somehow. Can somebody explain it?
Also, i don't see difference here between using class_eval or instance_eval, seems like it do the same thing.
And, at last but not least, can I create class method here using define_method? So I can call it like Temp1.something instead of Temp1.new.something?
Because blocks (do...end) are closures and have access to their
surrounding scope.
You used block with class_eval,so it has the access to its surroundings,which is the scope of the method add. Now you use another block with define_method,which as also has the access to the scope of the method add,via the block with the class_eval.match local variable has been created inside the scope of the method add. So the blocks has the access to the variable.
And, at last but not least, can I create class method here using define_method?
No you can't.define_method Defines an instance method in the receiver.self.class is Temp1. Now under Temp1.class_eval do..end,you are defining instance methods of the class Temp1,with the method define_method.define_method is a private class method of all the classes,in which ancestor chain Object class present.
class C;end
C.private_methods.grep(/define_/)
# => [:define_method]
Also, i don't see difference here between using class_eval or instance_eval, seems like it do the same thing.
Okay! Let me explain for you. You can't see the difference here,as Teamp1 is a Class and also an instance of a Class. In both call class_eval and instance_eval,self is being set to Teamp1,by their respective definition as documented.
class C
def self.bar;11;end
def baz;12;end
end
C.is_a? Class # => true
C.instance_of? Class # => true
C.class_eval{ bar } # => 11
C.instance_eval{ bar } # => 11
Hope this helps!
Let's say I have a model called Article:
class Article < ActiveRecord::Base
end
And then I have a class that is intended to add behavior to an article object (a decorator):
class ArticleDecorator
def format_title
end
end
If I wanted to extend behavior of an article object, I could make ArticleDecorator a module and then call article.extend(ArticleDecorator), but I'd prefer something like this:
article = ArticleDecorator.decorate(Article.top_articles.first) # for single object
or
articles = ArticleDecorator.decorate(Article.all) # for collection of objects
How would I go about implementing this decorate method?
What exactly do you want from decorate method? Should it simply add some new methods to passed objects or it should automatically wrap methods of these objects with corresponding format methods? And why do you want ArticleDecorator to be a class and not just a module?
Updated:
Seems like solution from nathanvda is what you need, but I'd suggest a bit cleaner version:
module ArticleDecorator
def format_title
"#{title} [decorated]"
end
def self.decorate(object_or_objects_to_decorate)
object_or_objects_to_decorate.tap do |objects|
Array(objects).each { |obj| obj.extend ArticleDecorator }
end
end
end
It does the same thing, but:
Avoids checking type of the arguments relying on Kernel#Array method.
Calls Object#extend directly (it's a public method so there's no need in invoking it through send).
Object#extend includes only instance methods so we can put them right in ArticleDecorator without wrapping them with another module.
May I propose a solution which is not using Module mixins and thereby granting you more flexibility. For example, using a solution a bit more like the traditional GoF decorator, you can unwrap your Article (you can't remove a mixin if it is applied once) and it even allows you to exchange the wrapped Article for another one in runtime.
Here is my code:
class ArticleDecorator < BasicObject
def self.[](instance_or_array)
if instance_or_array.respond_to?(:to_a)
instance_or_array.map {|instance| new(instance) }
else
new(instance_or_array)
end
end
attr_accessor :wrapped_article
def initialize(wrapped_article)
#wrapped_article = wrapped_article
end
def format_title
#wrapped_article.title.upcase
end
protected
def method_missing(method, *arguments)
#wrapped_article.method(method).call(*arguments)
end
end
You can now extend a single Article by calling
extended_article = ArticleDecorator[article]
or multiple articles by calling
articles = [article_a, article_b]
extended_articles = ArticleDecorator[articles]
You can regain the original Article by calling
extended_article.wrapped_article
Or you can exchange the wrapped Article inside like this
extended_article = ArticleDecorator[article_a]
extended_article.format_title
# => "FIRST"
extended_article.wrapped_article = article_b
extended_article.format_title
# => "SECOND"
Because the ArticleDecorator extends the BasicObject class, which has almost no methods already defined, even things like #class and #object_id stay the same for the wrapped item:
article.object_id
# => 123
extended_article = ArticleDecorator[article]
extended_article.object_id
# => 123
Notice though that BasicObject exists only in Ruby 1.9 and above.
You'd extend the article class instance, call alias_method, and point it at whatever method you want (although it sounds like a module, not a class, at least right now). The new version gets the return value and processes it like normal.
In your case, sounds like you want to match up things like "format_.*" to their respective property getters.
Which part is tripping you up?
module ArticleDecorator
def format_title
"Title: #{title}"
end
end
article = Article.top_articles.first.extend(ArticleDecorator) # for single object
Should work fine.
articles = Article.all.extend(ArticleDecorator)
May also work depending on ActiveRecord support for extending a set of objects.
You may also consider using ActiveSupport::Concern.
So this is a bit of a silly one and is more lack of programming knowledge rather than anything ruby or rails specific.
If i wanted to turn an ordinary class into hash its not so bad. I already have one:
class CustomRequest
require 'json'
require 'net/http'
attr_accessor :url, :depth, :status
def initialize(url,depth)
#url = url
#depth = depth
end
def make_me_hash_and_send
#myReq = {:url => #url, :depth =>#depth}
myJsonReq = #myReq
puts myJsonReq
res = Net::HTTP.post_form(URI.parse('http://127.0.0.1:3008/user_requests/add.json'),
myJsonReq)
end
end
Simply generates the hash from the internal variables that are passed in the constructor. I want to do the same for active record but the abstractness of it isn't making it easy.
Lets say I have this class
def turn_my_insides_to_hash
#How do I take the actual record variables
# nd generate a hash from them.
#Is it something like
#myHash = {:myString = self.myString
:myInt => self.myInt }
end
I may be looking at this the wrong way. I know Outside of the class I could simply say
#x = Result.find(passed_id).to_hash
and then do what I want to it. But I would rather call something liks
#x = Result.send
(which turns the result's variables into hash and sends them)
I already have the send part, just need to know how to turn variables into hash from inside class.
You could try use JSON instead of YAML:
Result.find(passed_id).to_json
or
Result.find(passed_id).attributes.to_json
also you can use options like :except and :only for to_json method.
Result.find(passed_id).attributes.to_json(:only => ['status', 'message'])
record.serializable_hash
http://api.rubyonrails.org/v4.0.12/classes/ActiveModel/Serialization.html#method-i-serializable_hash
I write something more because SO ask me to do so.
I have a private method in my Rails app to connect to Amazon S3, execute a passed block of code, then close the connection to S3. It looks like so;
def S3
AWS::S3::Base.establish_connection!(
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
)
data = yield
AWS::S3::Base.disconnect
data
end
It is called like this (as an example);
send_data(S3 {AWS::S3::S3Object.value("#{#upload_file.name}",'bucket')}, :filename => #upload_file.name)
I call this method in a number of ways in my controller and model so have it included in both classes as a private method. This works fine and I'm happy with it but it's not very DRY.
How can I make this method accessible to both my model and controller but only have the code appear once? This is more of a Ruby question than a Rails question and reflects my newness to OOP. I'm guessing a module or a mix-in is the answer but I haven't really been using either of these up until now and need a little hand-holding.
Thanks.
Modules are used for 3 different things in ruby. First is namespacing. Having class or constant definitions inside a module won't collide with classes or constants outside that module. Something like this
class Product
def foo
puts 'first'
end
end
module Affiliate
class Product
puts 'second'
end
end
p = Product.new
p.foo # => 'first'
p = Affiliate::Product.new
p.foo # => 'second'
The second use for modules is as a place to stick methods that don't really have a place anywhere else. You can do this inside a class too, but using a module sort of tells people reading the code that it is not meant to be instanciated. Something like this
module Foo
def self.bar
puts 'hi'
end
end
Foo.bar #=> 'hi'
Finally (and the most confusing) is that modules can be included into other classes. Using them this way is also referred to as a mixin, because you are "mixing in" all the methods into whatever you are including.
module Foo
def bar
puts 'hi'
end
end
class Baz
include Foo
end
b = Baz.new
b.bar #=> 'hi'
Mixins are actually a way more complected topic then I am covering here, but going deeper would probably be confusing.
Now, to me, S3 seems to be something that really belongs in the controller, since controllers are usually the things dealing with incoming and outgoing connections. If that is the case, I would just have a protected method on application controller, since that will be accessible to all other controllers, but still be private.
If you do have a good reason for it being in the model too, I would go for a mixin. Something like
module AwsUtils
private
def S3
AWS::S3::Base.establish_connection!\
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
data = yield
AWS::S3::Base.disconnect
data
end
end
If you put that in lib/aws_utils.rb, you should be able to use it by adding include AwsUtils in both your controller and your model. Rails knows to look for classes and modules in lib, but only if the name matches (in wide case). I called it AwsUtils because I know what rails will look for when it sees that (aws_utils.rb), and to be honest, I have no idea what it will need for S3Utils ;-)
Feel free to ask for more info if I wasn't clear on something. Modules tend to be one of those things in ruby that while amazing, are downright baffling to newcomers.
Your hunch is correct: you can put a module in the lib directory. In
order to make these methods available to your models, simply include it
with:
class Model < ActiveRecord::Base
include MyModule
end
The included module's instance methods will become instance methods on your class. (This is known as a mixin)
module MyModule
def S3
#...
end
end
You can write a module as :
module MyModule
def self.S3(args*)
AWS::S3::Base.establish_connection!(
:access_key_id => 'Not telling',
:secret_access_key => 'Really not telling'
)
data = yield
AWS::S3::Base.disconnect
data
end
end
and then call it in your controller or model as
MyModule.S3(params*)