Rails: Ruby Code That Can Scan Ruby Code - ruby-on-rails

I'm starting a project with several contributors. We want to keep track of who wrote what code, as well as to get a count of how many methods, controller actions, and views a contributor has written. This necessitates a level of meta-programming that none of us are familiar with.
So far, the best idea we've come up with is to add a comment before each snippet of code with the contributor's username and a short, consistent phrase. For instance:
# method by RacerX
def a_useful_method
. . .
end
# method by MysteryProgrammer123
def another_useful_method
. . .
end
# action by MysteryProgrammer123
def new
. . .
end
Then we'd run a method to count all instances of action by and method by and view by that each user has written throughout the project. Unfortunately, we don't know how to write Ruby code that can inspect other Ruby code. It might not even be possible. If it is possible, though, how is it done?
Alternatively, there might be a better approach that we're not considering.

You should prefer your source control system to track who wrote what. git blame, for instance, can produce an annotated listing showing author and source line.
Identifying views should be easy, they're in the view directory. Static method definitions can generally be found with regexp /\bdef\s+(?:\w+\.)?(\w+)\b/. Distinguishing "actions" from other methods probably involves filtering method names against common action names and other names discovered by examining your routes.

Rather than reinventing the wheel, use a ready-made tool. And if it does not come to your mind how to implement such code, then you probably will not be able to write such code. A documentation tool such as YARD may be useful. The way this works is that you add explanations as comments before method definitions. Usually this is for writing the documentation to be read by the users, but you can depart from its intended use and write things like the programmers name, or whatever other information you like.

Related

How to find the module/class where a method comes from in Rails?

Using Rails, I was originally looking for a way to use the resource path helper methods (e.g. users_path) in models, and this gives me the answer: I can use Rails.application.routes.url_helpers.users_path .
Good, but in general and specifically in Rails, how can one find what module/class a method comes from? Are reading the source code or some official docs the only way to find it? Isn't it possible to, for example, directly check in the debug console to get Rails.application.routes.url_helpers from users_path ?
I think what you are looking for is similar to this question How to find where a method is defined at runtime?.
In short, in Ruby 2+, (either of) the following would return an 2-element Array of the filename and line-number:
method(:users_path).source_location
my_object.method(:my_method).source_location
# => ["/My/Ruby/Code/some-module.rb", 123]
You can then work out the Module or Class name for the method.
[Added in EDIT]
To get the module name, you can use method(:users_path).owner instead: cf., Answer1 and answer2.
However, Rails uses a lot of meta programming and dynamic assignments. The url-helper like users_path in the question is one of them.
Quote of #spickermann's comment to the question
users_path is defined by meta programming and therefore you will not find a method definition with that method name at all in the code.
Quote of #Stefan's comment to the question
there might multiple such objects. You have to identify the one "closest" to the current self.
Specifically, url_helpers are defined in actionpack-6.*/lib/action_dispatch/routing/route_set.rb for Rails 6.0 and you see the method generate_url_helpers is responsible to (dynamically) generate it. That is why method(:users_path).owner returns something like #<Module:0x0000000117346e18>, which is not really helpful. Even method(:users_path).source_location simply points to the line of
mod.define_method(name) do |*args|
(or something like that, depending on the Rails version), which indicates that the object is dynamically generated.
In consequence, I doubt if there is any easy way to do-all to derive a Rails object (class/module), say, Rails.application.routes.url_helpers from the given helper function like users_path. But an analysis using a combination of methods like owner and source_location would be of great help to pinpoint it and might, in some cases, give you an answer you are looking for.
I note that, as far as this specific case is concerned, the error message written in actionpack-6*.*/lib/abstract_controller/url_for.rb would be of help – quote:
In order to use #url_for, you must include routing helpers explicitly.
For instance, include Rails.application.routes.url_helpers.

Rails proper way to distinguish methods from variable names?

Now that my controllers are expanding beyond the basic REST actions it can get somewhat confusing as to whether a given expression is either a built in method, a controller method, from the model, or a variable.
Would adding empty parentheses to my controller methods when I call them from other methods cause any problems down the road? Like so:
if customer.save?
subscription_checker()
end
I'm at least trying to always use an underscore in the names of the methods I create in order to make them look different from most of the built in methods.
Would adding empty parentheses to my controller methods when I call them from other methods cause any problems down the road?
This is a valid way to distinguish between variables vs methods in ruby, but "You're Doing It Wrong" ™
Part of the ruby paradigm is that you shouldn't typically care whether the thing you're referencing is a variable or a method. This makes the code easier to read and refactor; keeping the developer's attention focused on code's intent rather than its implementation.
This design pattern is often referred to as "bare words", and I highly recommend the following RubyTapas episode on the subject: http://www.virtuouscode.com/2012/10/01/barewords/
I would also recommend that you have a quick read through a ruby style guide, to see what common conventions are considered "good" or "bad" by the community. (Ruby code styles are extremely well conformed to by the community!) For example, from this guide:
Only omit parentheses for
Method calls with no arguments:
# bad
Kernel.exit!()
2.even?()
fork()
'test'.upcase()
# good
Kernel.exit!
2.even?
fork
'test'.upcase
In your particular code above, although it's hard to say for sure without knowing more context, I suspect that subscription_checker should actually be abstracted into a separate class rather than yet-another-method in the controller. But as I say, in order to give any concrete advice here I'd need to see more of the source file this is taken from.

Best practices for coding in controller and models?

I have this short and simple code for sending an email notification to the user when someone comments on his post. What I'm concerned about is the location of this snippet.
if user.settings.enabled_notifications && some_other_conditions
NotificationMailer.notify_topic_owner(comment,owner)
end
notify_topic_owner() just shoots a mail according to the parameters passed to it.
Basically, some_other_conditions contain some 3-4 conditions to be evaluated to true so as to send a mail. So clearly a controller isn't the right place for this code (I read somewhere that a controller code should be light and clean).
I dont think i can move this snippet to a helper as helpers contain code for views. Again, models dont look right either as the code is not really about the model (or is it?).
Do I make a new module for this short snippet? Going forward, I would really appreciate if you could also tell about the best practices or some reference for such dull confusions. I find myself struggling with this quite often!
You are asking the right questions. Why not go one step further and attempt to do some OOP :
(the code below is not ideal, but it should give you a good idea of how to approach it). I have not taken "some_other_conditions" into consideration because those are likely something you know best where it will fit into your domain logic.
# A class for notification. I usually avoid depending directly on xxxMailer and similar
class Notifier
# Inject the recipient
def initialize(recipient)
#recipient = recipient
end
def topic_commented(comment)
# Only let Notifier know that NotificationMailer exists. (not perfect OOP. could inject this too)
NotificationMailer.notify_topic_owner(comment,#recipient) if #recipient.notifications_enabled? # Ideally should be telling, not asking. Oh well.
end
end
class User
# Sprinkling of Law of Demeter
def notifications_enabled?
settings.enabled_notifications
end
end
You call Notifier.new(current_user).topic_commented("Hello World"). In future, the topic_commented can send SMS, smoke signals, print, write to database etc. all without you having to change the calling code lke NotificationMailer.xxxx in many places.
I don't see what would be wrong with putting this in a controller. If it's related to a method in your controller, it can definitely go there. If it's called after a save or something, you can probably move it to the model.
Generally I think the best practice is try to put as much stuff into models and classes as possible. Save the controller for controller specific code, and helpers should only contain code related to rendering content in views. A lot of times, I'll take code in my controller and move it to the model while refactoring. My opinion anyway :)
The convention I use to think about it is: "Should the mail be sent every time a comment is added, no matter by what action?". Think about whether if, in the future, you implemented an automated system that added comments, the mail should be sent in that case. If so, it's probably model code; otherwise, it's related to the way in which the comment was added, and it's controller code.

Specifying and Executing Rules in Ruby

I am looking for a Ruby/Rails tool that will help me accomplish the following:
I would like to store the following string, and ones similar to it, in my database. When an object is created, updated, deleted, etc., I want to run through all the strings, check to see if the CRUD event matches the conditions of the string, and if so, run the actions specified.
When a new ticket is created and it's category=6 then notify user 1234 via email
I am planning to create an interface that builds these strings, so it doesn't need to be a human-readable string. If a JSONish structure is better, or a tool has an existing language, that would be fantastic. I'm kinda thinking something along the lines of:
{
object_types: ['ticket'],
events: ['created', 'updated'],
conditions:'ticket.category=6',
actions: 'notify user',
parameters: {
user:1234,
type:'email'
}
}
So basically, I need the following:
Monitor CRUD events - It would be nice if the tool had a way to do this, but Ican use Rails' ModelObservers here if the tool doesn't natively provide it
Find all matching "rules" - This is my major unknown...
Execute the requested method/parameters - Ideally, this would be defined in my Ruby code as classes/methods
Are there any existing tools that I should investigate?
Edit:
Thanks for the responses so far guys! I really appreciate you pointing me down the right paths.
The use case here is that we have many different clients, with many different business rules. For the rules that apply to all clients, I can easily create those in code (using something like Ruleby), but for all of the client-specific ones, I'd like to store them in the database. Ideally, the rule could be written once, stored either in the code, or in the DB, and then run (using something Resque for performance).
At this point, it looks like I'm going to have to roll my own, so any thoughts as to the best way to do that, or any tools I should investigate, would be greatly appreciated.
Thanks again!
I don't think it would be a major thing to write something yourself to do this, I don't know of any gems which would do this (but it would be good if someone wrote one!)
I would tackle the project in the following way, the way I am thinking is that you don't want to do the rule matching at the point the user saves as it may take a while and could interrupt the user experience and/or slow up the server, so...
Use observers to store a record each time a CRUD event happens, or to make things simpler use the Acts as Audited gem which does this for you.
1.5. Use a rake task, running from your crontab to run through the latest changes, perhaps every minute, or you could use Resque which does a good job of handling lots of jobs
Create a set of tables which define the possible rules a user could select from, perhaps something like
Table: Rule
Name
ForEvent (eg. CRUD)
TableInQuestion
FieldOneName
FieldOneCondition etc.
MethodToExecute
You can use a bit of metaprogramming to execute your method and since your method knows your table name and record id then this can be picked up.
Additional Notes
The best way to get going with this is to start simple then work upwards. To get the simple version working first I'd do the following ...
Install acts as audited
Add an additional field to the created audit table, :when_processed
Create yourself a module in your /lib folder called something like processrules which roughly does this
3.1 Grabs all unprocessed audit entries
3.2 Marks them as processed (perhaps make another small audit table at this point to record events happening)
Now create a rules table which simply has a name and condition statement, perhaps add a few sample ones to get going
Name: First | Rule Statement: 'SELECT 1 WHERE table.value = something'
Adapt your new processrules method to execute that sql for each changed entry (perhaps you want to restrict it to just the tables you are working with)
If the rule matched, add it to your log file.
From here you can extrapolate out the additional functionality you need and perhaps ask another question about the metaprogramaming side of dynamically calling methods as this question is quite broad, am more than happy to help further.
I tend to think the best way to go about task processing is to setup the process nicely first so it will work with any server load and situation then plug in the custom bits.
You could make this abstract enough so that you can specify arbitrary conditions and rules, but then you'd be developing a framework/engine as opposed to solving the specific problems of your app.
There's a good chance that using ActiveRecord::Observer will solve your needs, since you can hardcode all the different types of conditions you expect, and then only put the unknowns in the database. For example, say you know that you'll have people watching categories, then create an association like category_watchers, and use the following Observer:
class TicketObserver < ActiveRecord::Observer
# observe :ticket # not needed here, since it's inferred by the class name
def after_create(ticket)
ticket.category.watchers.each{ |user| notify_user(ticket, user) }
end
# def after_update ... (similar)
private
def notify_user(ticket, user)
# lookup the user's stored email preferences
# send an email if appropriate
end
end
If you want to store the email preference along with the fact that the user is watching the category, then use a join model with a flag indicating that.
If you then want to abstract it a step further, I'd suggest using something like treetop to generate the observers themselves, but I'm not convinced that this adds more value than abstracting similar observers in code.
There's a Ruby & Rules Engines SO post that might have some info that you might find useful. There's another Ruby-based rules engine that you may want to explore that as well - Ruleby.
Hope that this helps you start your investigation.

Rails: Best practice for model dependency class locations?

I have a rails app moving along fairly well, but the fact that I'm doing this myself means that some poor sod is eventually going to see this and say, "What the hell were you thinking? Why did you put this here?!?!"
Where is that poor, sorry soul going to expect to see a series of classes that aren't used by anything but a single model class? Obviously, I could chuck it in the_model.rb along with class TheModel, but this may expand beyond the planned two classes...
I thought about lib, but it doesn't need to clutter everyone's view of the world....
Thank you.
My predecessor thanks you.
Leave them in the_model.rb until you need them in more than one place. If you refactor needlessly, you're not doing the simplest thing that could possibly work. You aren't gonna need it.
At that point, the general pattern is to create a directory for "concerns". See this weblog post by Jamis Buck or this one by Peter Marklund for more information.
In general: follow the Rails naming conventions when translating class names into filesystem locations. (that is: keep the class FooHelper::Bar in foo_helper/bar.rb)
You can make exceptions for small helper classes that are only used once and keep them in the same file as your model, but those should be exceptions. (but the converse is also true, don't create one-line thousands of single line files)
Use modules and class namespaces to your advantage. If you have a helper class that is only used by (and dependent on) your model, put them into the namespace of the model class:
class TheModel::HelperClass
end
the location in the file system would be app/models/the_model/helper_class.rb
And something that is not dependent on your model can probably still be namespaced
module Bar
class Foo
end
end
living in bar/foo.rb, of course
You should probably not be afraid to put things that are not models into lib -- that's what this directory is for
I'd say concerns, while useful, are not really the right way to go because that is a way to split a single class into multiple files and you don't seem to be doing that.

Resources