How to extend model in rails 4.2 - ruby-on-rails

I have a user model
class User
def fname
#fname
end
def fname=(str)
#fname = str
end
def greeting
"Hello #{#fname}"
end
end
But I want to remove the greeting method to somewhere else so that my user model don't include the business logic.
How should I achieved that?
I try to create a module(foo.rb) in lib but its not working. Should I include in User model?
Updated Info:
I updated my code
module UserBusinessEntity
def speak(sound)
return "#{sound} is its sound"
end
def greeting
"#{self.id} Hello, #{self.fname} #{self.lname} you are #{self.age} years old"
end
end
class User < ActiveRecord::Base
include UserBusinessEntity
end
This works if both code in same file.i.e. app/models/User.rb
But I want to move the module UserBusinessEntity code to app/services/
Do I have to add require at User Model. If so I added like require UserBusinessEntity But Its gives uninitialized constant UserBusinessEntity

Just create a module like this:
module Foo
def greeting
"Hello #{self.fname}"
end
end
Then include the module in your User module:
class User
include Foo
# ...
end
Then you can call in a controller or a view
#user = User.new
#user.greeting

I believe you may use greeting to render in views or mailers. So this is a showcase of using presenter. A good article is here.
Basically, defining a presenter will be:
app/presenters/user_presenter.rb
class UserPresenter < DelegateClass(User)
def greeting
"Hello #{fname}"
end
end
There are many ways to define, the above is just basic, check out above article for detail.
Then, you can use it anywhere you want to:
#user = User.first
UserPresenter.new(user).greeting
Or even in a view
example.html.erb
<p><%= UserPresenter.new(user).greeting %><p>
Moreover, people may use concern to implement this, but with me that is not a good practice!

Related

Encapsulating controller logic in rails

I have 2 controllers in rails with different authentications schemes,
but they do almost the same.
What is the best way in rails to encapsulate
the logic of a controller in another class or helper?
Sample:
def ControllerA < BasicAuthController
def create
blablacode
end
end
def ControllerB < TokenAuthController
def create
blablacode
end
end
Whats the proper way to do this? create a model with the code?
Create a helper? other?
The simplest thing is to make a module and then include it into the other controllers:
module ControllerMixin
def create
blablacode
end
end
The remaining question, though, is where to put this code such that it is works with Rails autoloader, since it needs to be loaded before the controllers. One way to do it would be to write the module to a file in the lib/ directory, then add that to the autoload paths (see auto-loading-lib-files-in-rails-4
Why don't you enable both schemes for a single controller? Especially if the only difference is Authentication. You could have two app/controllers/concerns to encapsulate both authentication methods and include Auth1 and include Auth2 for a single controller who is only responsible for whatever resource it manages.
Otherwise, services are the best approach to encapsulate controller logic.
Create a folder called services in your app folder and write PORO classes here. Say you have a few places in your app where you want to pay for stuff via make Stripe.
# app/services/stripe_service.rb
module StripeService
def customer(args)
...
end
def pay(amount, customer)
...
end
def reverse(stripe_txn_id)
...
end
end
# controller
StripeService.customer(data)
=> <#Stripe::Customer>
Or if you only need to do one thing.
# app/services/some_thing.rb
module SomeThing
def call
# do stuff
end
end
# controller
SomeThing.call
=> # w/e
If you need an object with multiple reponsibilities you could create a class instead.
class ReportingService
def initialize(args)
...
end
def query
...
end
def data
...
end
def to_json
...
end
end
https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
I do it something like this:
#app/services/my_app/services/authentication.rb
class MyApp::Services::Authentication
class < self
def call(params={})
new(params).call
end
end # Class Methods
#==============================================================================================
# Instance Methods
#==============================================================================================
def initialize(params)
#params = params
end
def call
... do a lot of clever stuff
... end by returning true or false
end
private
def params() #params end
end
Then:
class FooController < ApplicationController
before_action :authenticate
def authenticate
redirect_to 'some_path' unless MyApp::Services::Authenticate.call(with: 'some_params')
end
end
Short answer, i choose to create a Helper.
From all the suggestions in the answers
Create a Module:
Seems correct but it didnt feel right to have logic outside
the app directory. This wasnt an external module or library but
something very related to the logic of my app.
Integrate diferents authentications in one controller:
Was a good suggestion but i have to change all the logic of my app.
Create a Helpers:
It seems to me the better solution, i had the code on a helper, and
is inside the app directory, very near from the other logic.

Rails, place to put user input parsing

In my Rails app there is a view with a simple user form consisting of a text box and a submit button.
When the user submits the form, depending on his input, different models are created:
class MessageController < ApplicationController
def create
if is_foo params[:text]
Foo.create
else
Bar.create
end
end
def is_foo(text)
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
My question is, do you think that it's a better design to put the "is_foo" logic inside the Foo model instead of the controller like so?
Model:
class Foo < ActiveRecord::Base
def self.is_foo(text)
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
Controller:
class MessageController < ApplicationController
def create
if Foo.is_foo params[:text]
Foo.create
else
Bar.create
end
end
end
On one hand, the model should take care of the logic. On the other, this isn't really logic, its more of an input rule... What do you think guys?
Helper
I'd leave the is_foo out of the model, as model logic should be to do with the model directly, not determining which model should be created / saved
I would personally look at using a helper method for the test - calling the file ControllerHelper or similar:
#app/helpers/controller_helper.rb
class ControllerHelper
def is_foo? text
# Here the message gets parsed
# i.e if text[0] == "M"
end
end
This will allow you to call the helper in your controller, giving you the ability to use the logic to form the fixes:
#app/controllers/messages_controller.rb
class MessagesController < ApplicationController
include ControllerHelper
def create
model = is_foo?(params[:text]) ? "foo" : "bar"
model.constantize.send(:create)
end
end
I wouldn't call it a ControllerHelper module as mentioned in Rich Pecks answer (since helpers in Rails are view-related), but something like
# app/lib/foo_bar_creator.rb
FooBarCreator = Struct.new(:params) do
def create
build.save
end
def build
klass.new
end
def is_foo?
params[:text] == 'foo'
end
def klass
is_foo? ? Foo : Bar
end
end
(some call these kind of classes "Service Objects")
This way I could just call FooBarCreator.new(params).create in my controller.

devise: instance the current_user using single table inheritance

I am using rails 3.0.9 and devise for authentication. Now I'm trying to use single table inheritance because I need to use polymorphism, so I have two classes: UserType1 and UserType2, which inherit from User class. I need that Devise instance correctly the current_user depending the type of user.
For example,
class User < ActiveRecord::Base
#devise and other user logic
end
class UserType1 < User
def get_some_attribute
return "Hello, my type is UserType1"
end
end
class UserType2 < User
def get_some_attribute
return "Hello, my type is UserType2"
end
end
In controller
class MyController < ApplicationController
def action
#message = current_user.get_some_attribute #depending the type using polymorphism
render :my_view
end
end
it's exactly what you need : http://blog.jeffsaracco.com/ruby-on-rails-polymorphic-user-model-with-devise-authentication
you need to override the sign in path method in your application controller, hope it help.
You will need to add get_some_attribute method inside User model
Module User < ActiveRecord::Base
#devise and other user logic
def get_some_attribute
#You can put shared logic between the two users type here
end
end
then, to override it in the user sub types, like this:
Module UserType1 < User
def get_some_attribute
super
return "Hello, my type is UserType1"
end
end
Module UserType2 < User
def get_some_attribute
super
return "Hello, my type is UserType2"
end
end
Then, current_user.get_some_attribute will work as you expecting, if you like to read more about overriding methods in Ruby, you can read about it here
I added super as I assumed that you have some shared logic in get_some_attribute, as it will call get_some_attribute in User model, you can remove it if you don't need it.
Good luck!

How does CanTango work?

I am new to Rails and I need your help.
I have this:
# config/initializers/cantango.rb
CanTango.config do |config|
config.engines.all :on
# more configuration here...
end
# app/models.User.rb
class User < ActiveRecord::Base
def roles_list
roles_rel = Role.where(:user_id=>self.id)
roles=[]
roles_rel.each do |x|
roles.push(x.name)
end
return roles #return [":reader","writer"] from database
end
end
# app/permits/reader_permits.rb
class ReaderPermit < CanTango::UserPermit
def initialize ability
super
end
protected
def permit_rules
can :read, :all
end
end
In my view I have
<%= link_to 'readddd', "/news/feed/read_full?s=#{g.id}&page_id=#{params[:page_id]}" if user_can?(:read, Newsfeed)%>
but I get an error undefined method 'user_can?' for #<#<Class:0xaf41f50>:0xaf40eac>
Please give me a very simple explanation for my situation, where and what I must write. GitHub doesn't help me.
Fallow the tutorial here:
https://github.com/kristianmandrup/cantango/wiki/Quickstart
I think you forgot this: Create and register a User model
First you must have a User model. Use the tango_user macro in order to
register a user class with CanTango. CanTango will then generate User
APIs methods such as #user_can? for the User class, admin_can? for a
registered Admin user class etc.
class User
# register as a "user class" for CanTango
tango_user
end

Virtual attributes in plugin

I need some help with virtual attributes. This code works fine but how do I use it inside a plugin. The goal is to add this methods to all classes that uses the plugin.
class Article < ActiveRecord::Base
attr_accessor :title, :permalink
def title
if #title
#title
elsif self.page
self.page.title
else
""
end
end
def permalink
if #permalink
#permalink
elsif self.page
self.page.permalink
else
""
end
end
end
Thanks
You can run the plugin generator to get started.
script/generate plugin acts_as_page
You can then add a module which defines acts_as_page and extends it into all models.
# in plugins/acts_as_page/lib/acts_as_page.rb
module ActsAsPage
def acts_as_page
# ...
end
end
# in plugins/acts_as_page/init.rb
class ActiveRecord::Base
extend ActsAsPage
end
This way the acts_as_page method is available as a class method to all models and you can define any behavior into there. You could do something like this...
module ActsAsPage
def acts_as_page
attr_writer :title, :permalink
include Behavior
end
module Behavior
def title
# ...
end
def permalink
# ...
end
end
end
And then when you call acts_as_page in the model...
class Article < ActiveRecord::Base
acts_as_page
end
It will define the attributes and add the methods. If you need things to be a bit more dynamic (such as if you want the acts_as_page method to take arguments which changes the behavior) try out the solution I present in this Railscasts episode.
It appears that you want a Module for this
# my_methods.rb
module MyMethods
def my_method_a
"Hello"
end
end
The you want to include it into the classes you want to use it for.
class MyClass < ActiveRecord::Base
include MyMethods
end
> m = MyClass.new
> m.my_method_a
=> "Hello!"
Take a look here for more information on mixing in modules. You can put the module wherever in a plugin if you like, just ensure its named correctly so Rails can find it.
Create a module structure like YourPlugin::InstanceMethods and include it this module like this:
module YourPlugin
module InstanceMethods
# your methods
end
end
ActiveRecord::Base.__send__(:include, YourPlugin::InstanceMethods)
You have to use __send__ to make your code Ruby 1.9 compatible. The __send__ line is usually placed at the init.rb file on your plugin root directory.

Resources