does a sequel models generator exists? - ruby

I'm looking for a ruby class that could generate the sequel model file for Ramaze after reading the definition of the table in a mySQL database.
For example, I would like to type :
ruby mySuperGenerator.rb "mytable"
And the result shold be the file "mytable.rb" in "model" directory, containing :
class Mytable < Sequel::Model(:mytable)
# All plugins I've defined somewhere before lauching the generator
plugin :validation_helpers
plugin :json_serializer
one_to_many :othertable
many_to_one :othertable2
def validate
# Generating this if there are some not null attributes in this table
validates_presence [:fieldthatshoulnotbenull1, :fieldthatshoulnotbenull2]
errors.add(:fieldthatshoulnotbenull1, 'The field fieldthatshoulnotbenull1 should not be null.') if self.fieldthatshoulnotbenull1.nil?
end
def before_create
# All the default values found for each table attributes
self.creation_time ||= Time.now
end
def before_destroy
# referential integrity
self.othertable_dataset.destroy unless self.othertable.nil?
end
end
Does someone knows if such a generator exists ?

Well...
I finally wrote my script.
see https://github.com/Pilooz/sequel_model_generator Have look and fork !

Related

ActiveRecord mapping to table name with schema name as prefix

Has anyone experienced this issue on mapping table in ActiveRecord when table name need a schema name as a prefix (oracle)?
Gemfile
gem 'activerecord', '4.2.4'
gem 'activerecord-oracle_enhanced-adapter', '1.6.7'
....
db_helper.rb
class Student < ActiveRecord::Base
self.abstract_class = true
self.table_name_prefix = 'OPT_ABC.'
self.table_name = 'STUDENT'
def self.list_student
puts Student.take(1) #testing
end
end
The actual table name looks like:
SELECT * FROM OPT_ABC.STUDENT;
I am able to connect to the database instance, but when the code gets line:
puts Student.take(1) # SELECT * FROM STUDENT LIMIT 1
I get the following error:
ActiveRecord::StatementInvalid:
table or view does not exist: SELECT "STUDENT".* FROM "STUDENT"
I am looking for solution on how to handle 'OPT_ABC." table prefix. Please share your solution.
It looks like the problem is that you're trying to use both self.table_name_prefix= and self.table_name= together when you should be using one OR the other.
First let's consider how both self.table_name_prefix= and self.table_name= work.
self.table_name_prefix=
According to the documentation, self.table_name_prefix= works by prepending the passed in value to the table name that ActiveRecord automatically generates based off of the name of the class.
So, if the class name is Student and you do self.table_name_prefix = 'OPT_ABC.', your table name will be OPT_ABC.STUDENTS. Note that the generated table name is plural (and ends in an s).
self.table_name=
According to the documentation, self.table_name= sets the table name explicitly. This means that it completely overrides the table name to the value that you pass in.
So, if you do self.table_name = 'OPT_ABC.STUDENT' your table name will be OPT_ABC.STUDENT.
So, given that, to set the table name to OPT_ABC.STUDENT, you should be able to simply pass the value into self.table_name like this:
class Student < ActiveRecord::Base
self.abstract_class = true
self.table_name = 'OPT_ABC.STUDENT'
def self.list_student
puts Student.take(1) #testing
end
end

How to get data from other tables using Sequel

I use Sequel and I have the following 2 models in my Sinatra app
# foo.rb
require 'sequel'
module Notes
module Models
class Foo < Sequel::Model
plugin :json_serializer, naked: true
plugin :validation_helpers
one_to_many :bars
def validate
super
validates_presence [:foo_name], message: "can't be empty"
end
end
end
end
# bar.rb
require 'sequel'
module Notes
module Models
class Bar < Sequel::Model
plugin :json_serializer, naked: true
plugin :validation_helpers
many_to_one :foo
def validate
super
validates_presence [:bar_name], message: "can't be empty"
end
end
end
end
There is a foo_id foreign key in the Bar table.
I have an API route where you can get all bars or just a specific bar if a parameter is passed in, looks something like:
app.get '/api/bars' do
require_session
bar_name = params[:bar_name]
bar_model = Models::Bar
if bar_name.nil?
bar_model.all.to_json
else
bar_model.where(Sequel.ilike(:bar_name, '%' + bar_name + '%'))
.all
.to_json
end
end
What I'd like to do but haven't figured out yet is how can I also get at least foo_name from the Foo table in the result as well, retrieved based on the foo_id that's in Bar table?
Even more, what if there are even longer association, say there's another foreign key, for example baz_id, in the Foo table that's linked to a Baz table, and in this same API, I also want to get all the info from the Foo and Baz tables based on the foreign key association in those respective tables.
Hope that makes sense and any help is greatly appreciated.
You can use the :include option of Sequel's to_json method to do this. You can specify this in the model when you set the json_serializer options, but you can also override those defaults when you call to_json on an instance.
So, on an individual instance of Bar, you could do:
Bar.first.to_json(include: {foo: {only: :foo_name}})
If you want to do it for a full list, as in your example, you would want to call it on the class. Note that if you call it on an array this won't work, so you must call it on the dataset before converting it to an array.
# all models
Bar.to_json(include: {foo: {only: :foo_name}})
# a subset of models
Bar.where(Sequel.ilike(:bar_name, '%' + bar_name + '%'))
.to_json(include: {foo: {only: :foo_name}})

Make friendly_id scope play nice with subclassed ActiveRecord model

I have a subclassed ActiveRecord model which uses a separate table to store records and friendly_id (4.1.0.beta.1) to generate slugs. Problem is friendly_id is using the parent class's table to check for existing slugs, instead of using the child table. Basically I'd like friendly_id to scope its checks to the right table.
Example:
class Parent
friendly_id :name, :use => :slugged
end
class Child < Parent
self.table_name = 'children'
end
Parent.create(name: 'hello').slug
> 'hello'
Child.create(name: 'hello').slug
> 'hello--2'
I want friendly_id to generate the 'hello' slug for the second create, because there are no records in the children table with that slug. Is there a way to configure or monkey patch the class friendly id uses for its queries?
EDIT: added friendly_id version for future reference
I'm posting my own solution to this problem, just in case someone is having the same problem. I should reiterate that this problem was found on version 4.1.0.beta.1 of the friendly_id gem (which at the time was the most recent version), so this issue may not occur any more.
To solve this problem, I basically configured slug_generator_class to use my own class, so I could monkey patch the culprit method.
In my model:
friendly_id do |config|
config.slug_generator_class = SubclassScopableSlugGenerator
end
In an initializer, I overrode the FriendlyId::SlugGenerator.conflicts method so I could access the sluggable_class var:
# Lets a non-STI subclass of a FriendlyId parent (i.e. a subclass with its
# own dedicated table) have independent slug uniqueness.
class SubclassScopableSlugGenerator < FriendlyId::SlugGenerator
private
def conflicts
# this is the only line we're actually changing
sluggable_class = friendly_id_config.model_class
pkey = sluggable_class.primary_key
value = sluggable.send pkey
base = "#{column} = ? OR #{column} LIKE ?"
# Awful hack for SQLite3, which does not pick up '\' as the escape character without this.
base << "ESCAPE '\\'" if sluggable.connection.adapter_name =~ /sqlite/i
scope = sluggable_class.unscoped.where(base, normalized, wildcard)
scope = scope.where("#{pkey} <> ?", value) unless sluggable.new_record?
length_command = "LENGTH"
length_command = "LEN" if sluggable.connection.adapter_name =~ /sqlserver/i
scope = scope.order("#{length_command}(#{column}) DESC, #{column} DESC")
end
end

How to get a (Ruby) DataMapper custom type to work?

I have a SchoolDay class that represents a school day: it can tell you the date, the semester, the term, the week, and the day. It can generate a string like "Sem1 13A Fri". To store these objects in the database, I want them serialized as a string.
Here is my DataMapper custom type code. I've sort of scraped ideas from the code in dm-types because (disappointingly) there is no real documentation for creating custom types. Sorry it's long.
module DataMapper
class Property
class SchoolDay < DataMapper::Property::String
#load_as ::SchoolRecord::DomainObjects::SchoolDay
# Commented out: the 'load_as' method is not found
def load(value)
# Take a string from the database and load it. We need a calendar!
val = case value
when ::String then calendar.schoolday(value)
when ::SR::DO::SchoolDay then value
else
# fail
end
end
def dump(value)
# Store a SchoolDay value into the database as a string.
case value
when SR::DO::SchoolDay
sd = value
"Sem#{sd.semester} #{sd.weekstr} #{sd.day}"
when ::String
value
else
# fail
end
end
def typecast(value)
# I don't know what this is supposed to do -- that is, when and why it
# is called -- but I am aping the behaviour of the Regexp custom type,
# which, like this one, stores as a String and loads as something else.
load(value)
end
# private methods calendar() and error_message() omitted
end
end
end
This code works for reading from the (SQLite) database, but not for creating new rows. The error message is:
Schoolday must be of type String
The code that defines the DataMapper resource and tries to create the record is:
class LessonDescription
include DataMapper::Resource
property :id, Serial
property :schoolday, SchoolDay # "Sem1 3A Fri"
property :class_label, String # "10"
property :period, Integer # (0..6), 0 being before school
property :description, Text # "Completed yesterday's worksheet. hw:(4-07)"
end
# ...
ld = LessonDescription.create(
schoolday: #schoolday,
class_label: #class_label,
period: #period,
description: description
)
Here is the code for the Regexp datamapper type in the dm-types library. It's so simple!
module DataMapper
class Property
class Regexp < String
load_as ::Regexp # NOTE THIS LINE
def load(value)
::Regexp.new(value) unless value.nil?
end
def dump(value)
value.source unless value.nil?
end
def typecast(value)
load(value)
end
end
end
end
For some reason, I cannot use the load_as line in my code.
To summarise: I am trying to create a custom type that translates between a SchoolDay (domain object) and a String (database representation). The translation is easy, and I've copied the code structure primarily from the DataMapper Regexp type. But when I try to save a SchoolDay, it complains that I'm not giving it a string. Frustratingly, I can't use the "load_as" method that the built-in and custom types all use, even though I have the latest gem. I can't find the "load_as" method defined anywhere in the source code for DataMapper, either. But it's called!
Sorry for the ridiculous length. Any help would be greatly appreciated, as would a pointer to a guide for creating these things that I have somehow missed.
It seems that the current code of dm-types at github hasn't made it to any official release -- that's why load_as doesn't work in your example. But try to add this method:
module DataMapper
class Property
class SchoolDay < DataMapper::Property::String
def custom?
true
end
end
end
end
That's working here.

ruby activerecord how to use alternate updated_at column

I am trying to use Rails3 with an existing db schema. The timestamp fields updated_at and created_at need to be mapped to created and modified on the db.
Is there a way to tell activerecord to use alternate column names?
Thanks in advance, Chris
EDIT
Could it be done by having this in a file in the model directory:
module ActiveRecord
module Timestamp
def timestamp_attributes_for_update #:nodoc:
[:modified, :updated_on]
end
def timestamp_attributes_for_create #:nodoc:
[:created, :created_on]
end
end
end
I don't think there is a way to do that without overriding timestamp_attributes_for_update in ActiveRecord:: Timestamp.
A simple workaround would be to use a before_update callback in the model.
before_update :set_modified_time
private
def set_modified_time
self.modified = Time.now
end

Resources