I've found that if I create a module, I have to create a "wrapper class" for it to work in certain circumstances (actually Resque-pool in my case, but I think it's relevant elsewhere too, even though it worked in regular Unicorn without needing it). By wrapper class, I mean if I create:
models/
posts/
selfie.rb
I will get a "Expected selfie.rb to define Posts::Selfie" unless I also create models/posts.rb containing "require posts/selfie.rb". (Based on https://stackoverflow.com/a/11001022/18706)
My question is, should this be done for every module under models, controllers, etc? And is there a name for this kind of class, or any reference info about it?
Related
i am trying to create a plugin for Discourse, which is written in Ruby. As normal blank files my program is working perfect and without errors, but when i try to adapt my code into the plugin context i run into issues and i am not sure if i really understand how the whole idea with functions is meant to be.
I thought it would be smart to have more than just one file, to outsource functionality in different methods and require them in a kind of "main" file. For example getting tweets is one method in an extra file, sending tweets a different method in another file. In blank ruby code its working fine but when i try to integrate that into the plugin file structure i get the error
undefined method `my_method' for #<Plugin::Instance:0x00007f9004012fc0> (NoMethodError)
the files with the methods are in a lib directory and the "main" file which is called the plugin.rb is in the mainfolder
so i tried
require_relative 'lib/my_method'
and the other way
require_relative File.expand_path('../lib/my_method.rb', __FILE__)
but i still run into that error.
i have not defined any kind of classes or modules or something like that so the "method files" are literally starting with
def self.my_method
#my code here
end
Could that be the reason why i run into the error above? Why is it working as blank ruby code, but not when i try to run the plugin with rails s on my discourse instance?
I am still pretty new into ruby programming, so maybe my question seems a bit silly.
Here is the link which lead me threw the plugin creation:
https://meta.discourse.org/t/beginners-guide-to-creating-discourse-plugins-part-1/30515
Unfortunately, your understanding of methods is shallow. Basically, any method you declare in the global scope is added to the Object class as private method, so it is accessible everywhere in your objects cause they derive from Object class and in global scope because it is the scope of the Object class. If you declare method as self.method, you make it a method of main Object, because self refers to main, which is not the desired behaviour for you. To fix that issue, you should just remove self and write it like that:
def my_method
end
This way this method will be added to the Object class itself, not the main Object. There is a link on the article about methods in general. And another one on the toplevel scope behaviour. In this codepen you may observe the difference. Also, it may be useful for you to learn some Ruby before going on with your development. I suggest rubymonk. Another issue is your one-method files which is not the best practice for ruby code organization. Ruby is truly object-oriented language and if you need to have a bunch or even one general-purpose method, it is better to put it in a module or class to define its purpose and role in application, make it reusable and trackable, without global scope pollution.
If i need to add (project specific) classes to my controler in rails, what is the correct way/place to put and "include" them/there .rb files? (quotes for: not the ruby keyword include)
I am new to rails, and did not find the correct way. LIB sounds like for more public libraries and - what I have learned - is not reloaded per default in dev mode.
sure, I could put all in controler.rb, but ...
the anser for me:
First: there are no rules, if you keep in mind (or learn like me) the rails rules:
NameOfCla -> name_of_cla(.rb) <-- not using class as word for clearence
name your class how you like:
class ExtendCon #<--- not using controller here for clearence
....
put it in a file extend_con.rb, wait for the path explaination, please. if you named your class 'MYGreatThing' it will be 'm_y_great_thing' (never testet that), so avoid chineese charachters
if your controller uses
#letssee=ExtendCon.new
rails learns that class and file (extend_con) on its own. i still did not figure out if a server restart is needed. (the first time)
choose the path to put the file: (I preferre Daves way) app/myexten or what you like, making it 'app' specific and still distquishes to standard rails 'things'
if you are not lasy like me (i put it in app/ontrollers)
put the path you have choosen into
config/application.rb like (comments are there to find it)
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/app/controllers)
config.autoload_paths += %W(#{config.root}/app/myexten)
this one workes for me in all modes including "developer" and i did not need to put "my own" things in app/lib
It depends.
I tend to put library code used explicitly (e.g., instantiated, injected, etc. into app-level artifacts) into app/xxx where xxx signifies the "type" of thing, like decorators, services, etc.
Magic stuff tends to end up in lib, like monkey patches, architectural-level artifacts, and so on.
Code anywhere can be added to the autoload paths, required automatically by an initializer, etc.
Rails 4 comes with an internal directory for controllers called concerns. You could try using that.
app/controlls/concerns
If you have concerns/foo_bar.rb, you include it as follows:
class FooController < ApplicationController
include FooBar
end
Models also have their own concerns directory. I find this approach useful, and it can be applied to Rails 3. You just have to add the directories to your load paths.
I'm working with some legacy RoR code, which has four related classes, each defined in its own file. All of these classes are parser classes, and live in app/models/parsers. Each file name ends with _parser.rb.
Here's an example class def line from file adf_parser.rb:
class Parsers::AdfParser
I'm lost as to what the Parsers:: part of that is doing.
There's no explicit module called Parsers defined anywhere that I can find.
I don't see any documentation about implicitly creating modules just by adding module specifications to class names.
The only external dependency is "require 'csv'".
There are include statements within the class def, but I don't think they have anything that would explain the class name.
I created a new RoR test project and put stubs of these files in a parallel directory, and they won't run from the command line due to a
name error.
I don't see any examples online of classes named in this way.
I'm sure this isn't rocket surgery, but I've lost most of my morning trying to figure this out, and I'd love it if someone could just tell me what's going on with it.
Update: It sounds like this is just a bit of Rails magic, based on the subdirectory name. I think the reason that I got an error in my test app is that I just ran the files through the ruby interpreter, rather than invoking them with Rails in some way.
class Parsers::AdfParser is in practice equivalent to:
module Parsers
class AdfParser
For this to work properly, and the file to be autoloaded its location should be parsers/adf_parser.rb, whether under app/models or lib. Basically the file path needs to mimic the class hierarchy.
It's in the parsers sub-directory of modules; Rails namespaces for you by convention.
I'm working on a rails app that is starting to have what seems (to me) to be a lot of models. There are 15 right now, but I'm thinking about adding 3-4 more to serve as "tag" like models (I need more functionality than Acts As Taggable offers).
So, the reason this bugs me a bit, is that 7 of the 15 models belong to a common parent. Several are belong_to, and a few are has_and_belongs_to_many. All the new models I'm contemplating would belong_to the same parent as well.
So, what I'm wondering is, what is the best "Railsy" way of organizing this kind of situation?
Instead of app/models being super crowded with 6 "first-class" models and 10+ children of one of these, should/can I start using sub folders in my app folder? ie: app/models/parent/child.rb?
I know this is kind of an open-ended question, but I would really appreciate advice as to the best way to handle a rails project with a proliferation of models.
Thanks!
You can do this, I always do :)
Just beware of something: if you create a folder which has the name of one your models, it will fail. Actually, Rails will think you want to extend it.
So in your model folder, prepend the name of your class with whatever fancy you want.
Example: if you want to put models related to users, put them in models/user_related/
You'll have to add this to your application.rb file:
config.autoload_paths += Dir["#{Rails.root.to_s}/app/models/*"].find_all { |f| File.stat(f).directory? }
This will autoload all folders included in modelsdirectory.
I think apneadiving's answer is good approach
Based on research with activesupport 3.0.11, there are some rules to follow when choosing a directory name however:
The name must never match a constant in your system, or LoadError's could occur
The name must be able to be converted to a valid constant name, or NameError's will occur.
Explanation of problem #1
Apneadiving's example of a directory name app/models/user_related works as long as a
constant UserRelated is never used in your code. Otherwise a LoadError could
potentially happen.
For example, assume there was a model called UserProfile and the first time
rails sees the constant is in the UserRelated module. Rails will first try to
load a UserRelated\:\:UserProfile constant and failing that a UserProfile
constant.
If the user_profile file is at app/models/user_related/user_profile.rb, this
matches the underscored path of UserRelated\:\:UserProfile and the file would
be loaded expecting to define the UserRelated::UserProfile constant. This
would raise the following error because it really defines the UserProfile
constant.
Expected app/models/user_related/user_profile.rb to define UserRelated::UserProfile (LoadError)
This happens in the active support dependency code.
Explanation of problem #2
Another caveat is the directory name must be able turned into a valid ruby
constant name (although to follow #1 the constant should be undefined). For
example, if the directory name were app/models/user.related this would result
in the following error inside the active_support dependency code:
wrong constant name User.related (NameError)
Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.