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.
Related
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.
I have a text file in my Ruby on Rails application that I would like to call in a model. The problem is, I want to follow rails conventions in terms of placing files.
The reason I'm using a .txt file is because I have a very large array of words I have to iterate over. This is for a model validation and I need to ensure that the input does not contain any of these words.
I could just declare an array, but it would make my models to fat. Therefore, I'd like to read from a file where I place my comma separated array elements.
There are several ideas I have
Root Directory: I could add the .txt file to my root directory directly
Public Directory: Could store it in /public
Temp Directory: Could store it in /tmp
Lib Directory: Could store it in /lib
Inside Model: Could just store it inside /app/models/mymodel.rb
Tried to find a similar answer here, but couldn't find a consensus. Anyways, this is a two part question. How would I read from the file inside the model. Would I do something like
file = File.read("#{Rails.root}/public/textfile.txt")
Thanks for your help
There are no conventions for storing files. All files can be stored in the public folder. This can vary if you're using Heroku or hostings that do not allow write access on the file system, and obviously you need to write.
Otherwise, you must take into account what is the purpose of this file. It might be a better idea to store files in a database or using rails store systems that are explained here: session store guides and here you can find more information about security
I hope you are not opening and reading this txt file each time you do a validation. I would suggest writing a rake task that would read the file once, parse the words and store them in a an indexed database column. You could create a model ForbiddenWord with an attribute called 'name' of type string(with index of course). Then you could write a custom validator and use it in MyModel, something like this:
class WordConstraintValidator < ActiveModel::Validator
def validate(record)
if ForbiddenWord.exists?(name: record.name)
record.errors[:base] << "You can not use the word #{record.name}"
end
end
end
class MyModel < ActiveRecord::Base
validates_with WordConstraintValidator
end
You can put your custom validator inside /app/models/, I am also assuming you have a name attribute on MyModel class, otherwise change this accordingly. There could be other possible solutions to this problem, this is the first thing that comes to mind.
I have a yaml file of application settings (arrays and hashes) that I would like a user to be able to edit through a view. What is the best way to do this?
Load the yaml file into an object that the view helper fields_for can use? So an ActiveModel or OpenStruct object?
These are application settings and are not associated with a particular user.
In the end I used an interim active record object with a serialze column as advised by Dmitri and then can use YAML.dump(app_config.settings, file) to create a yaml file
Why would you want to store users' settings in YAML files ? Wouldn't it be easier to create a column in the user's table, and add a serialization rule for that column - it would produce pretty much the same result, and you would be able to use all the stuff you need without any pain
If you are that stubborn, then create a separate action for user's controller and post a form_tag (i.e model-less behavor) form and then apply all the logic to re-write the data in the file, e.g .. YAML.load_file, update and write-back to file.
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.
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.