Where do I place my non-model classes? - ruby-on-rails

Where should I put my simple classes that are not models (they don't represent any data) and provides only some functionality?
Like for example:
class FileUploader
def save_uploaded_file(filename, tempfile)
path = Rails.root.join('public', 'uploads', filename)
File.open(path, 'wb+') do |file|
file.write(tempfile.read)
end
end
end
What this does is just simply copies file to predefined place.
Do I must to place this under models? Or do Rails have some other location more suitable for those kind of classes? I tried to put in lib before, but it was just too much of the pain as Rails cached those files inside lib directory (edited config to not cache files, but it still cached inside lib).
So, I placed those files in models but testing becomes very painful (I'm just starting Rails, maybe I'm doing something wrong with the tests) as rake spits out errors about not having set up a test database (I want to test classes that are not related to database in any form or shape - why do I need to set up a database?).

I've taken to adding app/lib to some of my projects to accommodate this, but there's also the new Rails 4 concerns locations like app/models/concerns or app/controllers/concerns depending on where this sort of thing is used.
You're right that it's best to avoid putting non-model classes in app/models.

For service-type objects I put them in app/services.
For model-type objects I put them in app/models. I do not think it is necessary to inherit from ActiveRecord to be considered a model.
I think your object classifies as a 'service object' since it is designed to wrap the service of uploading a file, it isn't really a domain object like a model would be.

Related

Rails 5 - Namespacing models

I am using namespacing in my Rails 5 app to try to keep resources organised.
I've been generating resources using the command line by adding the namespace folder to the generate command.
This makes a folder in the models folder for the main folder which the namespaced files are saved in.
I've since been reading posts from others that suggest namespacing models is not a good idea.
An example of what I currently have is:
class Stance::Assessment < ApplicationRecord
It seems to work alright so far.
What is the problem with namespacing models?
If it is a problem, does that mean I can't organise my models into folder groups, or does it mean that the model class doesnt need to be named wiht the "Stance::"?
There is a certain cost of complexity involved with "namespacing" your models. Ruby does not actually have true namespaces. Rather it has has modules which provide encapsulation.
Rails and ActiveRecord was designed around placing your application code in the Main object (the global object). While this might seem like a bad practice it is very simple and works well with the convention over configuration approach. It also allows a much simpler autoloading scheme and avoids the need to nest every single file in an additional folder.
Namespacing does have great organizational merits though and lets you avoid collisions. But there are a few minor aches in the backside:
table prefixes, having the generated table names like my_app_projects_tasks is really inconvenient when you need to write a custom join.
You need to override ActiveModel::Naming so that it does not look for paths like my_app_projects_tasks_path when using the polymorphic route helpers.
You need to explicitly set the class_name option when creating associations or override how ActiveRecord resolves constant names.
you can prefix your models, so instead of Stance::Assessment you would have StanceAssessment. not as clean as a namespace but it's pretty close to it

Rails model for storing only constant attributes and translations for them

We need for our project a "model" (doesn't necessarily have to be a model) where we can store constants attributes (they won't change and can be hardcoded) and also translations for them. We could've used a database to store these attributes, but since they won't change, it's a bit overkill.
The approach we've taken is to define all these attributes in the translations file, en.yml, then wherever we need them, we would iterate over the specific translation key: I18n.t('attributes').each do ... end.
But this feels as if it's not the best solution. I guess it's a pretty common scenario, so where should we store these constant attributes and how should we use them?
create a file constent.rb in your lib folder.
module Constent
# your code goes here.....
end
and in your application.rb
config.autoload_paths << Rails.root.join('lib')
and in your controller or model use
include Constent

what is the correct way to add classes to a controller in rails?

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.

Most appropriate way to generate directory of files from directory of template files with Rails and ERB?

My goal is to generate a directory of static html, javascript, and image files within my Rails (3) app, driven by ERB templates. For example, as a developer I might want to generate/update these files:
#{Rails.root}/public/products/baseball.html
#{Rails.root}/public/products/football.js
..from the following template files:
#{Rails.root}/product_templates/baseball.html.erb
#{Rails.root}/product_templates/football.js.erb
Ideally the templates would have access to my app's Rails environment (including URL helpers, view helpers, partials, etc.).
What's the latest and greatest way to accomplish this?
I experimented with a custom Rails generator, but found that I needed to write custom logic for skipping non-ERB files, substituting file names, etc. There must be a better way.
I'm not sure what you are trying to do exactly, that may help provide better answers, but here is some useful information:
You can call into erb directly, some information on that is here, which have probably already been doing:
http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
For the list of template files an easy Dir.glob should be able to help find the specific files easily and loop through them:
http://ruby-doc.org/core/classes/Dir.html#M000629
The tricky part I wouldn't know how to advise you on is getting access to the helpers and other things Rails provides. The helpers that you write are just modules, so you could mix those in, something similar might be possible with the built-in rails helpers.
This is interesting and related but doesn't directly answer your question, since its uses the Liquid templating engine instead of ERB, but otherwise, it does some of the static site generation you are talking about:
https://github.com/mojombo/jekyll
This is how I accomplished something similar. It accepts source and destination directories, wipes out the destination, then processes the source directory, either ERB-processing files and placing them in the destination or simply copying them (in the case of on-ERB files). It would need to be modified to handle recursively processing a directory.
I invoke it from a rake task like so:
DirectoryGenerator.new.generate(Rails.root.join('src'), Rails.root.join('public', 'dest'))
class DirectoryGenerator
include Rails.application.routes.url_helpers
include ActionView::Helpers::TagHelper
default_url_options[:host] = 'www.example.com'
def generate(source, destination)
FileUtils.rmtree(destination)
FileUtils.mkdir_p(destination)
Dir.glob(File.join(source, '*')).each do |path|
pathname = Pathname.new(path)
if pathname.extname == '.erb'
File.open(destination.join(pathname.basename.sub(/\.erb$/, '')), 'w') do |file|
file.puts(ERB.new(File.read(path)).result(binding))
end
else
FileUtils.cp(pathname, File.join(destination, pathname.basename))
end
end
end
end
Have you looked into Rails templates?
http://m.onkey.org/rails-templates for instance..
Not sure what you are getting at exactly.. are you trying to generate client sites by providing a few parameters.. that the end goal?

Where would I place custom classes and how would I access them?

I have the following class that I'd like access to within certain controllers in my app:
class Spreedly
include HTTParty
base_uri 'https://example.com/api/1234'
basic_auth 'user', 'xyz124'
headers 'Accept' => 'text/xml'
headers 'Content-Type' => 'text/xml'
format :xml
end
Where would I put that class so that I could then access it in a controller like Spreedly.post(xyz)?
You can simply place this in a file called spreedly.rb in the lib/ directory. It'll be auto-loaded by rails and will be available for use.
True, but there is nothing stopping you from putting it in your app/models directory.
And to me it seems a better place to put it - because your are accessing data like any other model in your app, and when you reference Spreedly.post etc from your controllers another programmer will naturally (imho) look in the app/models directory first before the /lib ..
Anything that needs to be referenced in multiple places or anything that is large, I put in lib. If it is tiny, and if it is used in a single model, I will put it in the same model file.
I'm very interested in testing this custom classes quickly, just as if you we're testing any other model. One problem I faced by trying to put the file under /lib, is that Spork or Zeus will reboot the environment to run the test.
I love the idea of storing the file under /lib, but this is making my continuos testing process slower. I ended up putting it under app/models.
If there was a way to test a a file under /lib more easily, that'd be great.

Resources