Internal rails notifications - ruby-on-rails

I'm creating an rails app, which imports some stuff from external service. At the end of that import, user should get an info about how many new items has been imported (it's like a periodical check, which adds just new items to local database. Not all of them each time.). A whole process and method-chain is quite complex so i'm looking for a best-practice on how to pass such information from deeply nested method. Schema looks more or less like that:
some_controller.rb
-> Model.method()
-> Lib1.method1()
-> Lib2.method2()
-> Lib3.method3()
-> Lib4.method4()
-> Lib5.method5()
-> items_import_method()
and i need to somehow pass info about how many new items has been imported from items_imported_method() to some_controller.rb (or any other place this import is fired). The most obvious way of doing that is passing new_items_count var all the way up though this whole method chain but it seems a bit wrong to me. Isn't there any better way?
I was thinking about some kind of internal events/messages system which would let me to subscribe to some custom channel, like activeresource events but maybe there is some well-known and suggested approach for such situation?
Thanks in advance.

One way to tackle this is to create some kind of context container object that has properties any of those steps can manipulate, then pass this in to each method on the way down.
For example:
class ContextExample
attr_accessor :example_field
end
In practice:
#context = ContextExample.new
Lib1.method1(#context)
#context.example_field
# => "Test!"
Where what you're doing is:
module Lib1
def self.method1(context)
Lib2.method2(context)
end
end
module Lib2
def self.method2(context)
context.example_field = "Test!"
end
end
These modules or classes can save context if required, and pass it on when necessary.
If you have well-defined properties you need to set and manipulate, a class with attr_accessor for those usually works pretty well. If it's a more arbitrary thing and you're not sure what might shake out in the end, either use a Hash or an OpenStruct object to capture whatever might come up.

Related

RIght way of writing module methods in Ruby

what is right way of writing module? is it only used to stock some peace of code to minimize the number of lines, or is it something much more important than that
I have used and seen ways of writing module, I am working on setting up correct way to define and standardised module. this example is kind of controller code that we use in rails
Way 1 :-
module B
extend ActiveSupport::Concern
def process_items
# do somthing...
#items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items
end
end
Way 2 :-
module B
extend ActiveSupport::Concern
def process_items(items)
# do somthing...
items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items(#items)
end
end
Way 1 :-
When I see this independently, its not much readable as I don't know how #items appeared in this method
Unit testing would be hard for method as its dependent
Way 2 :-
Looking at method I can see input is coming we are processing it and returning it back (readablity is good)
Unit testing is easy to this, we wll call method pass what it needs and expect
The way I see modules should be independent, self explanatory, it should be generic so that can be used in any class, kind of helpers. But other way could be dependent on where we use modules
We are using modules like in rails
We use conccern in models, when we call module method we can use self.<field> we don't need to pass anything because instance variable is supposed to be accesssable in every instance method
View helpers are modules I see they put logic into it hard to understand how the variable come from may be instance variable or params, what about making it method which accept somthing and return it back
Concerns on controllers, like the example I have given
I would like to have thoughts on this, what is best approach out of it? is it something which can be standarise or it is more situational or I don't know yet :)
Note: -
I was looking at this question but answer given on this question is no more valid as referenced links are not working.
Right Way to Use Module
The difference here is practically academic, as if you have attr_reader :x then both #x and x will have the same meaning.
It's understood that within a mixin module you will be referencing methods and/or variables that are part of the class or module doing the "mixing in". As such, seeing #x, or in your case, #items, should not come as a real surprise.
If you want to add it as an explicit argument you're sort of missing a lot of the benefits of using a mixin in the first place. You don't need to mix it in at all, you can just use it like B.process_items(...). In other words, your second approach is having an identity crisis. Is it a stand-alone module that includes Concern for no reason, or a weak mixin?
When it comes to testing, you must test the mixin in a module or class which implements the required features. In this case you need either an #items variable, or an items method, and that must have a value of the expected type.
This should be documented somewhere for clarity, but is effectively an implicit contract with anyone using this module.

Do I need a global variable or better OO design in this rails application?

I'm writing a rails application that uses the discogs api ruby wrapper by buntine and most of my objects need access to the #discogs object that exists in the controller and represents the entire ruby wrapper and connection to discogs. Instead of passing #discogs to other objects, which in turn are working with other objects that of course need #discogs passed to them should I be making #discogs a global variable in my application to some extent?
I feel like I'm writing bad code passing this one object around, but I'm also hesitant that about a global scope solution, and I'm not even sure what the reasonable way to do that is in rails.
Advice much appreciated!
Rather than passing a #discogs variable around your application, you can create a global object in an initializer like so:
config/initializers/discogs.rb
DiscogsWrapper = Discogs::Wrapper.new("My awesome web app")
Now you can refer to the DiscogsWrapper object in other parts of your application.
Example 1:
module Artists
def self.search(name, wrapper = DiscogsWrapper)
wrapper.search(name)
end
end
Example 2:
class Artist
def get
wrapper.get_artist(discogs_id)
end
private
def wrapper
DiscogsWrapper
end
end
In my opinion, this approach is reasonable; I've seen it in other applications and it works well.
Hope that helps. Good luck!

Rails Limit Model To 1 Record

I am trying to create a section in my app where a user can update certain site wide attributes. An example is a sales tax percent. Even though this amount is relatively constant, it does change every few years.
Currently I have created a Globals model with attributes I want to keep track of. For example, to access these attributes where needed, I could simply do something like the following snippet.
(1+ Globals.first.sales_tax) * #item.total
What is the best way to handle variables that do not change often, and are applied site wide? If I use this method is there a way to limit the model to one record? A final but more sobering question.......Am I even on the right track?
Ok, so I've dealt with this before, as a design pattern, it is not the ideal way to do things IMO, but it can sometimes be the only way, especially if you don't have direct disk write access, as you would if deployed on Heroku. Here is the solution.
class Global < ActiveRecord::Base
validate :only_one
private
def only_one
if Global.count >= 1
errors.add :base, 'There can only be one global setting/your message here'
end
end
end
If you DO have direct disk access, you can create a YAML config file that you can read/write/dump to when a user edits a config variable.
For example, you could have a yaml file in config/locales/globals.yml
When you wanted to edit it, you could write
filepath = "#{Rails.root}/config/locales/globals.yml"
globals = YAML.load(File.read("#{Rails.root}/config/locales/globals.yml"))
globals.merge!({ sales_tax: 0.07 })
File.write(filepath) do |f|
f.write YAML.dump(globals)
end
More on the ruby yaml documentation
You could also use JSON, XML, or whatever markup language you want
It seems to me like you are pretty close, but depending on the data structure you end up with, I would change it to
(1+ Globals.last.sales_tax) * #item.total
and then build some type of interface that either:
Allows a user to create a new Globals object (perhaps duplicating the existing one) - the use case here being that there is some archive of when these things changed, although you could argue that this should really be a warehousing function (I'm not sure of the scope of your project).
Allows a user to update the existing Globals object using something like paper_trail to track the changes (in which case you might want validations like those presented by #Brian Wheeler).
Alternatively, you could pivot the Global object and instead use something like a kind or type column to delineate different values so that you would have:
(1+ Globals.where(kind: 'Colorado Sales Tax').last) * #item.total
and still build interfaces similar to the ones described above.
You can create a create a class and dump all your constants in it.
For instance:
class Global
#sales_tax = 0.9
def sales_tax
#sales_tax
end
end
and access it like:
Global.sales_tax
Or, you can define global variables something on the lines of this post

Calling active record method from active resource (Ruby/Rails)

Apologies for the very broadly title question.
Basically this follows on from my earlier question about defs and how they are called on instantiated methods.
Basically the way I have it now:
I set up an active resource on client side and post it with .save
This then goes through my controller on server and stores an active record of the same class.
so MyResource-->save-->MyRecord
The MyRecord is stored with a status column containing a simple string.
Thing is MyRecord class has a def called
def get_status
puts status
end #Amazing method, I know
In my mind If i wanted to execute the get_status on MyRecord, all I had to do was this.
(Please note this is client side)
#test = MyRecord.find(1)
#test.get_status
Sadly this is not the case as #test becomes an active resource and cant call a method it doesnt have.
(NOTE: My classes are not actually called MyRecord and MyResource, They are just title that for simplicity as I'd rather understand the solution than have someone solve it for me.)
Would anyone care to point me in the right direction to explain how I call the active record method from client side. Have I gone completely the wrong way about it and should it be processed in controller instead of model?
On a side note: Are there any alternatives to .save? My boss doesn't like it for reasons I cannot understand. (NOTE: he's a lead, I'm an intern therefore I don't argue or ask questions that seem like a challenge)
Honestly, ActiveResource needs a little work... I had to implement this exact feature where I work and ended up rolling my own semi RPC lib using basic http to share code between client and server.
Really though something like ruby-rpc or DRb would probably be a better option.
You can include the result of ARecord methods in the data shipped to your AResource model:
class MyRecord < ActiveRecord::Base
...
def to_xml(options={})
super(options.merge(:methods => [ :your_method_name_here ]))
end
...
end
You'll need to re-create any other methods in the AResource model that you want to use on the client. (I don't know how a puts belongs in an ARecord model, but do whatever floats your boat.)

Use find to initialize a constant?

Something like this:
class Category
SOME_CATEGORY = find_by_name("some category")
end
Category::SOME_CATEGORY
tried without a problem, but want to know if it is a bad idea, and the reasons if any..
thanks
If you don't want to hit the database each time you'll have to cache the model. There are several ways to do this, but one quick way is using Memoization. This was introduced in Rails 2.2.
class Category < ActiveRecord::Base
class << self
extend ActiveSupport::Memoizable
def named(name)
find_by_name(name)
end
memoize :named
end
end
Use it like this.
Category.named("some category") # hits the database
Category.named("some category") # doesn't hit the database
The cache should stay persistent across requests. You can reset the cache by passing true as the last parameter.
Category.named("some category", true) # force hitting the database
What do you want to do?
Maybe:
class Category
def self.some_category
Category.find_by_name("some category")
end
end
So you can call:
Category.some_category
=> <Category#2....>
It's not a terrible idea, but it's not really a good one either. It doesn't really fall in line with the way Rails does things. For one thing, you'll end up with a lot of ugly constant code. Too many ALL_CAPS_WORDS and your Ruby starts to look like C++. Bleah.
For another, it's inflexible. Are you going to make one of these constants for every category? If you add a new category two months from now, will you remember to update your Rails code, add a new constant, redeploy it and restart your server?
If it's important to you to be able to access categories very easily, and not repeat DB queries, here's a bit of metaprogramming that'll automatically look them up and create static methods like Lichtamberg's for you on first access:
def self.method_missing(category, *args) # The 'self' makes this a class method
#categories ||= {}
if (#categories[category] = find_by_name(category.to_s))
class_eval "def self.#{category.to_s}; #categories[#{category}]; end"
return #categories[category]
end
super
end
With this method in place, whenever you first call Category.ham, it'll create a class method that returns the value of find_by_name("ham") -- so that neither the query nor method_missing() runs again the next time you call it. This is pretty much the way the OpenStruct class works, BTW; look it up in the Pickaxe book if you want to learn more.
(Of course you'll still have the risk that, because these are all memoized, your Rails app won't reflect any changes you make to your category objects. This makes the assumption that changes won't happen or don't really matter. It's up to you to determine whether that assumption is valid for your app. You could always put an after_update callback in your code that resets ##categories if that's a problem; but at that point this starts to get complicated.)

Resources