I have a collection of 'data endpoints'. Each endpoint has a name and can be available or unavailable. In Ruby I want to present the available endpoints as a Hash to make it easy to work with them. The difficulty is that getting information about the endpoints is costly and should be done lazily.
Some examples of how I want my object to behave:
endpoints = get_endpoints.call # No endpoint information is accessed yet
result = endpoints['name1'] # This should only query endpoint "name1"
is_available = endpoints.key? 'name2' # This should only query endpoint "name2"
all_available = endpoints.keys # This has to query all endpoints
The comments describe how the object internally makes requests to the 'data endpoints'.
It is straightforward to make a Hash that can do the first 2 lines. However I don't know how to support the last 2 lines. To do this I need a way to make the keys lazy, not just the values.
Thank you for taking a look!
You'd have to override the key? method, and do your own checking in there.
class LazyHash < Hash
def key?(key)
# Do your checking here. However that looks for your application
end
end
In my opinion, you're asking for trouble though. One of the most powerful virtues in computer science is expectability. If you're changing the behavior of something, modifying it far beyond it's intent, it doesn't serve you to continue calling it by the original name. You don't need to shoe-horn your solution into existing classes/interfaces.
Programming offers you plenty of flexibility, so you can do stuff like this (dependent on the language of course), but in that same argument, you have no reason not to simply build a new object/service with it's own API.
I recommend starting fresh with a new class and building out your desired interface and functionality.
class LazyEndpoints
def on?(name)
end
def set(name, value)
end
end
(Or something like that, the world is yours for the taking!)
Related
I have a Rails model, which is using the str_enum gem.
I'm building a generator which reads the models and creates pages for them, and so I'd like to be able to understand what str_enums are attached to a model.
For example
class User < ApplicationRecord
str_enum :email_frequency, %i[every daily weekly], default: 'every'
end
Ideally, I'd like to be able to query the User model and understand there is a str_enum attached to email_frequency, with values of every, daily & weekly.
Once I can understand there is a str_enum attached to a given field, I can pluralize the field and get the values:
irb(main):004:0> User.email_frequencies
=> ["every", "daily", "weekly"]
The question has also be asked over here and the suggestion is to use Module#prepend. I'm familiar with prepend to conditionally insert methods into a model.
How can I use it for this problem?
EDIT
This is quite simple with validations, for example: get validations from model
If I understand your question correctly is that you wanna get all column that has attached with enum string. If so you can override the gem method like this
# lib/extenstions/str_enum.rb
module Extensions
module StrEnum
module ClassMethods
def str_enum(column, *args)
self.str_enums << column.to_s.pluralize
super
end
end
def self.prepended(base)
class << base
mattr_accessor :str_enums
self.str_enums = []
prepend ClassMethods
end
end
end
end
In the User model
prepend Extensions::StrEnum
Now you can use
User.str_enums
to list all columns has attached with str enum.
Make sure you have add lib directory into load path.
So for starters, you could, of course, use the approach that Ninh Le has described and monkeypatch your desired behavior into the gem. In fact, I'm fairly confident that it would work, since your use case is currently relatively easy and you really just need to keep track of all the times the str_enum method gets called.
I would, however, encourage you to consider doing one of two things:
If you plan to do more complex stuff with your enums, consider using one of the more heavy-duty enum gems like enumerize, enumerate_it or active_enum. All of these are packages that have been around for a decade (give or take) and still receive support and all of them have been built with a certain degree of extensibility and introspection in mind (albeit with different approaches).
Have a look at the gem and consider building your own little macro on top of it. IMO one of multiple of Andrew Kane's libraries' biggest weaknesses is arguably their kind of hacky/scripty approach which, while making the libraries hard to extend, makes them inherently easy to understand and thus use as a basis for your own stuff (whereas the gems with a better/more elaborate approach are harder to understand and adapt beyond the means the author has intended to).
Either way, you'll be fine with both of my suggestions as well as Ninh Le's.
I'm looking for a function that would do the following:
def self.my_find
object = self.first #Whatever
return object.my_check? ? object : nil
end
Something like check:
object.check(&:my_check?)
It should exist, shouldn't it?
Here's my situation:
In a method (controller), I have some nil-able objects and I need to check something on the object to further use it.
In the event of trying to write pseudo-functional code, the logic is first to retrieve the objects, then making actions on the objects and then return those.
If I have a collection, the code would be:
result = get_my_collection_method.select(&:my_check?).map(&:my_action)
There is no issue if the select methods filters all objects, then result will equal [] but the code is still valid.
I find natural to wanting to do the same even if it is not a collection but a single object:
result = get_my_object.check(&:my_check?).try(:my_action)
But the fact that this method doesn't exist tells me there is no monadic transformation between object and array in ruby.
I guess the simplest way to achieve this is that I transform my singleton into a single value array:
result = Array(get_my_object).select(&:my_check?).map(&:my_action).first
I hope this clarifies why I was looking for such method.
But the question remains: does it exist natively? If I write it myself (even at the Object level), this is not standard, I lose the benefits of writing clean code.
While a method like that might seem useful in your particular circumstance, this sort of thing is generally expressed like:
object = self.first
object.my_check? and object
Where it's understood that this method may return false but that's not a logically true value so it's fine.
If you want to write your own method for Object that does this, you're welcome to, but keep in mind the more esoteric and quirky your code is the harder it will be for other people to engage with. Those "other people" might be you in the future when you've forgotten about your unusual core extensions.
Here's a few ways:
def self.my_find
object = self.first # Whatever
# Use ONE of the following examples:
# 1.
object if object.my_check?
# 2.
object.my_check? && object
# 3.
object.my_check? ? object : nil
end
Note that an explicit return is not needed at the end of a method in ruby.
Depending on the context though (I can only speculate, as none was given!), there might be a cleaner way to do this. For example, if this is actually something you're building in a rails application, then one of these examples would be better:
def self.my_find
where(...).find_by(my_check: true)
# Or, where `my_check` is a *SCOPE*!!
my_check.find_by(...)
end
...With the key difference being that you're performing the check in SQL rather than in ruby. This gives better performance.
Is it better to use case/when things or the send method when dynamically calling methods based on user input? "better" based primarily on good coding practices.
input = gets.chomp
case input
when foo
foo
when bar
bar
end
versus
input = gets.chomp #Where hopefully the input would be 'foo' or 'bar'
send(input)
Your wording makes the question incredibly hard to read.
If I understood you correctly, you want to call methods based on user input. One alternative would be to check every possible value and call a method, the other - to use send directly.
First of all, notice that in your first example, you were calling method1 when the user entered foo. If you used send(input) you would have called foo instead. So they are not exactly the same.
You can achieve the same behavior by putting the input->method mapping in a hash like so:
dispatch = {foo: :method1, bar: :method2}
input = gets.chomp.to_sym
send(dispatch[input])
Another thing to note is that send in the original situation would call any method passed. You can instead whitelist the possible methods with the hash above and checking if such value exists:
send(dispatch[input]) if dispatch.key? input
Now to the question of when to use one or the other:
If you have 2, 3, 5 or so possibilities, prefer explicitly listing them. It will be faster, easier to read, easier to do static code analysis and so on.
If you have hundreds and thousands of different methods, prefer send. The costs outweigh the benefits of being DRY.
If the list of allowed methods is generated dynamically, you don't have a choice - use send. Examples:
You want to call methods to a given object and that object is different each time
You want to allow different methods depending on the user's permissions
You want to implement a REPL or some other awesome tool that has extremely dynamic needs
In general, don't use meta programming, unless there is significant gain or you don't have any other choice.
Unless you'd like your user to be able to call any method in the method lookup chain, including private methods which send can invoke, it probably makes sense for you to lock it down and only allow your users some methods.
If you don't specify an object to send to (like in your code above), Ruby will look at self for a method by that name and then use a normal method lookup. In other words self will be the first link in the method lookup chain. If you do specify an object, maybe an object that you create for that purpose for example, another option might be to use the methods like try or respond_to?.
input = gets.chomp
if defined?(input.to_sym)
send(input)
else
puts "No such thing!"
I'm looking for the best way to structure a class (or set of classes) that coordinate an ordered set of API calls or steps and then persist mixed data from the results of those API calls and steps. Ideally there would be rollback handling for failure scenarios to cleanup any created API data, in the case where persisted data fails or doesn't pass certain validations. I have created a similar structure with some Ruby pseudo code below, however this just doesn't feel right.
Any help with a better way to structure this would be greatly appreciated!
class SomeImportantAction
def initialize(obj)
#obj = obj
end
def run!
result = API.get(...)
result_2 = OtherAPI.post(...)
some_var = do_some_work()
update_obj(result, some_var)
create_something_new(result,result_2)
end
private
def update_obj(result,somevar)
...
end
def create_something_new(arg,arg)
...
end
end
Rule of thumb is to have one method working on one level of abstraction.
Your aim is to gather information (with API calls) and to save it to DB. This is top level:
def run!
result = make_api_calls(#obj)
another_result = do_some_work()
if results_valid?(result, another_result)
save_results(result, another_result)
else
cleanup(result, another_result)
end
end
def make_api_calls(#obj)
result = API.get(...)
result_2 = OtherAPI.post(...)
combine_results(result, result_2)
rescue
handle_api_fail()
end
Reading run! method you can clearly say what the task is doing. And then, if interested, you can inspect each method.
Each method that called in run! should then operate on lower level of abstraction - make API calls, save to DB. But again, only one level - don't dig into establishing DB connection or something like that.
Once you split work into smaller and task-specific methods, you can see groups of methods that only use each other (like API calls and handling related exceptions). That would be candidates for a new class.
For example, you need series of API calls to create a user profile - fetch auth info, fetch profile picture, call some API to add user's role, book something etc. That is a bunch of API calls that work as a transaction - all succeed or have no effect if one fails. This should be organized as a class - it is simpler to use (less API calls to keep in mind), to debug and to test.
Don't be afraid to create classes and methods. Even one-lined methods can help significantly.
Say I have an object with a method that accesses an object:
def foo
#foo
end
I know I can use send to access that method:
obj.send("foo") # Returns #foo
Is there a straightforward way to do a recursive send to get a parameter on the #foo object, like:
obj.send("foo.bar") # Returns #foo.bar
You can use instance_eval:
obj.instance_eval("foo.bar")
You can even access the instance variable directly:
obj.instance_eval("#foo.bar")
While OP has already accepted an answer using instance_eval(string), I would strongly urge OP to avoid string forms of eval unless absolutely necessary. Eval invokes the ruby compiler -- it's expensive to compute and dangerous to use as it opens a vector for code injection attacks.
As stated there's no need for send at all:
obj.foo.bar
If indeed the names of foo and bar are coming from some non-static calculation, then
obj.send(foo_method).send(bar_method)
is simple and all one needs for this.
If the methods are coming in the form of a dotted string, one can use split and inject to chain the methods:
'foo.bar'.split('.').inject(obj, :send)
Clarifying in response to comments: String eval is one of the riskiest things one can do from a security perspective. If there's any way the string is constructed from user supplied input without incredibly diligent inspection and validation of that input, you should just consider your system owned.
send(method) where method is obtained from user input has risks too, but there's a more limited attack vector. Your user input can cause you to execute any 0-arghument method dispatchable through the receiver. Good practise here would be to always whitelist the methods before dispatching:
VALID_USER_METHODS = %w{foo bar baz}
def safe_send(method)
raise ArgumentError, "#{method} not allowed" unless VALID_USER_METHODS.include?(method.to_s)
send(method)
end
A bit late to the party, but I had to do something similar that had to combine both 'sending' and accessing data from a hash/array in a single call. Basically this allows you to do something like the following
value = obj.send_nested("data.foo['bar'].id")
and under the hood this will do something akin to
obj.send(data).send(foo)['bar'].send(id)
This also works with symbols in the attribute string
value = obj.send_nested('data.foo[:bar][0].id')
which will do something akin to
obj.send(data).send(foo)[:bar][0].send(id)
In the event that you want to use indifferent access you can add that as a parameter as well. E.g.
value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)
Since it's a bit more involved, here is the link to the gist that you can use to add that method to the base Ruby Object. (It also includes the tests so that you can see how it works)