What command can I use to create this Ruby on Rails model? - ruby-on-rails

I'm creating a model in Ruby on Rails that will act like a file system. You'll have assets (like files) that could either be folders or files themselves. How can I create a command for this?
Asset
id (unique auto-incrementing number)
name
is_directory (bool)
user_id (id of the owner)
parent_asset_id (id of parent directory, or null if under the root)
access_token (randomly generated token, used to send shareable links)
contents
I'm thinking something like:
rails generate model Asset
name:string
is_directory:boolean
user_id:integer
parent_asset_id:integer
access_token:string contents:??
Some questions I have:
What's the difference betweeen blob vs longblob vs mediumblob vs longtext vs etc, and which would I want to use? (The assets are essentially text... not sure what the max size will be yet)
Is the parent_asset_id a good naming convention, or is there something else that would make Rails give me some secret sauce, similar to why I picked the name user_id (to match the User model)?
Is there a way to declare a default random string value for the access_token? (The access token will be used for a shareable link to the asset)
Anything else I'm overlooking?
This is a detailed question, so I hope it serves as a case study for anyone looking to implement something like a file system in RoR.
Obviously if you really wanted to implement a file system you'd use an actual file system or Amazon S3... but if you want a light-weight file-like system in RoR, this seems like the best approach.

First off, I strongly recommend you consider an existing gem like Paperclip, which will handle a lot of these details for you.
Answers in order:
You would use the binary field type to store general data, but if you use Paperclip there are some specific fields you would need to use instead, which are explained in Paperclip's docs.
If by parent_asset_id you really mean the asset can 'belong' to many other models, then look into setting up a polymorphic relationship, with an id and type field. If instead you mean storing the path to the stored file, then Paperclip handles this for you. See #3 for details...
You can access a stored file on Paperclip by calling something as simple as asset.url in your view. If you wish to go manual and insert a random code, you can insert a callback into your Asset.rb model that does something like:
before_create :generate_key
def generate_key
self.key = ActiveSupport::SecureRandom.hex
end
S3 is not a complex system to set up on Rails, and it is far more flexible and scaleable than storing the files elsewhere - however, if you want to, then use the 'assets' path.

Related

Do not nest files in rails active storage

It appears that by default, Rails Active Storage nests your file uploads by means of the associated active_storage_blob key.
The rules appear to be as follows for the default behavior. Within the <Rails.root>/storage/ directory:
take the first two characters of the key and make a directory
within that directory, take the next two characters of the key and make another directory
store the file there, and the file name is the entire key
For example: where the key of a particular file's associated active_storage_blob is: 2HadGpe3G4r5ygdgdfh5534346, It would look like the following:
I do not want this nesting behavior. I want to store the files flat within the storage directory. So I simply want it to look like this:
.
How can I do that? A google search and a read through of the Active Storage Rails Guides didn't reveal a solution.
Also just out of curiosity: why is this the default behavior?
Digging around in the code of the ActiveStorage DiskService, I found the code which generates the folder structure. All is conveniently contained within a single function:
def folder_for(key)
[ key[0..1], key[2..3] ].join("/")
end
This makes it easy to eliminate the two-letter subfolder structure by a simple patch:
module ActiveStorage
class Service::DiskService < Service
private
def folder_for(key)
""
end
end
end
Best to do a little testing on this patch, but as far as I could tell it should work just fine.
The answer to the second question I was not able to determine by just looking at the DiskService code. There are no clues there to this folder structure, so the reasons may lie elsewhere. It may be done entirely for cosmetic purposes, in order to avoid a giant single folder blob on large servers. Perhaps someone who knows more can comment.

Storing and retrieving static data in rails

In a Ruby on Rails 3.2.12 project I want to store data for a model in static files.
I don't need to be able to write to the data through the app, and want to handle updating and adding new objects by changing the static file(s).
Is there a way to set up a model, and then say "look at this file (or folder) to find the records", rather than the database (the app also has a database)?
I can imagine the data being stored in two main ways:
all the records are kept in a .json or .yml file
each record is kept in an individual .yml or .markdown file, inside a specific folder (as in jekyll).
Examples: a glossary
My application has a glossary. Each entry in the glossary should be an individual record.
There will be a glossary index page (/glossary) that is just like your standard glossary, listing all the terms and definitions. Each individual record(term + definition) also needs to be individually retrievable for use in footnotes on other pages.
I want people to be able to edit the glossary by editing files through github. They could be pointed to a single file with all the records, or (preferably) an individual file with just the record they want to edit (less syntax to handle, less risk).
Key questions
How do you point the model to the static data file, rather than the database?
Where should the source directory be?
What would be the best storage format?
Thanks very much for your help!
ActiveHash would be a good solution for this. You could use the ActiveYaml configuration if you would like to store the data in a separate yaml file instead of directly in the model.
Unless someone comes up with a better solution or with a gem which can do this out of the box I would recommend building your own model with own find and save methods. Something like (untested):
class Term
attr_accessor :name, :definition
def self.find(name)
YAML.load(File.read("#{name}.yaml"))
end
def save
File.open("#{name}.yaml", 'w'){ |f| f.write(YAML.dump(self)) }
end
end
The format for saving is up to you, see here or here for more info. Since you want the users to change the files I would go with whichever you find the most user friendly.

Models have access to text files in Rails?

As a learning exercise, I'm trying to convert an existing Sinatra app to a Rails app. The information in countries.txt will eventually be moved into a database, but in order to keep things simple for me, I'd like to first read data in from the text file, in the same way the source app did. Problem is I can't figure out where File will read from in a Rails app. Where in the Rails directory do I put the countries.txt document for a method in a model to have access?
def get_random
content = File.read("countries.txt")
words = content.split("\n")
words[rand(words.size)].upcase
end
I don't have a good suggestion on where to put countries.txt, but let's say you put it in the 'config' directory. You could then use the following to read it, regardless of what file is doing the reading.
content = File.read(File.join(RAILS_ROOT, 'config', 'countries.txt'))
However, if you don't want them in the database, there aren't that many countries... I would consider creating a file in say config/initializers/countries.rb that had something like this:
COUNTRIES = ['Country 1', 'Country2', etc...]
Or a hash mapping the name to the iso code. The advantage to this is you're only reading the file once, not every time you need to get a random country.
But with all that said.. you could also use one of the country gems that are out there to deal with it for you.

custom properties in Rails

I have just started Rails and have a basic question.
I need to add customer properties(like email id etc) so that the Rails app can read them at runtime. How can I do this ?
Can I add them to development.rb and if so how can I read it ?
In java I would have created a properties file and read it from my app.
thank you,
firemonkey
Are you trying to do store and load configuration settings?
It's easy to store configuration settings in a yaml file and load them with initializers - loads better than littering your environment files.
This Railscast: http://railscasts.com/episodes/85-yaml-configuration-file shows you how.
I'm not sure exactly what you are asking. I'm guessing you want an initial set of data in the database that you can access when you actually run the app? If that is so check out this other SO question How (and whether) to populate rails application with initial data
It's a little unclear exactly what you're trying to do, but it sounds like maybe you have a model called Customer and you would like to add some attributes to it, such as email address, id, and so on?
Basically, with Active Record you don't need to do anything special to add a simple attribute (like a string or an integer). Just add a field called "email_address" to your customers table in the database, and all of your Customer objects will automagically get "email_address" and "email_address=" methods (not to mention the Customer class itself getting "find_by_email_address" and other useful methods as well). If you are adding a field containing another model, it's a bit more complicated - add a "something_id" field to the table, and an association to the class definition (eg, "has_one :something"). For more information, see the ActiveRecord api documentation.
You don't have to use any particular means to add the field to your database, but you might want to consider Migrations. Migrations are a convenient way to keep your schema versioned and synchronized across multiple machines.
If you are building your model right now, there's a short cut built in to the generator to add fields. Instead of just saying...
script/generate scaffold customer
...you can say...
script/generate scaffold customer email:string name:string badge_number:integer
...and it will generate all the appropriate fields in your migration, as well as adding them to your generated views.

Where is the best place to store application parameters : database, file, code...?

I am developing a Ruby on Rails website and I have an "architectural" question : my application needs some parameters and I'm wondering where to store them.
In concrete terms, my application receive some requests which are evaluated and then sent. So, the Request model must have attributes concerning these treatments : a validation status and a sending status. For instance, validation status can be "accepted", "rejected" or "waiting". Sending status can be "sent", "waiting", "error during sending" or stuff like that. I have to store those status codes parameters somewhere, but I don't know what is the best solution.
I could create a model for each one and store them in the database (and having an active record model ValidationStatus for instance) but : wouldn't it be a bite excessive to create a database/model for storing data like that?
I could also just use them in the code without "storing" them, I could store them in a YAML file...
So, a more simpler question: how do you deal with your application parameters in RoR?
There are lots of global configuration plugins, most of them revolve around the idea of loading a YAML file at some point. Check this page, this plugin and even this Railscast.
I put them in the database. I have a lot of these, and they are all pretty straightforward lists of strings. The tables are all the same - id, name, description.
I generate models for them rather than having an actual model file for each one. In app/models I have a file called active_record_enums.rb, which in your case would look something like this:
ACTIVE_RECORD_ENUMS = %w{
ValidationStatus
SendingStatus
}
ACTIVE_RECORD_ENUMS.each do |classname|
eval "class #{classname} < ActiveRecord::Base; end"
classname.constantsize.class_eval do
# Add useful methods - id_for(name) and value_for(id) are handy
end
end
This file has to be required in a config file somewhere; other than that it's pretty straightforward.
(Have since viewed that rails cast mentioned above [episode 85] - it looks like a bit more 'the rails way' than below)
Another approach is to build on the existing configuration mechanism in Rails.
Lets presume there are two types of configs:
App wide configs common to dev/test/prod environments
Configs specific to envrionments dev/test/prod
For the first scenario, items in "RAILS_ROOT + '/config/environment.rb'" work. Just see that the names are captialised so they are Ruby constants. A variation to this is have a reference to another file in that environment.rb file ...
require RAILS_ROOT + '/config/appConfigCommon.rb'
and place relevant config items in that file. This has the advantage of being able to be referenced independant of Rails.
For scenario 2, a similar approach can be taken. Place items for development in "RAILS_ROOT + '/config/environments/development.rb'" or something like
require RAILS_ROOT + '/config/environments/appConfigDev.rb'
and place environment specific items in that required file, making sure they start with caps. And follow the same pattern for test/prod (and others if need be).
The config items are directly accessible in views and controllers (not sure about models) by simply using the constant name.
I am not using Ruby but I will tell you that I started out (in ASP.NET) placing lots of settings in a Web.Config file (similar to a YAML). As time went on, though, the system evolved to the point where different instances needed different settings. So, almost all of them have migrated to the database. So...if you'll be deploying multiple instances of your site, I'd strongly recommend keeping settings in a table of your database (mine has just one record, with fields for various settings). If I had done this to start, I'd have saved a significant amount of time.
I tend to just make a string column for each, and use validates_inclusion_of to set what's acceptable for them.
class Request < ActiveRecord::Base
validates_inclusion_of :validation_status, :in => ['accepted','rejected','waiting']
validates_inclusion_of :sending_status, :in => ['sent','waiting','...']
end
If you need to have things happen (ie. emails sent) when the status changes, look into using the Acts As State Machine plugin to manage it.

Resources