I created a plugin (gem) with my models that I'm using in two other apps.
I placed models to /lib folder and my app is working all good. But my question is where do I put future migrations? And how do I generate migrations inside the gem? Or do I have to write them manually?
To answer your first question the future migrations can be stored in /lib/db/migrate/ folder
Coming to the second part
Rails does not include any built-in support for calling migrations from plugins.But still you can run migrations by different methods
1.Create a Custom Rake Task
The first method is by using a rake task
Consider an example
namespace :db do
namespace :migrate do
description = "Migrate the database through scripts in vendor/plugins/pluginname/lib/db/migrate"
description << "and update db/schema.rb by invoking db:schema:dump."
description << "Target specific version with VERSION=x. Turn off output with VERBOSE=false."
desc description
task :pluginame => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("vendor/plugins/pluginname/lib/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
end
end
2. Call Migrations directly
Dir.glob(File.join(File.dirname(__FILE__), "db", "migrate", "*")).each do |file|
require file
end
3. Migration Generator
These methods apart you can create your own migration generator.
Generating migrations has several advantages over other methods. Namely, you can allow other developers to more easily customize the migration. The flow looks like this:
call your script/generate script and pass in whatever options they
need
examine the generated migration, adding/removing columns or other
options as necessary
Providing the generator template example of railties gem
https://github.com/rails/rails/blob/master/railties/lib/rails/generators/migration.rb
Please Refer http://guides.rubyonrails.org/v2.3.11/plugins.html#migrations
for more details
Related
I'm trying to create a new Django application whose migrations depend on other apps migrations. I want migrations on the other apps to have a dependency on migrations on the new application, even when the new application doesn't reference at all the models at the other apps.
For example, let's say I have application A (an existing app) and application B (my new application):
A has a migration called A.0001, and B has a migration called B.0001 which depends on A.0001. I now make a change at A.MyModel, so I need to run python manage.py makemigrations to generate a new migration A.0002.
What I want is A.0002 to automatically depend on B.0001.
How can I specify this dependency on new migrations in A without having to do it by hand each time I modify a model in A?
I tried to add empty modifications of models in A at the migration B.0001, but I haven't got it to work and it looks very hacky to me.
I found a solution that doesn't exactly solve what I asked, but can be a workaround for some people, so I will post it here.
There is an undocumented field of the django.Migration class, called run_before, that works just like an inverse dependencies.
A.0002 will depend on B.0001 if I simply define run_before = [ A.0002 ] at B.0001.
I'm trying to test a named scope in my Rails model with RSpec using FactoryBot. I'm creating several records, where only one is returned by the scope.
RSpec.describe GemNamespace::GemModel, type: :model do
before(:all)
FactoryBot.create(:gem_model, :trait1) # id 1
FactoryBot.create(:gem_model, :trait2) # id 2
FactoryBot.create(:gem_model, :trait3) # id 3
end
let(:included_record) { GemNamespace::GemModel.find 1 }
describe 'my_named_scope' do
it 'returns only records matching the conditions' do
scope_results = GemNamespace::GemModel.my_named_scope
expect(scope_results).to contain_exactly(included_record)
end
end
end
The test is failing because even though included_record is the only record in the scope_results, some debugging shows that the included_record is actually a different Ruby object than the one in the results for some reason. Thus, the contain_exactly fails.
I've done scope testing like this on tons of models and it's always worked. The only difference with this one is that the model is defined inside a gem, and I'm extending its functionality by adding my named scope to it in my Rails app.
What am I missing? Why is it behaving like this only for this model?
If it matters:
Ruby 2.5.0
Rails 5.1.5
rspec 3.7.0
rspec-rails 3.7.2
factory_bot(_rails) 4.8.2
UPDATE: I'll put this here instead of editing the above. I am actually testing a database view as opposed to a table. The views do not have a unique id column, so I'm not actually doing a GemNamespace::GemModel.find 1 above, but instead a where(column: <condition value>).
I solved this with a workaround. I don't know too much about the internals of Rails, but it seems that the database view (and corresponding model) not having an id column kinda screws things up (i.e., the separate Ruby objects being created). So I simply compared all the values of the two objects "manually"
# As a workaround, we're just gonna convert them both to Ruby hashes using
# the #as_json method, and compare those instead.
expect(scope_results.as_json).to contain_exactly(included_record.as_json)
I am building my first Rails engine. I called it my_engine, so it generates the files
lib/my_engine.rb
lib/my_engine/engine.rb
lib/my_engine/version.rb
That all have the module named MyEngine. And in the gemspec, the gem name is also set to my_engine.
If I create a model my_model, it goes into app/models/my_engine/my_model.rb, and is generated as
module MyEngine
class MyModel < ActiveRecord::Base
end
end
If I make a class method in here, and put the gem in a Rails project, it all works fine.
def self.hello
"Hello from your Engine's model!"
end
$ bundle exec rails c
[1] (pry) main: 0> MyEngine::MyModel.hello
=> "Hello from your Engine's model!"
However, I do not want the gem name to be my_engine. But if I change the name in the gemspec to something else, like what-i-really-want-to-name-it, everything stops working. Rails cannot see my model, although it can see my namespace. I am indeed changing the gem name in the Rails app and re-bundling, so it's not an issue of that.
$ bundle exec rails c
[1] (pry) main: 0> MyEngine::VERSION
=> "0.0.1" # default version from engine generation
[2] (pry) main: 0> MyEngine::MyModel.hello
NameError: uninitialized constant MyEngine::MyModel
from (pry):2:in `__pry__'
Why is "this" tied directly to the gem name? Is there any kind of workaround for this? I would really like to have the gem name and the module name to be different values.
Using: Rails 4.2.6, Ruby 2.3.0
The answer is that one of Rails' foundational concepts is:
Convention over configuration.
When you decide to override the principle of Convention with Configuration, that's a Rails anti-pattern. Is it possible to to do it and be happy and have a working Rails app? Sure, but it's not code you would want to show as an example of your work on a Rails app.
So, the convention is that the module name matches the gem name. It's just a convention, but since convention is the mutually agreed law in Rails town, you're going anti-pattern when you don't follow it.
Added in response to OP comment
Rails engines are typified by the fact they use an isolated namespace and isolated resources. Gems don't, so in effect, the answer is yes, using a Rails engine does enforce a namespace convention that does not exist for a gem. And that namespacing is used by the middleware to keep the main_app separated from the engine at runtime. Two examples to illustrate:
An extreme example: you can have an app mounted as as engine on itself. Namespacing isolates one from the other so the middleware services act on the right processes which are only differentiated by the namespace.
Another example: 2 engines mounted on a main_app. Now there are essentially 3 apps running. How would you want to implement non-conventional namespace isolation in this case?
So...
It is possible to hammer in a nonconforming namespace in a Rails engine? Probably. I've never tried. But your engine will not be portable. And worse, someone installing it will not be able to mount another engine that is conforming (and sharing the main_app and the middleware stack) because you've forced them into a configuration maze that breaks a conventional Rails engine. This last part is a theory.
I have a Customer_ratings model that allows users to leave feedback on each other. The web app is working properly, and feedback is collected, stored and displayed.
I wanted to go in and delete some feedback through the rails console, but when I enter Customer_rating.all, I get the following error:
LoadError: Unable to autoload constant Customer_rating, expected /Users/myapps/app/models/customer_rating.rb to define it
Similarly, if I enter Customer_rating[0], I get:
RuntimeError: Circular dependency detected while autoloading constant Customer_rating
I don't have this issue while accessing other tables through my console.
What could be causing the issue, and why wouldn't this error prohibit Customer_ratings from working properly through the web app?
It seems like a case of messed up naming convention.
As per Rails naming convention, file names should be in snake_case and class names in CamelCase. In your scenario, the file name should be customer_rating.rb and class name should be CustomerRating.
After making these changes, use CustomerRating.all(as the updated class name is CustomerRating) to fetch all the customer ratings. Do not use Customer_rating.all.
I'd also like to add a scenario of this problem that I found for future reference.
I'm running Rails 4.0 and I had this same problem but what happened was I had a model named Student inside student.rb that was contained in a folder called Student. I didn't realize it at first but the folder name was the problem. Changing the folder name to something other than a model name solved the problem.
If the naming convention is not off, like in this question, it may be an issue on initial first load if you're making a lot of requests at the same time. I experienced this with nested controllers Api::LocationsController.
I solved it by enabled eager_load in development env:
Rails.application.configure do
...
# Enabled this to avoid crash unable to autoload controller
# Error happens when you start and stop server on initial requests
# solution found via https://github.com/rails/rails/issues/32082#issuecomment-367715194
config.eager_load = true
I based this off of rails issues comments: https://github.com/rails/rails/issues/32082#issuecomment-367715194
You just need to modify the name of the Module
For example if the link is http://sairam.esy.es/users/customer_rating then
you controller should like be
module Users
class RatingController
# ...
def customer_rating
# ...
end
# ...
end
end
I have a Rails 4.1.0 mountable engine. In the engine's application_helper.rb:
module MyEngine
module ApplicationHelper
def test123
"test123"
end
end
end
The method is in the dummy app's view general/index.html.erb view:
%<= test123 %>
This works. However, when I change the string returned by def test123 and refresh the browser, the new string is not displayed.
Of course, restarting the web server in the dummy app shows the new string.
So the question is, how to reload the engine's files without having to restart the web server?
PS. I am preferably looking for a way to do this using Rails itself, or a specific gem that solves this problem (but not the generic gems like Guard, Spork etc. although if all else fails, I will consider those too.)
PPS. There are similar questions on SO, but I have tried them all (even though they are for Rails 2.x, 3.x), and they have not worked for me.
You should explicitly require dependent helpers:
# engines/my_engine/app/controllers/my_engine/application_controller.rb
require_dependency "my_engine/application_helper" # This is a key point!
module MyEngine
class ApplicationController < ::ApplicationController
helper ApplicationHelper
...
you can use some thing like zeus which is helping a lot in watching project files for changes except in some cases when you change the configuration files it doesn't work and needs to be manually restarted.
but overall in most cases this works more than awesome