Model associations using DataMapper in separate files - ruby

I'm working with DataMapper and trying to use associations between models Project and Task. I have the models in separate files project.rb and task.rb. When I try associating them with each other I get the following error:
Cannot find the parent_model Project for Task in project (NameError)
I gather this is caused by project.rb requiring task.rb and vice versa, since the association works fine if I just include it in one of the files. Here's the code:
project.rb
require 'dmconfig'
require 'task'
class Project
include DataMapper::Resource
property :id, Serial
has n, :tasks
end
DataMapper.auto_upgrade!
DataMapper.finalize
task.rb
require 'dmconfig'
require 'project'
class Task
include DataMapper::Resource
property :id, Serial
belongs_to :project
end
DataMapper.auto_upgrade!
DataMapper.finalize
dmconfig.rb
require 'rubygems'
require 'dm-core'
require 'dm-migrations'
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, 'sqlite://' + Dir.pwd + '/taskmanager.db')
If I remove the association from one of the files it works fine, at least from one direction:
require 'dmconfig'
class Project
include DataMapper::Resource
property :id, Serial
end
DataMapper.auto_upgrade!
DataMapper.finalize
If I want the association to work from both directions is the only reasonable solution to just put both classes in the same file? Or is there a way that I can keep them separated and still manage it?

You need to call finalize after you require all your models, not after each one. One of the things finalize does is sanity check your models, to make sure all the relevant models have been required. The application boot process, after requiring all the library files is an ideal place to do this. I suggest something like:
project.rb
class Project
include DataMapper::Resource
property :id, Serial
has n, :tasks
end
task.rb
class Task
include DataMapper::Resource
property :id, Serial
belongs_to :project
end
dmconfig.rb
require 'dm-core'
require 'dm-migrations'
require 'project'
require 'task'
# note that at this point, all models are required!
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, 'sqlite://' + Dir.pwd + '/taskmanager.db')
DataMapper.finalize
DataMapper.auto_upgrade!
Or something of that nature. In your application, you require 'dmconfig' and have everything set up with that one require. DataMapper defers checking for the far end of relationships (say, projects in the Task model) until you call finalize or auto_upgrade!, so make sure all the models are required before you do this.

It looks like that might be caused by a typo in task.rb
belongs_to, :project
should be written as:
belongs_to :project
And for what it's worth, when using Sinatra, for example, I prefer to keep all of my models together in one lib/models.rb file... at least for as long as that's manageable.

First of all, call DataMapper.finalize before you call auto_upgrade. Secondly, it's better to load the models, call finalize and then do DataMapper.auto_migrate! instead of calling auto_upgrade after each model definition.

Related

Rails Association belongs_to marking as optional dynamically in class and mixin

I have came across a scenario in an existing code base.
Im upgrading Rails to 5.1 from 5.0 and we now need to define relationships as optional where its not required.
I have came across a situation where we have a class and a mixin that are causing conflict.
Is there a way to define the relationship with the :user so that it will meet requirements in both scenarios.
In the Model, a user is not required, but within the mixin, a users presence is validated, causing the conflict.
As the Mixin is included, writing a proc to evaluate whether to mark the relationship as optional by using a function/proc results in the class method being used at runtime. If you prepend the mixin instead, it will default to the method in the mixin, meaning either way they conflict.
class Foo < ActiveRecord::Base
belongs_to :user, optional: true
end
module Mixins
module Foo
extend ActiveSupport::Concern
validates :user, presence: true, if: :user_is_required?
private
def user_is_required?
<!-- LOGIC HERE -->
end
end
end
At an engine level.. the mixin is included as so:
::Foo.include(Mixins::Foo)

Query values from database using Datamapper

I am making a web application using Sinatra, Ruby, SQLite. When the user logs in, I need to record their username and password into the table. If this is the code for the main ruby file, what do I put in the erb file? Thank you!
require 'dm-core'
require 'dm-migrations'
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/user.db")
class User
include DataMapper::Resource
property :id, Serial
property :name, String
property :password, String
end
Getting individual column values in row:
#user[0].name
#user[0].password

mongoid configuration on ruby without framework

I try to write business logic of my application. It is all ruby classes. There is no database or no UI framework like Rails, Sinatra. I only have a Gem_file on business logic and, Gem_file only contain "mini_test" gem. I use mini_test for testing business logic. Now, I need to add a database to the system. How can I do this?
mongoid configuration is made in application.file on Rails. But ,I don't use Rails or any other framework. Is there anyway to make configuration of mongoid without framework like Rails, Sinatra.
I hope I can explain my problem. Also, I add my codes in below:
this is my context-
class HeadTeacherDefineAcademicYearContext
attr_reader :person, :academicyear
def initialize(person, academicyear)
#person, #academicyear = person, academicyear
#person.extend HeadTeacher
end
def call
#person.define_academic_year #academicyear
end
end
this is my role module
module HeadTeacher
def define_academic_year(academicyear)
#i write db save process here using any database
end
end
my model class
class AcademicYear
attr_accessor :year
end
You have to include gem 'mongoid' in your Gemfile and install it. After that, you can require and initialize Mongoid where you need it:
require 'mongoid'
Mongoid.load!("mongoid.yml", :development)
It expects a mongoid.yml file with configuration. Examlpe:
development:
sessions:
default:
database: myapp_development
hosts:
- localhost:27017
Of course, you can use another context than :development, maybe assign it via a environment variable. Now, add Mongoid::Document to your model:
class AcademicYear
include Mongoid::Document
field :year, type: Integer
end
Add gem "mongoid", "~> 3.0.0" to your Gemfile
Then put configuration yaml file to your project with contents like this:
development:
sessions:
default:
database: mongoid
hosts:
- localhost:27017
Then use Mongoid.load!("path/to/your/mongoid.yml", :development) in your app.
In every class you want to save objects to DB you have to include Mongoid::Document.
So your example becomes:
class HeadTeacherDefineAcademicYearContext
attr_reader :person, :academicyear
field :person, type: String
field :academicyear, type: Date
...
end
You should better check mongoid docs for stuff to do next.

How do I require classes with a circular dependency? [duplicate]

This question already has answers here:
Circular Dependencies in Ruby
(3 answers)
Closed 9 years ago.
I've been spoiled by Rail's autoloading of missing constants. In Ruby, if I have two classes, one nested inside the other but in different files, how do I require them since both depend on each other (circular dependency).
# user.rb
class User < ActiveRecord::Base
serialize :preferences, User::Preferences
end
# user/preferences.rb
class User::Preferences
end
# user_spec.rb
require 'user'
require 'user/preferences'
Note: I have not required the Rails environment.
If I try and load User first, the code fails because it does not know about User::Preferences yet. If I load "user/preferences" first, it fails when it loads User because the existing User class does not subclass ActiveRecord.
I have a suspicion I need to remove the circular dependency or, if possible, make serialize lazy load the class by passing a string 'User::Preferences' which is turned in to a constant when needed.
One hack I have is to create an empty User class inheriting from ActiveRecord::Base in user/preferences.rb:
class User < ActiveRecord::Base; end
class User::Preferences
end
Rather than wire knowledge of User's implementation into User::Preferences you could put the stub declaration in a common base, like so:
# user_base.rb
class User < ActiveRecord::Base; end
# user.rb
require 'user_base'
require 'user/preferences'
class User
serialize :preferences, User::Preferences
...
end
# user/preferences.rb
require 'user_base'
class User::Preferences
end
Alternatively, you could move User::Preferences into an independent module namespace such as ModelHelper::User::Preferences. I think I prefer this solution. The fact that you have a circular dependency is a code smell and the only thing causing it is the reuse of User class as a namespace container for User::Preferences.

I get same error with my DataMapper models no matter how i'm building them

Whatever columns/types/relations i'm using within my DataMapper models i'm always get same fatal error:
undefined method `include?' for nil:NilClass
a sample model:
class Book
include DataMapper::Resource
property :id, Serial
property :name, String
end
Even with this trivial model i get that weird error.
Latest datamapper, reinstalled to be sure it is no broken somehow.
Ruby 1.9.3
Mysql 5
Sequel works just well on same environment.
did you call DataMapper.finalize after defining your models?
try:
class Book
include DataMapper::Resource
property :id, Serial
property :name, String
end
DataMapper.finalize # this is required on any scenario
Official docs:
http://datamapper.org/getting-started.html
See Finalize Models at the bottom

Resources