How to split a module over multiple files in Rails? - ruby-on-rails

In my Rails app I have a module, which holds all the classes that make up an API wrapper. Here's the module's structure:
(file: zway.rb)
module Zway
class API
# instantiates API object
end
class Main
# holds top level functions
end
class Controller
# holds controller functions
end
class Error < StandardError
end
class BadRequestError < Error
end
end
Now that module is getting too big to keep it in one file so I want to split it up. As there are several classes in the module, I figured that every big class should be one file. So I didn't think a lot but tried to take out one class to see if it would work. Like so:
(file: zway.rb)
module Zway
class API
# instantiates API object
end
class Controller
# holds controller functions
end
class Error < StandardError
end
class BadRequestError < Error
end
end
(file: main.rb)
module Zway
class Main
end
end
But now I am getting this error which doesn't sound right to me as I do exactly what the error complains about: defining class Main in main.rb :
Unable to autoload constant Main, expected /bla/homer/app/models/main.rb to define it
I've searched on the net how to use modules but mostly what I found was about multiple inheritance and namespacing. But not about the practics of using modules to organize code.

If you put your classes in modules, as per convention you should define them within folders named with the module_name. That's the whole point of organising. You can do something like this and give it a go. First organize your code and file like this ->
models/
zway/
main.rb
zway.rb
And inside your main.rb you namespace it like this ->
class Zway::Main
...
end
and inside your zway.rb you define the module
module Zway
...
end
In this way, when you're trying to access the main class it, since its namespaced, it will look it up inside a folder zway by convention. And all your models are organized neatly within their respective folders.

Related

Module/Class clash trying to namespace models in Rails

As my app is getting a bit big now, I'm trying to use namespacing to help organise my models a little better.
I've created a app/models/theme.rb file which will act as a gateway to the rest of the theme related models which will go in a theme subdirectory.
# app/models directory
theme.rb
theme/compiler.rb
theme/instance.rb
theme/revision.rb
Where instance.rb will start something like....
class Theme::Instance < ActiveRecord::Base
end
and theme.rb is simply...
class Theme
def initialize(args)
# some stuff here
end
end
But anytime I create a new model using a generator, it tries to overwrite a new theme.rb as per below.
rails g model theme::revision
#new theme.rb
module Theme
def self.table_name_prefix
'theme_'
end
end
I could just get rid of the module and copy the def self.table_name_prefix method to each class but that doesn't seem very DRY. I would like to use the table prefix as it keeps things more obvious in the DB. Is there a 'correct' Rails way of going about this that I've missed?
I guess the easiest way is to define Theme as a module that all classes extends
The Theme::Instance imply that the Instance class is contained in a Theme module.
As an alternative, you can create a ThemeUtils module that contains all common method that is included in each class, like a plugin in /lib

Defining/nesting modules and classes in a Rails app

I'm trying to define a variety of modules/classes in a Rails app. My directory structure looks something like this:
lib/
fruit/ # just a module, with no associated file
fruit_operator.rb
apple.rb # abstract class, defines behavior for inheritance
orange.rb # abstract class, defines behavior for inheritance
apple/
granny_smith.rb # should inherit from apple.rb
red_delicious.rb
orange/
valencia.rb
seville.rb
I want two things:
The sub-classes should inherit from their parent classes (Apple and Orange).
I should be able to access these classes from a top-level (within /fruit file -- ie fruit_operator.rb
All the attempts I've tried to get this working are throwing an error of some sort or another.
Attempt # 1:
apple.rb
module Fruit
class Apple
def juicy
true
end
end
end
apple/granny_smith.rb
module Fruit
class GrannySmith::Apple
end
end
When I try to access GrannySmith from fruit_operator.rb I run into errors. Accessing as simply GrannySmith generates
uninitialized constant Fruit::FruitOperator::GrannySmith
If I try Fruit::GrannySmith, I get
uninitialized constant Fruit::GrannySmith
If I try Apple::GrannySmith or Fruit::Apple::GrannySmith, I hit the error
Unable to autoload constant Fruit::Apple::GrannySmith, expected /lib/fruit/apple/granny_smith.rb to define it
Attempt #2:
apple.rb
class Fruit::Apple
def juicy
true
end
end
apple/granny_smith.rb
class GrannySmith < Fruit::Apple
end
Attempting to access from fruit_operator.rb, I run into identical errors as the above.
Attempt #3:
apple.rb
class Fruit::Apple
def juicy
true
end
end
apple/granny_smith.rb
class Fruit::Apple::GrannySmith
end
This last version allows me to access the class directly from fruit_operator.rb (as Apple::GrannySmith), but it doesn't inherit from Apple!
Any idea how to structure/access these classes and modules? I've looked around quite a bit (on SO and elsewhere), and can't find a great guide for how to do this, particularly in a Rails app.
You must import the definition of the fruit files into the fruit operator file. For example,
require_relative './apple/granny_smith'
I think you're best solution is to implement Fruit as a class, and have Apple and Orange both inherit from Fruit, and GrannySmith inherit from Apple, like so:
Class Fruit
def seeds?
true
end
end
Class Apple < Fruit
def juicy
true
end
end
class GrannySmith < Apple
def color
"green"
end
end
Depending on what your need for the fruit_operator is, you may choose to include those methods/actions via a mixin Module.

application helper method invoking in another helper

I am stuck in a weird Design problem,
I'm facing issues when I'm trying to invoke a application helper method in another helper which have multiple class and want to access application helper method.
what is the better approach?
Thoughts about what can I do?
my code looks similar like this,
## app/helpers/application_helper.rb
module ApplicationHelper
def my_name
"stackoverflow"
end
end
Another helper with sub classes
## app/helpers/my_helper.rb
module MyHelper
class MyTestclass
def want_myname
my_name ## giving undefined method-- how can i call myname method here?
end
end
end
undefined method `myname' for MyHelper::MyTestclass
Here is a brief explanation of your problem and how to use modules:
To 1. A module is created/opened by simply saying:
module MyModule
def first_module_method
end
end
To 2. The lib folder. If you want to organize your modules in the lib folder, you can put them into modules themselves. For example, if you wanted a subfolder super_modules your modules would be defined as follows:
module SuperModules
module MyModule
def first_module_method
end
end
end
To 3./5. When including the module in a class you can simply call the modules methods as if they were defined within the class:
class MyClass
include MyModule
def some_method
first_module_method #calls module method
end
end
To 4. Frst, make sure that your module is really needed in every class of your application. If it isn't it makes sense to only include it where it is need so as not to bloat the classes that don't need it anyways. If you really want the module everywhere, include look at the class hierarchy of your classes in the app. Do you want the module in all models? You could open ActiveRecord::Base and add add your module there.

Display a Variable from a Method in a File in the /lib/ directory on a View in Rails

In a file called foo.rb in my /lib/ directory it reads:
module Foo
def some_method
#text_1 = "Hello!"
end
end
How can I get the results of this method to show up in a View?
I've seen that I need to include the following line in the /config/application.rb file:
config.autoload_paths += %W(#{config.root}/lib
However, I do not completely understand how to pass a variable from a module in a file saved in the /lib/ directory - to show up in a View. I appreciate any advice.
In order to get that value to show up in the view, you'll need to understand how modules are used in Ruby. Typically modules are mixed into other classes either by including or extending them. This would then make that method available to another class which could then be referenced in the view. In your case you might want to include it so it becomes available to instances of whatever class you put it in. Say you have an ActiveRecord model called MyClass and you include Foo. You can then call my_method on instances of that model as demonstrated below:
class MyClass < ActiveRecord::Base
include Foo
end
In your controller:
class MyController
def new
#my_class = MyClass.new
end
end
In your view:
#my_class.some_method
Having said all that, it seems like there might be a better way to do whatever it is you're trying to do :)
Yes.I agree with
Beerlington.
You can do it in an other way,
It is not mandatory to add config.autoload_paths += %W(#{config.root}/lib to application file.Because by default the files which are located in /lib directory won't be executed at first when we run an application using rails s.
In order to make those files to be loaded,we need to include that line in application.rb.
Otherwise,we can directly write it as below,
In model,
require 'Filename'
class MyClass < ActiveRecord::Base
include Foo
end
In controller,
require 'foobar'
class BuyerController < ApplicationController
include Foobar
end
In foobar.rb,
module Foobar
def Foobar.foobar
"Hello world!"
end
end
In view,
<%= Foobar.foobar %> (You can directly call the method by using Modulenmae.Methodname)

inject/access methods from a parent module inside a model

In Rails I have the following structure
#.../models/A.rb
module A
def m
end
end
#.../models/a/B.rb
class A::B < ActiveRecord::Base
end
This automatically places A as a parent of B. Is there a way to do something like B.m without modifying B? I know that I could do something like B.parent.m and from there create aliases, but then i would have to change B.
I'm looking to somehow inject a code present in A into B, but I don't know where this automatic association is done behind the scenes.
Something like
module A
module C
def mc
end
end
def placed_as_parent (child) # supposing this is the method called to put this module as a parent
super child
child.include(C) #this is what I would like to do
end
end
The question behind it is that I have a module which is already being shared among several models of that folder and I would like to put some common stuff for the models in there without have to manually include/extend a module in each of my models
[[EDITED]]
I'm not being clear with my question. In rails 3 if you do
rails generate active_record:model A::B
it will generate the files
#.../models/A.rb
module A
def self.table_name_prefix
'a_'
end
end
#.../models/a/B.rb
class A::B < ActiveRecord::Base
end
So if I open a console and type
A::B.table_name # -> 'a_b'
A::B.table_name_prefix # -> ''
A::B.parent # -> A
A.table_name_prefix # 'a_'
This happens automatically without any include/extend in the model B. What I want is to include more stuff in A and access it from B, without changing anything on B as i described earlier.
To be honest I'm not sure I fully understand your question but I'll give it a shot anyway.
There is a hook in the Module class that allows you to get a reference to the class the module is being included into. Thus, you could then do virtually anything with it.
An example:
module A
# you can change the class' behavior here
def self.included(klass)
puts "included in #{klass}"
end
end
And then to use it:
class B
include A #this causes the included hook in the module to be called
end
Is this what you're after?
The OP wrote:
The question behind it is that I have a module which is already being shared among several models of that folder and I would like to put some common stuff for the models in there without have to manually include/extend a module in each of my models
Here's what I would do:
module Stuff1
...
end
module Stuff2
...
end
module StuffIWantInSeveralModels
include Stuff1, Stuff2
end
class X < ActiveRecord::Base
include StuffIWantInSeveralModels
end
class Y < ActiveRecord::Base
include StuffIWantInSeveralModels
end
Then when you want to add a new module to several of your models, you only have to write an "include" statement in one place (in the StuffIWantInSeveralModels module).
Each module should be in its own file in the lib directory, with the file name matching the name of the module so Rails auto-loading will work (e.g. stuff_i_want_in_several_models.rb).
Does this achieve what you wanted?

Resources