Coder Social home page Coder Social logo

petehamilton / citier Goto Github PK

View Code? Open in Web Editor NEW

This project forked from altrabio/citiesforrails

88.0 6.0 24.0 1.54 MB

CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a solution for simple Multiple Class Inheritance in Rails.

Ruby 100.00%

citier's Introduction

CITIER

What is it?

CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a very simple way of implementing Multiple Table inheritance in Rails.

In short it allows you (finally) to do this:

class Product < ActiveRecord::Base
  acts_as_citier
  validates_presence_of :name
  def an_awesome_product
    puts "I #{name} am an awesome product"
  end
end

class Book < Product
  acts_as_citier
  validates_presence_of :title
  
  def an_awesome_book
    self.an_awesome_product
    puts "A book to be precise"
  end
end

class Dictionary < Book
  acts_as_citier
  validates_presence_of :language
  
  def is_awesome
    self.an_awesome_book
    puts "I am a dictionary. Yeah!"
  end
end

There are two lines you need to add to your migration files as well, see Here for full information

$ rails console
Loading development environment (Rails 3.0.7)
>> :001 > d = Dictionary.new(:name=>nil,:price=>25.99,:title=>nil,:language=>nil)
citier -> Root Class
citier -> table_name -> products
citier -> Non Root Class
citier -> table_name -> books
citier -> tablename (view) -> view_books
citier -> Non Root Class
citier -> table_name -> dictionaries
citier -> tablename (view) -> view_dictionaries
 => #<Dictionary id: nil, type: "Dictionary", name: nil, price: 25, created_at: nil, updated_at: nil, title: nil, author: nil, language: nil> 
>> :002 > d.valid?
 => false 
>> :003 > d.errors
 => #<OrderedHash {:language=>["can't be blank"], :name=>["can't be blank"], :title=>["can't be blank"]}> 
>> :004 > d = Dictionary.new(:name=>"Ox. Eng. Dict",:price=>25.99,:title=>"The Oxford English Dictionary",:language=>"English")
 => #<Dictionary id: nil, type: "Dictionary", name: "Ox. Eng. Dict", price: 25, created_at: nil, updated_at: nil, title: "The Oxford English Dictionary", author: nil, language: "English"> 
>> :005 > d.valid?
 => true
>> :006 > d.is_awesome()
I Ox. Eng. Dict am an awesome product
A book to be precise
I am a dictionary. Yeah!
=> nil
>> :007 > d.save()
citier -> Non-Root Class Dictionary
citier -> Non-Root Class Book
citier -> UPDATE products SET type = 'Product' WHERE id = 1
citier -> SQL : UPDATE products SET type = 'Book' WHERE id = 1
citier -> SQL : UPDATE products SET type = 'Dictionary' WHERE id = 1
 => true 
>> :008 > Dictionary.all()
 => [#<Dictionary id: 1, type: "Dictionary", name: "Ox. Eng. Dict", price: 25, created_at: "2011-04-28 22:46:23", updated_at: "2011-04-28 22:46:23", title: "The Oxford English Dictionary", author: nil, language: "English">] 
>> :009 > Product.all()
 => [#<Dictionary id: 1, type: "Dictionary", name: "Ox. Eng. Dict", price: 25, created_at: "2011-04-28 22:46:23", updated_at: "2011-04-28 22:46:23", title: "The Oxford English Dictionary", author: nil, language: "English">] 
>> :010 > d = Dictionary.all().first()
 => #<Dictionary id: 1, type: "Dictionary", name: "Ox. Eng. Dict", price: 25, created_at: "2011-04-28 22:46:23", updated_at: "2011-04-28 22:46:23", title: "The Oxford English Dictionary", author: nil, language: "English"> 
>> :011 > d.delete()
citier -> Deleting Dictionary with ID 1
citier -> Deleting back up hierarchy Dictionary
citier -> Deleting back up hierarchy Book
 => true 
>> :012 > Dictionary.all()
 => []

Cool huh?

It is based on original code by ALTRABio (www.github.com/altrabio/)

For full information please go to the github page: http://peterhamilton.github.com/citier/

Contributing to citier

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
  • Fork the project.
  • Start a feature/bugfix branch.
  • Commit and push until you are happy with your contribution.
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright

Copyright (c) 2012 Peter Hamilton. See LICENSE.txt for further details.

citier's People

Contributors

douwem avatar educobuci avatar hyveag avatar noisecapella avatar raphaelpereira avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

citier's Issues

Child looses it's type when saved after being retrieved from root class

Fixed Issue with saving a child after it's retrieved from parent

Facility is my Root
SwimmingPool is a subclass

Problem:

  1. Create a Facility of Type 'SwimmingPool'
    SwimmingPool.create(:name => 'Dan Pool', :tldc_id => 97)
  2. Retrieve it through root class
    f = Facility.last
  3. Save a change
    f.name = 'tits'
    f.save
  4. BOOM
    Our previous SwimmingPool now becomes a Facility type.

This was because when we fetch through the root we have no attributes from the child so it didn't update the child type when we saved the name change. The first run through the save that saves the root, chooses the type from the classname.

I have fixed this and it's in my fork.

Views aren't dumped into schema

When you create a view it doesn't create any migration code in the schema. If you rolback/reset you need to delete your schema.

Using the rails_sql_view gem fixes this as it dumps the schema correctly.

I used my fork of the rails_sql_view gem

s.add_dependency('rails_sql_views') #needs the 'rails_sql_views', :git => 'git://github.com/morgz/rails_sql_views.git' fork. Set this in your apps bundle

Don't know how to properly add :git => to the s.add_dependency line...

My fork has this.

parent.save failing

Not sure what's going on here...
I have a simple Patient < User setup. Could be handled by STI, but I wanted to get acquainted with MTI. After correctly implementing citier (I'm pretty sure), nothing happens--the save fails. When creating a new Patient in rails c, I tried using p.save! to get a full error report:

p.save!
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'view_patients.email' in 'where clause': SELECT 1 FROM users WHERE (view_patients.email = BINARY '[email protected]') LIMIT 1

This occurs on line 19 of file 'child_instance_methods.rb' of the plugin in citier-0.1.11/lib/citier. When it tries to save the parent part of the instance into the Users table it tries to look at the view_patients file within the query. I can't pinpoint where the SQL is being generated in the plug in, and I doubt I could change it if I knew. Unfortunately there is no support forum for citer outside of these 4 Issues on the github page, so I'm coming here to try and figure out what's going on.

This may be an issue with mysql2 in particular--in browsing the resolved issues I noticed you hadn't dealt with it much.

new_record? is not being properly overridden in core_ext.rb

Peter,

The way your overriding the @new_record in in your is_new_record method is not correctly causing it to take. When the @object.save method is called from the controller, it returns successfully, but on the next line if i call @object.new_record? it still returns true, even though @object.id is non-nil. From what I can tell, the core code may have changed, but right now in rails 3.0.x, the new_record? method is located in ActiveRecord::Persistence class and the @new_record in there is not getting set when you override it in ActiveRecord::Base.

As a workaround, I've added a method to your core_ext called is_new_record? which returns the @new_record variable that your setting and that seems to work correctly even after I've bubbled up to the controller again, but this is clearly a hack as the core new_record? method should be transparent at the controller level.

I tried a number of different approaches without luck and I've run out of time to continue messing with it. The workaround is doing suitably for me for the time being so I'm moving ahead with that for now.

code excerpt below:

... def is_new_record(state)
@new_record = state
end

ryanchuleff

new_record? method is not getting set, because at no point do we actually persist

self to the database. The method above should be overriding that persistence layer

but its not. It looks like as of rails 3.0.x the native new_record? method is looking

to the ActiveBase::Persistence module to determine new_record status rather than to the

ActiveRecord::Base class where it previously went. For now, this new method is a hack

workaround to allow the controller methods above to know if we succeeded in saving.

Ideally this should be fixed so that the new_record? method is properly overridden.

def is_new_record?
  @new_record
end

end patch

def self.all(*args)
...

Good luck, and nice job so far on the gem, its working great otherwise!

-Ryan

item.type is deprecated

You use item.type, but it's not available in the latest stable versions of ruby.
citier / lib / citier / child_instance_methods.rb:39 for example
so it raises NoMethodError: undefined method `type'

SQL Query for each row

Is this bugged in that there seems to be a SQL query for each row in the table? This is from the console:

>> Fruit.all
  Banana Load (0.6ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana')
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 1]]
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 1
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 2]]
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 2
  Banana Load (0.8ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 3]]
  Banana Load (0.8ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 3
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 4]]
  Banana Load (0.8ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 4
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 5]]
  Banana Load (0.4ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 5
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 6]]
  Banana Load (0.8ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 6
  Banana Load (0.5ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 7]]
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 7
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 8]]
  Banana Load (0.8ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 8
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 9]]
  Banana Load (0.4ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 9
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 10]]
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 10
  Banana Load (1.7ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 11]]
  Banana Load (0.4ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 11
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 12]]
  Banana Load (0.3ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 12
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 13]]
  Banana Load (0.4ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 13
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 14]]
  Banana Load (0.2ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 14
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 15]]
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 15
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = ? LIMIT 1  [["id", 16]]
  Banana Load (0.1ms)  SELECT "view_bananas".* FROM "view_bananas" WHERE "view_bananas"."type" IN ('Banana') AND "view_bananas"."id" = 16

migrations/create_fruits.rb

class CreateFruits < ActiveRecord::Migration
  def change
    create_table :fruits do |t|
      t.integer :calories
      t.string :type
      t.timestamps
    end
  end
end

migrations/create_bananas.rb

class CreateBananas < ActiveRecord::Migration
  def up
    create_table :bananas do |t|
      t.string :color
    end

    create_citier_view(Banana)
  end

  def down
    drop_citier_view(Banana)
    drop_table :bananas
  end
end

models/fruit.rb

class Fruit < ActiveRecord::Base
  acts_as_citier
end

models/banana.rb

class Banana < Fruit
  acts_as_citier
end

Environment

Rails 3.1

How to use the latest Citier code?

Hi all,

I'd like to get access to the latest Citier code as I need some of the fixes that have already been committed. I'm using Bundler and when I include Citier in my Gemfile I get version 0.1.12 from rubygems (http://rubygems.org/gems/citier) but thats from April 2011.

So I forked citier on GitHub, then ran:

  1. gem build citier.gemspec
  2. gem install ./citier-0.1.13.gem

I then put a direct dependency on my locally built gem by adding the following the dependency to the Gemfile in my project:
gem 'citier', '0.1.13', :path => '../citier/'

After blowing away my old SQLLite3 dev database, I was able to create another using:
bundle exec rake db:create

But when I try and run db:migrate, I get the following error as soon as it runs a migration including a citier subtype.

undefined method `create_view' for #ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00000104300278

My code works fine if I pull in the 0.1.12 citier gem rom RubyGems, but I need the latest code as I'm running into the same problem described in issue 29 except against SQL server db. (#29)

So, could someone please explain the easiest way to run against the latest code?

Save on root class creates new child model based on :type

Hi,
Here's a feature request:

Quick version... I'd like to be able to create a new child model with:

child = Parent.new( :type => 'Child', :child_specific_param => 'something', .... rest of params ...)

I could create multiple controllers or have one controller with a switch statement in the create action to instantiate different class objects based on the type parameter. However, I think it would be cleaners and DRYer to implement this functionality in the gem itself.

I have a forked version I'll play around with but I'm still new to the code so if someone else wants to have a go at it, kudos.

Thanks,
John

Auto-Inc keys could get out of alignment

Example Scenario

Assume that Entity and User have auto incremented id fields
Entity - root
User - child of entity
Administrator - child of entity

Add a user, Entity id+1 = 1, User id +1 = 1
Add an administrator, Entity id+1 = 2, Administrator id+1 = 1

The id's are now out of sync and so cannot be traversed back up the tree.

Current Fix/Method

Currently this has been fixed by forcing the child to take the parent's ID on save. Since a child always requires a prent, and a parent can have many children, the parent auto ID will always be > child auto ID. As a result, there will never be conflicts. However, this is not completely optimal as the child ID can sometimes jump, not causing errors, but meaning in the child table the id's are not always in sequence (1, 2, skip, 5 not 1, 2, 3).

Desired (better) solution

My long term plan for this is to have a much nicer implementation where instead of using 'type' you simply have a parent id for child entities. This is how I have implemented it in python (pylons, django) and PHP before and it feels a lot nicer and like a more rounded and complete implementation.

e.g:

class Entity < ActiveRecord::Base
  acts_as_citier
  validates_presence_of :name
end
class User < Entity
  #Somehow citier would also create an entity_id field as well during the 'User' migration.
  #Since it will definitely be needed if doing this style of inheritance, the user shouldn't have 
  # to handle this themselves, citier *should* do it for them. Will investigate further after exams.

  acts_as_citier
  validates_presence_of :title
end

This would mean that when selecting all entities, the parent id could be used to link back through the hierarchies.

Potential Issues

At the moment, citier is really nice for determining classes on the fly. For example, if I have 6 types of notification all with slightly unique fields, I could still do Notification.all() and would get a list of all notifications, regardless of type, but still the correct class and with their own unique fields. It does this by examining the 'type' of the table of the root entity, and proceeding to define the class for the entry accordingly.

Using the id method, there is no way of knowing what class an entry is from a parent. User.all() would be fine, but Entities.all() could encounter issues.

Solutions to these

  1. Keep the type field, after all, it is only one field and while not needed when traversing up the hierarchy, it's really nice to be able to traverse down and this is a simple way of allowing it.
  2. Query tables of all children and see which one's have the root entry as a parent. Then for each of those which are prents themselves, repeat recursively, then when you get to a point where the table you are looking at is a child with no children itself, look at the table name and create an instance of the related class using the attributes you have collected while traversing the relationships.
  3. Another way would be to keep a view for the root class (currently not done) which does something similar to 1, displaying the table name of the child class alongside the entries, this wouldn't entail a field for the table, but would still require a lot of queries for investigating where a hierarchy stops (finding final children).

Conclusion

Personally I am inclined to leave the type field in and force the ID down the hierarchy dwn the chain for now, while it doesn't seem to make sense if all you want to do is do User.all(), if you want to be able to process all children of a particular model together (different user types for instance) it is very useful.

If anyone has any alternatives, I would love to hear them. If I implement a nicer method in future, I will just allow auto-increment to take over again alongside the fix and you will be sorted from then on.

Hope that made sense :)

Pete

Docs need some updating

First

Should note this requirement for rails_sql_views for sqlite3 in the User Guid after step 1

See also http://activewarehouse.rubyforge.org/rails_sql_views/

In your Gemfile add:
gem 'rails_sql_views', :git => 'https://github.com/morgz/rails_sql_views.git'

In your config/environment.rb add:
require 'rails_sql_views'

Second

The field field :inheritance_column_name seems to actually be :type for a citier parent class's inheritance column as seen in this example:

class CreateMedias < ActiveRecord::Migration
  def self.up
    create_table :medias do |t|
      t.string :inheritance_column_name
      t.string :name
      t.integer :price
    end
  end
  def self.down
    drop_table :medias
  end
end

schema.db dump create_views aren't order by class inheritance

I'm not 100% sure about this as a bug in general or on my end but here's the situation:

It looks like the schema.rb "create_view" entries are populated alphabetically. This is a problem when a child model references a parent model who's view is not yet defined.

Here's my example:
I have data model for literature citations in which class EPeriodicalCitation < PeridoicalCitation

create_view "view_e_periodical_citations", "select `view_periodical_citations`.`id` AS `id` (REST OF LINE REMOVED) do |v|
  # column definition
end

create_view "view_periodical_citations", "select `view_authored_citations`.`id` (REST OF LINE REMOVED) do |v|
  # column definition
end

Since 'view_e_periodical_citations' is < 'view_periodical_citations' it gets output first and causes an ERROR when rake db:test:prepare is run.

The simple fix for me was to reorder the schema.rb file so that base views are referenced before child views. I don't know if a general fix based on class hierarchy is easy or difficult but I thought I'd pass along the message.

Implement Testing

Need to implement some testing so changes can be checked to ensure they don't break code functionality.

Redundant pg gem dependecy.

I use SQLIte3. But when I run an application I got the next exception.

/home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/rubygems_integration.rb:143:in `block in replace_gem': pg is not part of the bundle. Add it to Gemfile. (Gem::LoadError)
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb:6:in `<top (required)>'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `block in require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `block in load_dependency'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:640:in `new_constants_in'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `load_dependency'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/citier-0.1.12/lib/citier/sql_adapters.rb:11:in `<top (required)>'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `block in require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `block in load_dependency'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:640:in `new_constants_in'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:223:in `load_dependency'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/activesupport-3.1.0/lib/active_support/dependencies.rb:240:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/citier-0.1.12/lib/citier.rb:24:in `<top (required)>'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:68:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:68:in `block (2 levels) in require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:66:in `each'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:66:in `block in require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:55:in `each'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler/runtime.rb:55:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/bundler-1.0.18/lib/bundler.rb:120:in `require'
    from /home/spotapov/poligon/rails/mti_citier/config/application.rb:13:in `<top (required)>'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.1.0/lib/rails/commands.rb:38:in `require'
    from /home/spotapov/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.1.0/lib/rails/commands.rb:38:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

If I add gem 'pg' to Gemfile and do bundle install it works fine. But it's redendant when I do not use PostgreSQL.

Environment

  • rails 3.1 (stable)
  • ruby 1.9.2p180

superclass mismatch for class SQLiteAdapter

I'm using jruby on rails. When I installed Citier gem, I started getting the above referenced superclass mismatch for class SQLiteAdapter.

A gist of the full trace is available here: https://gist.github.com/1547640

It seems like this some sort of name collision, but its a little beyond my knowledge level at this point. Any help would be appreciated.

Doesn't work with mysql2 / MySQL 14?

I was really psyched about this gem and I've been trying hard to get it to work, with no luck. :-\ I followed the documentation on the site (http://peterhamilton.github.com/citier/user_guide.html), and I couldn't get even the simplest example to run. I'm running SUSE Linux, MySQL 14.14, mysql2 gem, Rails 3.0.9, and Ruby 1.9.

First, there were problems with the migrations. The issue was in create_citier_view -- duplicate/ambiguous field names. (It was taking the timestamp fields from the "root" table and the "child" table.) I had it ignore those fields, and managed to get it to build the tables.

Then, I tried running it, and I get this error: "ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'type' in 'field list'"

In my example, a "LevelAlphaThing" has a name, and a "LevelBetaThing" inherits from LevelAlphaThing and adds a color.

Here's the code:

_the migrations_

class CreateLevelAlphaThings < ActiveRecord::Migration
def self.up
create_table :level_alpha_things do |t|
t.string :name
t.string :inheritance_column_name
t.timestamps
end
end

def self.down
drop_table :level_alpha_things
end
end

class CreateLevelBetaThings < ActiveRecord::Migration
def self.up
create_table :level_beta_things do |t|
t.string :color
t.timestamps
end
create_citier_view(LevelBetaThing)
end

def self.down
drop_table :level_beta_things
drop_citier_view(LevelBetaThing)
end
end

_the models_

class LevelAlphaThing < ActiveRecord::Base
#:name, :inheritance_column_name
acts_as_citier
end

class LevelBetaThing < LevelAlphaThing
#:color
acts_as_citier
end

_the test_

require 'test_helper'

class LevelBetaThingTest < ActiveSupport::TestCase
def test_should_be_valid
x = LevelBetaThing.new
x.name = "Fire"
x.color = "red"
assert x.save
end
end

_quick patch to core_ext.rb so it doesn't cause ambiguities / doesn't try to create duplicate fields_

def create_citier_view(theclass)
...
parent_columns = theclass.superclass.column_names.select{ |c| c != "id" && c != "created_at" && c != "updated_at" }
...
end

I really hope I'm doing something wrong; I'd love to get this working!

Thanks!

Opportunity to reduce number of SQL queries

In the ChildInstanceMethods::save method, each inherited object's type is set via a SQL UPDATE query after all other data has already been INSERTed. So in a simple example where Driver extends Person, there are two extra UPDATEs that occur after each of these tables has data INSERTed.

It seems that the type could (arguably should) be included in the original INSERT statement since we already know what the type value is, and it's not being generated as a result of the inserts.

Additionally, this would allow non-transactional databases (e.g.: MySQL's MyISAM) to define the type column as non-nullable, which would improve general query performance against the table.

The citier view won't reflect further changes on the tables

The citier view won't reflect further changes on the tables. So if you add a new column on the superclass or base class table, the views won't reflect this change.
Here are some options we have to workaround this:

  • Call create_citier_view / drop_citier_view after any change on the related tables. Even then, you will have to call ClassName.reset_column_information() to clear the rails columns cache
  • Create an update_citier_view method and call it after all related tables change.
  • Find a way to hookup the tables changes and fire some update method to sync the citrier view
  • Make the citier view dynamic, but I really don't know how to do this :P

What you think about it guys?

:counter_cache => true support

I'm trying to enable counter cache for a citier model's association like so

class Comment
  belongs_to :media, :counter_cache => true
end

and also adding a comments_count column for the root model Media. However, there doesn't seem to be support for the counter cache since I'm getting thrown this error

ActiveRecord::StatementInvalid: PGError: ERROR:  cannot update a view
HINT:  You need an unconditional ON UPDATE DO INSTEAD rule.

Anyone know of a workaround?

Child doesn't get id set properly on-create

I am using MySQL2 and the CITIER gem as currently available through git. I have a Status root-class and a LinkStatus subclass.

When I try to save a LinkStatus, it already has its id set to 0, causing future database-actions to fail:

pry(main)> link_status = LinkStatus.new
=> #<LinkStatus id: 0, type: "LinkStatus", [...]>
[set link_status properties]
pry(main)> link_status.id
=> 0
pry(main)> link_status.save
citier -> SAVING LinkStatus
citier -> Attributes for LinkStatus: {"link_url"=>"http://www.google.com"}
WARNING: Can't mass-assign protected attributes: id, type
   (0.1ms)  BEGIN
  SQL (0.2ms)  INSERT INTO `statuses` (`id`, `type`, [...]) VALUES (0, 'LinkStatus', [...])
   (0.2ms)  UPDATE statuses SET type = 'LinkStatus' WHERE id = 0
citier -> UPDATE statuses SET type = 'LinkStatus' WHERE id = 0
   (0.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (33.1ms)  INSERT INTO `link_statuses` (`id`, `link_url`) VALUES (0, 'http://www.google.com')
   (0.4ms)  COMMIT
citier -> SQL : UPDATE statuses SET type = 'LinkStatus' WHERE id = 0
   (0.2ms)  UPDATE statuses SET type = 'LinkStatus' WHERE id = 0
=> true
pry(main)> link_status.id
=> 0

As you can see, before saving id is equal to 0 instead of nil, causing it not be overwritten by the actual value after the root-object is INSERTed into the statuses table.
Because of this, the next INSERT query into link_statuses won't have same id as it's parent, causing the link_statuses row not to be linked to the statuses row.

It also causes all future database-actions on link_status to fail, as it goes looking for a record with id = 0.

The cause of this issue seems to be that the view_link_statuses.id column has 0 as its default value.

(For anyone else having this problem, I have temporarily fixed it by adding before_save { self.id = nil unless self.persisted? } to my Status model.)

Remove created_at and updated_at fields from root model

Pete,

So I added a citier_debug that output the parent_attributes in ChildInstanceMethods#save. It confirmed that the parent attribute list includes those protected fields (created_at, updated_at)

Here is that part of the output:

citier -> IN ChildInstanceMethods#save
citier -> Attributes for Parent: {"created_at"=>nil, "name"=>nil, "price"=>nil, "type"=>"Book", "updated_at"=>nil}
citier -> Attributes for Book: {"author"=>"BBB", "title"=>"AAA"}

Gonna review code to see where the best place to exclude might be. Trying to set those will usually cause a failure, given their "magical" nature.

Dan

Unable to call destroy on Citier subtype

When I call destroy on a book in the citier demo app, I get the following error:

undefined local variable or method `bind_values' for #ActiveRecord::Relation:0x00000100ea89f8

This appears to originate from the following method in the Relation class.

alias_method :relation_delete_all, :delete_all
def delete_all(conditions = nil)
  return relation_delete_all(conditions) if [email protected]_as_citier?

  return relation_delete_all(conditions) if conditions

  deleted = true
  ids = nil
  c = @klass

  bind_values.each do |bind_value|
    if bind_value[0].name == "id"
      ids = bind_value[1]
      break
    end
  end
  ids ||= where_values_hash["id"] || where_values_hash[:id]
  where_hash = ids ? { :id => ids } : nil

  deleted &= c.base_class.where(where_hash).relation_delete_all
  while c.superclass != ActiveRecord::Base
    if c.const_defined?(:Writable)
      citier_debug("Deleting back up hierarchy #{c}")
      deleted &= c::Writable.where(where_hash).delete_all
    end
    c = c.superclass
  end

  deleted
end

Build associations with model type

I was trying to figure out how to use CITIER with accepts_nested_attributes_for such that I could create nested child models dynamically. For example:

List.new( items_attributes => 
  [ { ItemTypeA => { STUFF },
    { ItemTypeB => { STUFF },
 ])

I found some information from: http://stackoverflow.com/questions/2553931/can-nested-attributes-be-used-in-combination-with-inheritance (see response 1).

This lead me to a monkey-patch with the following:

class ActiveRecord::Reflection::AssociationReflection < ActiveRecord::Reflection::MacroReflection
  def build_association(*options, &block)
    @original_build_association_called = true
    if options.first.is_a?(Hash) and options.first[:model_type].presence
      build_klass = options.first[:model_type].to_s.constantize     # raises NameError
      build_klass.new(*options)                               # raises ActiveRecord::AssociationTypeMismatch
    else
      klass.new(*options, &block)
    end
  end
end

Now I can pass the :model_type param to create specific child instances when creating nested_attributes or from List.build(:model_type => ItemTypeA).

I hope that this helps anyone trying to do the same thing.

:dependent => :destroy

Doesn't seem to work for model associations on citier child models

Any model association on a citier child model will not be destroy when the parent is destroyed

ie

has_many :promotions, :dependent => :destroy

is on a citier model

promotions will hang around even after the citier model is wacked

Rails Scopes don't invoke Find method override

The issue is basically that if I do a Parent.find(my_id) it will return the fully parsed object with parent and child attributes included, but if I declare a scope called foo on the Parent and define it as "where("type= ? and vendor_id = ?", @widget.type, @vendor.id)" and then call Parent.foo, it returns only the parents attributes.

APIDock acknowledges as much here: http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

It doesn't seem to say though what it is using instead so its hard to say where it needs to be overridden. I have a sinking suspicion that because each declared scope is instantiated as a method on the ActiveRecord class, you would have to override them individually, or at least force a call out of some sort to handle the override in a generic fashion.

Anyway, there is a workaround by doing Parent.find(:all, :conditions => "(type=#{@widget.type} and vendor_id=#{@vendor.id})") which seems to work just find. It just forces you to define the logic in each controller instead of obfuscating in the model.

Let me know if you have any ideas.

-Ryan

undefined method `create_view'

When trying to follow something similar to example with MySQL version 5.5.17 with Rails 3.1 I get the following error

== CreateBooks: migrating ====================================================
-- create_table(:books)
-> 0.0073s
citier -> Root Class
citier -> table_name -> master_objects
citier -> Non Root Class
citier -> table_name -> books
citier -> tablename (view) -> view_books
-- create_view("view_books", "SELECT master_objects.id, info,type,created_at,updated_at,title,created_at,updated_at FROM master_objects, books WHERE master_objects.id = books.id")
rake aborted!
An error has occurred, all later migrations canceled:

undefined method `create_view' for #CreateBooks:0x007f8e3ca00c40

View_xxx does not exist | PostgreSQL

Hi,

First, Thank you for this great tool !

I met the following problem when i try to seed my database :

citier -> Root Class
citier -> table_name -> triggers
citier -> Non Root Class
citier -> table_name -> soaps
citier -> tablename (view) -> view_soaps
rake aborted!
PG::Error: ERROR:  relation "view_soaps" does not exist
LINE 4:              WHERE a.attrelid = '"view_soaps"'::regclass
                                        ^
:             SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
             WHERE a.attrelid = '"view_soaps"'::regclass
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum

Tasks: TOP => db:seed
(See full trace by running task with --trace)

My schema.rb file :

ActiveRecord::Schema.define(:version => 20120411115108) do

  create_table "soap", :force => true do |t|
    t.string   "wsdl_filename"
    t.datetime "created_at",    :null => false
    t.datetime "updated_at",    :null => false
  end

  create_table "triggers", :force => true do |t|
    t.string   "type", :null => false
    t.string   "name"
    t.string   "description"
    t.string   "host"
    t.integer  "port"
    t.integer  "timeout",     :default => 60
    t.datetime "created_at",                  :null => false
    t.datetime "updated_at",                  :null => false
  end

end

My migration file :

class CreateSoaps < ActiveRecord::Migration
  def self.up
    create_table :soaps do |t|
      t.string :wsdl_filename
      t.timestamps
    end
    create_citier_view(Soap)
  end

  def self.down
    drop_citier_view(Soap)
    drop_table :soaps
  end
end

My environment configuration :

About your application's environment
Ruby version              1.8.7 (x86_64-linux)
RubyGems version          1.3.7
Rack version              1.4
Rails version             3.2.3
JavaScript Runtime        therubyracer (V8)
Active Record version     3.2.3
Action Pack version       3.2.3
Active Resource version   3.2.3
Action Mailer version     3.2.3
Active Support version    3.2.3
Middleware                ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x7f173b6ffaa0>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport
Application root          /var/www/html/Komets
Environment               development
Database adapter          postgresql

Could you help me to fix this problem ?

Notes : All models are well write as your documentation.

Best Regards,

relation_methods.rb delete_all strange behavior

ruby-1.9.3-rc1, rails 3.2.1
Department < Group

ruby-1.9.3-rc1 :003 > Department.delete_all
citier -> ids1:
citier -> ids2:
citier -> where_hash: , c.base_class:Group
SQL (1.5ms) DELETE FROM groups
citier -> Deleting back up hierarchy Department
SQL (0.5ms) DELETE FROM departments
=> true


1: It seems subclass.delete_all delete all records of the base_class.

2: "return relation_delete_all(conditions) if conditions" could leave out records in parent_class and parent_parent_class ...

3: It returns a boolean and if there is a condition it returns a deleted records count.

stack level too deep (when changing model inheritance)

I'm trying to use this gem to convert some models that already exist in the following way. So far this works:

Created models:

Attachable < ActiveRecord::Base
Link < Attachable

I added acts_as_citier to both models, created and ran the migrations with no problem. Views are created as well. Now if I try to do this to an existing model:

Before:

SpecialLink < ActiveRecord::Base

After:

SpecialLink < Link

and then I try to run any command like rails g migration or rails console, I get:

script/rails:6: stack level too deep (SystemStackError)

Unfortunately, this doesn't tell me a whole lot beyond a suspicion that I have an infinite loop somewhere. Does anyone know what might be causing this? Or have an idea of where I can look or get more information about the issue? It would not be a good solution for me to have to create the models again from scratch to work with citier...

Citier will not allow a migration for me.

Hi,
I can't get Citier to let me migrate. When I run

bundle exec rake db:migrate --trace

I get

-- create_view("view_authors", "SELECT people.id, type,first_name,middle_name,last_name,suffix,birth_date,gender,self_perms,other_perms,created_at,updated_at,admin FROM people, authors WHERE p
eople.id = authors.id")
rake aborted!
An error has occurred, this and all later migrations canceled:

undefined method `create_view' for #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x007ff46b484c40>

I thought this

undefined method `create_view' 

Might be caused by not having rails_sql_views in my gemfile, but when I add it I get a different error. That error is

undefined method `tables' for class `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'

This occurs even though I don't use PostgreSQL at all. This second issue is also documented at this address
https://github.com/aeden/rails_sql_views/issues/7

Getting citier to work with SQL server

Hi,

I'm trying to get Citier working with SQL server but I'm having an issue when trying to insert a subclass into the database.

When using the Citier sample app for example, an insert is made into the database for the subtype (the 'book' in the sample app) giving it an id of 1, however when the insert is made into the root entity (the 'product in the sample app) it is given an id of 19.

The view is therefore unable to link the two entities.

The stack trace below shows this happening.

Could anyone point me in the right direction of where I should look to fix this issue. Even just a class name or some ideas would be great.

Below is the log showing the behaviour described above: (The only detail missing is the fact that the book was created with an id of 1)

Started POST "/books" for 127.0.0.1 at 2012-01-03 16:12:46 +0200
Processing by BooksController#create as HTML
Parameters: {"utf8"=>"โœ“", "authenticity_token"=>"ET5lKz/lt+/wOPv1vdHMWT/SKZEQqFqq9sJQ+hez698=", "book"=>{"title"=>"qwe", "author"=>"qwe"}, "commit"=>"Create Book"}
EXECUTE (33.8ms) BEGIN TRANSACTION
AREL (73.6ms) INSERT INTO [products]([type], [name], [price], [created_at], [updated_at]) VALUES (N'Book', N'Title of book: qwe', NULL, '2012-01-03 14:12:46.740', '2012-01-03 14:12:46.740')
EXECUTE (46.2ms) COMMIT TRANSACTION
EXECUTE (33.4ms) BEGIN TRANSACTION
IDENTITY_INSERT (70.0ms) SET IDENTITY_INSERT [books] ON
AREL (36.7ms) INSERT INTO [books]([title], [author], [id]) VALUES (N'qwe', N'qwe', 19)
IDENTITY_INSERT (34.7ms) SET IDENTITY_INSERT [books] OFF
EXECUTE (35.0ms) COMMIT TRANSACTION
Redirected to http://localhost:3000/books/19

How to suppress the citier logging

How do I suppress this output (in the server log and before starting up rails console)?

citier -> tablename (view) ->
citier -> Non Root Class
citier -> table_name ->
citier -> tablename (view) ->

Uninitialized Constant error during Rake

I have been using citier on a project of mine and have run into a problem.

When I run rake db:migrate to on my migration files, rake is aborting. From what I can tell, its having problems with the create_citier_view( ) command.

As I look at the --trace version of the rake command, I see the table being created, but then it throws the following error:

rake aborted!
uninitialized constant Wager::Writable /[path]/lib/citier/core._ext.rb:33:in 'create_citier_view'.

I went and looked at the source, but really can't figure out what the issue is. I believe I have everything configured per your user guide, so I'm not sure what the problem is.

RSpec testing db:schema:load failure

It seems to be failing to load the schema when performing testing via rspec. Doing rake spec I get

** Execute db:schema:load
rake aborted!
SQLite3::SQLException: near "CREATE": syntax error: CREATE VIEW "view_fruit_bananas" AS CREATE VIEW "view_fruit_bananas" AS SELECT ...

It appears to be repeating the CREATE VIEW "view_fruit_bananas" statement twice. Any suggestions? I am using DouweM's fork @ https://github.com/DouweM/citier.git. Thanks!

Is this gem still being developed?

Two level inheritance => fetch issue

Hi,

I'm testing the gem so I've created a small project with this :

class Party < ActiveRecord:Base
acts_as_citier
end

class Person < Party
acts_as_citier
end

class Structure < Person
acts_as_citier
end

I wrote the migrations like in the example.

So the test is :

  • Open a console
  • Insert a Structure in the database => OK I've got everything in every table
  • I do Party.all => It fetches the Structure
  • Close the console
  • Open a new console
  • I do Person.all => nothing is fetched
  • I do Party.all => It fetches the Structure as usual
  • I do Person.all => Now it fetches the Structure

Weird ?

Thanks for help

Julien

Issue creating from child class

I have a very simple multiple-table inheritance setup with a Song and a Media class. Song inherits from Media.

However, when I try to do Song.create! it gives me the following error:
ERROR: null value in column "id" violates not-null constraint

I debugged it down to citier telling media to create with a NULL id, which postgres doesn't seem to like. It (postgres) requires the id field to be left out.

I've created a very simple fix: https://github.com/Rexly/citier/commit/5db02535f5f9ff16248736d617358aaa8bc11dd7

SQL Syntax Error Creating Book Object in Sample App

Peter,

I know you are in the throws of finals, but I thought I would bring this to your attention. First, a little background. I am hoping to use your MTI solution in a service request portal.

I downloaded your sample app--modifying the database.yml and Gemfile to use mysql2 instead of sqlite3 -- since I prefer that.

I was able to create a Product without any difficulty.

However, when I went to create a book, by going to localhost:3000/books/ and then clicking on New book, an exception was caught on the create action--invalid SQL syntax.

Here is the log

Started GET "/books" for 127.0.0.1 at 2011-05-02 16:38:59 -0400
Processing by BooksController#index as HTML
Book Load (0.3ms) SELECT view_books.* FROM view_books WHERE view_books.type = 'Book'
Rendered books/index.html.erb within layouts/application (1.9ms)
Completed 200 OK in 20ms (Views: 5.3ms | ActiveRecord: 0.3ms)
citier -> Root Class
citier -> table_name -> products
citier -> Non Root Class
citier -> table_name -> books
citier -> tablename (view) -> view_books

Started GET "/books/new" for 127.0.0.1 at 2011-05-02 16:39:06 -0400
Processing by BooksController#new as HTML
Rendered books/_form.html.erb (31.4ms)
Rendered books/new.html.erb within layouts/application (34.9ms)
Completed 200 OK in 55ms (Views: 38.0ms | ActiveRecord: 0.0ms)
citier -> Root Class
citier -> table_name -> products
citier -> Non Root Class
citier -> table_name -> books
citier -> tablename (view) -> view_books
citier -> Attributes for Book: {"author"=>"John Smith", "title"=>"A New Book"}
citier -> Class (Product) could not be saved
citier -> SQL : UPDATE products SET type = 'Book' WHERE id =

Started POST "/books" for 127.0.0.1 at 2011-05-02 16:39:19 -0400
Processing by BooksController#create as HTML
Parameters: {"utf8"=>"โœ“", "authenticity_token"=>"60LGRi8rWdDfIr4kaiIjLIyBYyWSN+7Wmsz4ykX2t4c=", "book"=>{"title"=>"A New Book", "author"=>"John Smith"}, "commit"=>"Create Book"}
WARNING: Can't mass-assign protected attributes: type
SQL (0.2ms) BEGIN
SQL (0.4ms) ROLLBACK
SQL (0.4ms) UPDATE products SET type = 'Book' WHERE id =
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1: UPDATE products SET type = 'Book' WHERE id =
Completed in 26ms

ActiveRecord::StatementInvalid (Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1: UPDATE products SET type = 'Book' WHERE id = ):
app/controllers/books_controller.rb:46:in block in create' app/controllers/books_controller.rb:45:increate'

Rendered vendor/ruby/1.9.1/gems/actionpack-3.0.7/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered vendor/ruby/1.9.1/gems/actionpack-3.0.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (3.2ms)
Rendered vendor/ruby/1.9.1/gems/actionpack-3.0.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (10.0ms)

Any thoughts? Am I using this wrong?

Dan

Cannot add child to Parent's belongs_to

Seems a shame you can't do this:

Site.first.products << Book.first

Where

Site
:has_many :products

Product
:belongs_to :site


And

class Book < Product
acts_as_citier

Throws an error:

ArgumentError: wrong number of arguments (1 for 0)
from /Users/danmorgz/.rvm/gems/ruby-1.9.2-p0@rails3/gems/citier-0.1.12/lib/citier/child_instance_methods.rb:3:in save' from /Users/danmorgz/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activerecord-3.0.9/lib/active_record/associations/has_many_association.rb:66:ininsert_record'
from /Users/danmorgz/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activerecord-3.0.9/lib/active_record/associations/association_collection.rb:136:in block (3 levels) in <<' from /Users/danmorgz/.rvm/gems/ruby-1.9.2-p0@rails3/gems/activerecord-3.0.9/lib/active_record/associations/association_collection.rb:480:inadd_record_to_target_with_callbacks'

Doesn't work with Heroku

This gem seems to require the sqlite3 gem to also be bundled, but Heroku us unable to successfully compile the sqlite3 gem, so the application fails to start with the following trace:

2011-10-16T22:55:43+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/bundler-1.1.rc/lib/bundler/rubygems_integration.rb:143:in `block in replace_gem': sqlite3 is not part of the bundle. Add it to Gemfile. (Gem::LoadError)
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.1.1.rc3/lib/active_record/connection_adapters/sqlite3_adapter.rb:3:in `<top (required)>'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:240:in `require'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:240:in `block in require'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:223:in `block in load_dependency'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:640:in `new_constants_in'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:223:in `load_dependency'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.1.1.rc3/lib/active_support/dependencies.rb:240:in `require'
2011-10-16T22:55:43+00:00 app[web.1]:   from /app/vendor/bundle/ruby/1.9.1/gems/citier-0.1.12/lib/citier/sql_adapters.rb:10:in `<top (required)>'

Is there any way around this?

Project status

Hello,
First let me say that I think this Gem/plugin is amazing compared to the alternatives I've researched. I'm ready to adopt this as my main modeling foundation and am happy to debug and possibly contribute to the project. The web application I'm developing has a 3-4 month development window so this project should also mature it that time. I do have a couple quick questions:

  1. Is anyone aware of any "gotchas" in terms of persistance, validation or associations?
  2. What is the state of the primary maintainer? I've seen him MIA from the commits and issues for the last month or so. I've pulled from DouweM in the meantime and it seems other are continuing to push development which is great.
  3. Any update on the testing front?
  4. Any predicted compatibility issues with new version of rails? This Gem seems to work more on the DB level by creating the views.

Thanks in advance,
John

Doesn't work with polymorphic relationships

Trying to use this in conjunction with a polymorphic association from my base class, but it's trying to execute some defunct SQL.

My classes:

class Activity < ActiveRecord::Base
  acts_as_citier

  belongs_to :profile
  belongs_to :entity, :polymorphic => true

  validates :profile_id, :presence => true
  validates :entity_type, :presence => true
  validates :entity_id, :presence => true, :uniqueness => {:scope => [:entity_type, :entity_id]}
end

class Activity::SetProfilePhoto < Activity
  acts_as_citier
end

And my migrations:

class CreateActivities < ActiveRecord::Migration
  def self.up
    create_table :activities do |t|
      t.string :type, :null => false
      t.integer :profile_id, :null => false
      t.string :entity_type, :null => false
      t.integer :entity_id, :null => false
      t.timestamps
    end

    add_index :activities, [:profile_id, :entity_type, :entity_id]
  end

  def self.down
    drop_table :activities
  end
end

class CreateActivityUploadPhotos < ActiveRecord::Migration
  def self.up
    create_table :activity_upload_photos do |t|
      # nothing extra for now, will add more fields later
    end
    create_citier_view Activity::UploadPhoto
  end

  def self.down
    drop_citier_view Activity::UploadPhoto
    drop_table :activity_upload_photos
  end
end

And finally, the class where I'm attempting to create the activity. (Everything else in this class works prior to using this, so I've just included the relevant code.)

class Photo < ActiveRecord::Base
  belongs_to :profile
  after_create :create_activity

  def create_activity
    Activity::UploadPhoto.create!(:profile => self.profile, :entity => self)
  end
end

When the Activity::UploadPhoto::create! method is called, it generates the following SQL errors/queries:

Mysql::Error: Unknown column 'view_activity_upload_photos.entity_id' in 'where clause': SELECT activities.id FROM activities WHERE activities.entity_type = 'Photo' AND activities.entity_id = 1 AND (view_activity_upload_photos.entity_id = 1) LIMIT 1

Can't destroy record

I am using MySQL2 and the CITIER gem as currently available through git. I have a Status root-class and a LinkStatus subclass.

When I try to destroy either a Status or any LinkStatus, it doesn't work:

pry(main)> LinkStatus.last.destroy
citier -> Non Root Class
citier -> table_name -> link_statuses
citier -> tablename (view) -> view_link_statuses
  LinkStatus Load (3.3ms)  SELECT `view_link_statuses`.* FROM `view_link_statuses` WHERE `view_link_statuses`.`type` IN ('LinkStatus') ORDER BY `view_link_statuses`.`id` DESC LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.2ms)  DELETE FROM `statuses` WHERE `statuses`.`id` IS NULL
citier -> Deleting back up hierarchy LinkStatus
  SQL (0.1ms)  DELETE FROM `link_statuses` WHERE `link_statuses`.`id` IS NULL
   (0.1ms)  COMMIT
=> #<LinkStatus id: 2, [...]>

pry(main)> Status.last.destroy
  Status Load (0.4ms)  SELECT `statuses`.* FROM `statuses` ORDER BY `statuses`.`id` DESC LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.2ms)  DELETE FROM `statuses` WHERE `statuses`.`id` IS NULL
   (0.1ms)  COMMIT
=> #<Status id: 7, [...]>

As you can see, the executed DELETE query looks for a record with id IS NULL, instead of id = 2 and id = 7 respectively.

It could very well be that this isn't a bug in CITIER and that I'm doing something wrong, but I couldn't find a better place to post this.

Error when validating Authlogic child model

When using Authlogic 3.0.3 with CITIER and testing for valid? on a child model I'm seeing:

SQLite3::SQLException: no such column: view_grown_ups.persistence_token: SELECT 1 FROM "users" WHERE ("view_grown_ups"."persistence_token" = '284db4af8fe7c243b30130b952a285c2ddd4344eedaf9c018e49487a2b836428b66288c027218a43232ed5e780cdadb3a41ca53fd94c5bda1a24942be00f0dfa') LIMIT 1

Simple validations don't work

class Product < ActiveRecord::Base
acts_as_citier
validates :name, :uniqueness => true

class Dictionary < Book
acts_as_citier

Product.create!(:name -> 'a book')
Dictionary.create!(:name => 'a book')

ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'view_dictionaries.name' in 'where clause': SELECT 1 FROM products WHERE (view_dictionaries.name = BINARY 'a book') LIMIT 1

Weirdness. Why does loading the product effect my second identical call to Book?

ruby-1.9.2-p0 > Book.first.respond_to?(:site_id)
citier -> Root Class
citier -> table_name -> products
citier -> Non Root Class
citier -> table_name -> books
citier -> tablename (view) -> view_books
=> false
ruby-1.9.2-p0 > Product.first.respond_to?(:site_id)
=> true
ruby-1.9.2-p0 > Book.first.respond_to?(:site_id)
=> true

Can not modify more than one base table through a join view

I'm still getting this error and I'm really not sure why. I've done all of your standard setup, but when I do save or save! it still causes:

Can not modify more than one base table through a join view

I'm on the latest rails, cities, mysql2

And I'll add that in postgres it complains as well:

ERROR: cannot update a view HINT: You need an unconditional ON UPDATE DO INSTEAD rule. : UPDATE "view_writing_assignments" SET "id" = 12, "heading" = 'This is a new writing assignment', "type" = 'WritingAssignment', "prompt" = 'Will it work? please please please?' WHERE "view_writing_assignments"."type" IN ('WritingAssignment') AND "view_writing_assignments"."id" = 12

Inserting a new inherited class in the middle

What is the exact procedure of writing the migration for inserting a class in the middle of the inheritance structure? Quick Example that follows from the Usage Guide

class BoundedBook < Book
acts_as_citier
end

class Dictionary < BoundedBook
acts_as_citier
end

class CreateBoundedBooks < ActiveRecord::Migration
def self.up
create_table :bounded_books do |t|
t.string :binder
end
drop_citier_view(Dictionary)
create_citier_view(BoundedBook)
create_citier_view(Dictionary)

/**  
 *  SOME CODE TO POPULATE THE bounded_book TABLE
 *  WITH THE :binder DATA FOR EXISTING DICTIONARIES
 **/

end
def self.down
drop_citier_view(BoundedBook)
drop_table :bounded_books
end
end

Whats the code I need to insert? Something like Dictionary.find_each do etc etc etc. Then create new BoundedBook objects? Please help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.