citier 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,10 @@
1
+ Rakefile
2
+ lib/citier.rb
3
+ lib/citier/acts_as_citier.rb
4
+ lib/citier/child_instance_methods.rb
5
+ lib/citier/class_methods.rb
6
+ lib/citier/core_ext.rb
7
+ lib/citier/instance_methods.rb
8
+ lib/citier/root_instance_methods.rb
9
+ lib/citier/sql_adapters.rb
10
+ Manifest
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('citier', '0.1.9') do |p|
6
+ p.description = "CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a solution for single and multiple class table inheritance.
7
+ For full information: http://peterhamilton.github.com/citier/
8
+ For the original version by ALTRABio see www.github.com/altrabio/"
9
+ p.url = "https://github.com/peterhamilton/citier/"
10
+ p.author = "Peter Hamilton, Original Authors - Laurent Buffat, Pierre-Emmanuel Jouve & "
11
+ p.email = "peter@inspiredpixel.net"
12
+ p.ignore_pattern = ["tmp/*", "script/*", 'doc/*']
13
+ p.development_dependencies = []
14
+ end
data/citier.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{citier}
5
+ s.version = "0.1.9"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Peter Hamilton, Originally from Laurent Buffat, Pierre-Emmanuel Jouve"]
9
+ s.date = %q{2011-04-29}
10
+ s.description = %q{CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a solution for single and multiple class table inheritance.
11
+ For full information: http://peterhamilton.github.com/citier/
12
+ For the original version by ALTRABio see www.github.com/altrabio/}
13
+ s.email = %q{peter@inspiredpixel.net}
14
+ s.extra_rdoc_files = ["lib/citier.rb",
15
+ "lib/citier/acts_as_citier.rb",
16
+ "lib/citier/core_ext.rb",
17
+ "lib/citier/class_methods.rb",
18
+ "lib/citier/instance_methods.rb",
19
+ "lib/citier/child_instance_methods.rb",
20
+ "lib/citier/root_instance_methods.rb",
21
+ "lib/citier/sql_adapters.rb"]
22
+ s.files = ["Rakefile",
23
+ "lib/citier.rb",
24
+ "lib/citier/acts_as_citier.rb",
25
+ "lib/citier/core_ext.rb",
26
+ "lib/citier/class_methods.rb",
27
+ "lib/citier/instance_methods.rb",
28
+ "lib/citier/child_instance_methods.rb",
29
+ "lib/citier/root_instance_methods.rb",
30
+ "lib/citier/sql_adapters.rb",
31
+ "Manifest",
32
+ "citier.gemspec"]
33
+ s.homepage = %q{https://github.com/peterhamilton/citier/}
34
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "citier", "--main", "README"]
35
+ s.require_paths = ["lib"]
36
+ s.rubyforge_project = %q{citier}
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = s.description
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+ end
44
+ end
data/lib/citier.rb ADDED
@@ -0,0 +1,27 @@
1
+ CITIER_DEBUGGING = (::Rails.env == 'development')
2
+
3
+ def citier_debug(s)
4
+ if CITIER_DEBUGGING
5
+ puts "citier -> " + s
6
+ end
7
+ end
8
+
9
+ require 'citier/core_ext'
10
+
11
+ # Methods which will be used by the class
12
+ require 'citier/class_methods'
13
+
14
+ # Methods that will be used for the instances of the Non Root Classes
15
+ require 'citier/instance_methods'
16
+
17
+ # Methods that will be used for the instances of the Root Classes
18
+ require 'citier/root_instance_methods'
19
+
20
+ # Methods that will be used for the instances of the Non Root Classes
21
+ require 'citier/child_instance_methods'
22
+
23
+ # Require SQL Adapters
24
+ require 'citier/sql_adapters'
25
+
26
+ #Require acts_as_citier hook
27
+ require 'citier/acts_as_citier'
@@ -0,0 +1,8 @@
1
+ module Citier
2
+ def self.included(base)
3
+ # When a class includes a module the module’s self.included method will be invoked.
4
+ base.send :extend, ClassMethods
5
+ end
6
+ end
7
+
8
+ ActiveRecord::Base.send :include, Citier
@@ -0,0 +1,51 @@
1
+ module ChildInstanceMethods
2
+
3
+ def save
4
+
5
+ #get the attributes of the class which are inherited from it's parent.
6
+ attributes_for_parent = self.attributes.reject{|key,value| !self.class.superclass.column_names.include?(key) }
7
+
8
+ # Get the attributes of the class which are unique to this class and not inherited.
9
+ attributes_for_current = self.attributes.reject{|key,value| self.class.superclass.column_names.include?(key) }
10
+
11
+ citier_debug("Attributes for #{self.class.to_s}: #{attributes_for_current.inspect.to_s}")
12
+
13
+ #create a new instance of the superclass, passing the inherited attributes.
14
+ parent = self.class.superclass.new(attributes_for_parent)
15
+ parent.id = self.id
16
+
17
+ parent.is_new_record(new_record?)
18
+
19
+ parent_saved = parent.save
20
+ self.id = parent.id
21
+
22
+ if(parent_saved==false)
23
+ # Couldn't save parent class
24
+ # TODO: Handle situation where parent class could not be saved
25
+ citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
26
+ end
27
+
28
+ # If there are attributes for the current class (unique & not inherited)
29
+ # and parent(s) saved successfully, save current model
30
+ if(!attributes_for_current.empty? && parent_saved)
31
+ current = self.class::Writable.new(attributes_for_current)
32
+ current.is_new_record(new_record?)
33
+ current_saved = current.save
34
+
35
+ # This is no longer a new record
36
+ is_new_record(false)
37
+
38
+ if(!current_saved)
39
+ citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
40
+ end
41
+ end
42
+
43
+ # Update root class with this 'type'
44
+ sql = "UPDATE #{self.class.root_class.table_name} SET #{self.class.inheritance_column} = '#{self.class.to_s}' WHERE id = #{self.id}"
45
+ citier_debug("SQL : #{sql}")
46
+ self.connection.execute(sql)
47
+ return parent_saved && current_saved
48
+ end
49
+
50
+ include InstanceMethods
51
+ end
@@ -0,0 +1,77 @@
1
+ module ClassMethods
2
+ # any method placed here will apply to classes
3
+
4
+
5
+ def acts_as_citier(options = {})
6
+
7
+ # Option for setting the inheritance columns, default value = 'type'
8
+ db_type_field = (options[:db_type_field] || :type).to_s
9
+
10
+ #:table_name = option for setting the name of the current class table_name, default value = 'tableized(current class name)'
11
+ table_name = (options[:table_name] || self.name.tableize.gsub(/\//,'_')).to_s
12
+
13
+ set_inheritance_column "#{db_type_field}"
14
+
15
+ if(self.superclass!=ActiveRecord::Base)
16
+ # Non root-class
17
+
18
+ citier_debug("Non Root Class")
19
+ citier_debug("table_name -> #{table_name}")
20
+
21
+ # Set up the table which contains ALL attributes we want for this class
22
+ set_table_name "view_#{table_name}"
23
+
24
+ citier_debug("tablename (view) -> #{self.table_name}")
25
+
26
+ # The the Writable. References the write-able table for the class because
27
+ # save operations etc can't take place on the views
28
+ self.const_set("Writable", create_class_writable(self))
29
+
30
+ # Add the functions required for children only
31
+ send :include, ChildInstanceMethods
32
+ else
33
+ # Root class
34
+
35
+ after_save :updatetype
36
+
37
+ citier_debug("Root Class")
38
+
39
+ set_table_name "#{table_name}"
40
+
41
+ citier_debug("table_name -> #{self.table_name}")
42
+
43
+ #returns the root class (the highest inherited class before ActiveRecord)
44
+ def self.root_class
45
+ if(self.superclass!=ActiveRecord::Base)
46
+ self.superclass.root_class
47
+ else
48
+ return self
49
+ end
50
+ end
51
+
52
+ def self.find(*args) #overrides find to get all attributes
53
+ tuples = super
54
+
55
+ # in case of many objects, return an array of them, reloaded to pull in inherited attributes
56
+ return tuples.map{|x| x.reload} if tuples.kind_of?(Array)
57
+
58
+ # in case of only one tuple, return it reloaded.
59
+ # Can't use reload as would loop inifinitely, so do a search by id instead.
60
+ # Probably a nice way of cleaning this a bit
61
+ return tuples.class.where(tuples.class[:id].eq(tuples.id))[0]
62
+ end
63
+
64
+ # Unlike destroy_all it is useful to override this method.
65
+ # In fact destroy_all will explicitly call a destroy method on each object
66
+ # whereas delete_all doesn't and only calls specific SQL requests.
67
+ # To be even more precise call delete_all with special conditions
68
+ def self.delete_all
69
+ #call delete method for each instance of the class
70
+ self.all.each{|o| o.delete }
71
+ end
72
+
73
+ # Add the functions required for root classes only
74
+ send :include, RootInstanceMethods
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,49 @@
1
+ class ActiveRecord::Base
2
+
3
+ def self.[](column_name)
4
+ arel_table[column_name]
5
+ end
6
+
7
+ def is_new_record(state)
8
+ @new_record = state
9
+ end
10
+
11
+ def self.all(*args)
12
+ # For some reason need to override this so it uses my modified find function which reloads each object to pull in all properties.
13
+ return find(:all, *args)
14
+ end
15
+
16
+ def self.create_class_writable(class_reference) #creation of a new class which inherits from ActiveRecord::Base
17
+ Class.new(ActiveRecord::Base) do
18
+ t_name = class_reference.table_name
19
+ t_name = t_name[5..t_name.length]
20
+
21
+ if t_name[0..5] == "view_"
22
+ t_name = t_name[5..t_name.length]
23
+ end
24
+
25
+ # set the name of the table associated to this class
26
+ # this class will be associated to the writable table of the class_reference class
27
+ set_table_name(t_name)
28
+ end
29
+ end
30
+ end
31
+
32
+ def create_citier_view(theclass) #function for creating views for migrations
33
+ self_columns = theclass::Writable.column_names.select{ |c| c != "id" }
34
+ parent_columns = theclass.superclass.column_names.select{ |c| c != "id" }
35
+ columns = parent_columns+self_columns
36
+ self_read_table = theclass.table_name
37
+ self_write_table = theclass::Writable.table_name
38
+ parent_read_table = theclass.superclass.table_name
39
+ sql = "CREATE VIEW #{self_read_table} AS SELECT #{parent_read_table}.id, #{columns.join(',')} FROM #{parent_read_table}, #{self_write_table} WHERE #{parent_read_table}.id = #{self_write_table}.id"
40
+ citier_debug("Creating citier view -> #{sql}")
41
+ theclass.connection.execute sql
42
+ end
43
+
44
+ def drop_citier_view(theclass) #function for dropping views for migrations
45
+ self_read_table = theclass.table_name
46
+ sql = "DROP VIEW #{self_read_table}"
47
+ citier_debug("Dropping citier view -> #{sql}")
48
+ theclass.connection.execute sql
49
+ end
@@ -0,0 +1,30 @@
1
+ module InstanceMethods
2
+
3
+ # Delete the model (and all parents it inherits from if applicable)
4
+ def delete(id = self.id)
5
+ citier_debug("Deleting #{self.class.to_s} with ID #{self.id}")
6
+
7
+ # Delete information stored in the table associated to the class of the object
8
+ # (if there is such a table)
9
+ deleted = true
10
+ c = self.class
11
+ while c.superclass!=ActiveRecord::Base
12
+ citier_debug("Deleting back up hierarchy #{c}")
13
+ deleted &= c::Writable.delete(id)
14
+ c = c.superclass
15
+ end
16
+ deleted &= c.delete(id)
17
+ return deleted
18
+ end
19
+
20
+ def updatetype
21
+ sql = "UPDATE #{self.class.root_class.table_name} SET #{self.class.inheritance_column} = '#{self.class.to_s}' WHERE id = #{self.id}"
22
+ self.connection.execute(sql)
23
+ citier_debug("#{sql}")
24
+ end
25
+
26
+ def destroy
27
+ return self.delete
28
+ end
29
+
30
+ end
@@ -0,0 +1,5 @@
1
+ module RootInstanceMethods
2
+
3
+ include InstanceMethods
4
+
5
+ end
@@ -0,0 +1,131 @@
1
+ #------------------------------------------------------------------------------------------------#
2
+ # #
3
+ # Modifications for SQL Adapters : needed to take views into account #
4
+ # (only SQLite, PostGreSQL & MySQL have been considered) #
5
+ # #
6
+ #------------------------------------------------------------------------------------------------#
7
+
8
+ # SQLite
9
+
10
+ require 'active_record'
11
+ require 'active_record/connection_adapters/sqlite_adapter'
12
+ require 'active_record/connection_adapters/sqlite3_adapter'
13
+ require 'active_record/connection_adapters/postgresql_adapter'
14
+ module ActiveRecord
15
+ module ConnectionAdapters
16
+ class SQLiteAdapter < AbstractAdapter
17
+
18
+ def tables(name = nil)
19
+ sql = <<-SQL
20
+ SELECT name
21
+ FROM sqlite_master
22
+ WHERE (type = 'table' or type='view') AND NOT name = 'sqlite_sequence'
23
+ SQL
24
+ # Modification : the where clause was intially WHERE type = 'table' AND NOT name = 'sqlite_sequence'
25
+ # now it is WHERE (type = 'table' or type='view') AND NOT name = 'sqlite_sequence'
26
+ # this modification is made to consider tables AND VIEWS as tables
27
+
28
+ execute(sql, name).map do |row|
29
+ row['name']
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+
38
+ # PostGreSQL
39
+ module ActiveRecord
40
+ module ConnectionAdapters
41
+ class PostgreSQLAdapter < AbstractAdapter
42
+ def tables(name = nil)
43
+ a=tablesL(name)
44
+ puts("1------>#{a}")
45
+ b=viewsL(name)
46
+ if(b!=[])
47
+ a=a+b
48
+ end
49
+ puts("2------>#{a}")
50
+ return a
51
+ end
52
+
53
+ def tablesL(name = nil)
54
+
55
+ query(<<-SQL, name).map { |row| row[0] }
56
+ SELECT tablename
57
+ FROM pg_tables
58
+ WHERE schemaname = ANY (current_schemas(false))
59
+ SQL
60
+ end
61
+ def viewsL(name = nil)
62
+
63
+ query(<<-SQL, name).map { |row| row[0] }
64
+ SELECT viewname
65
+ FROM pg_views
66
+ WHERE schemaname = ANY (current_schemas(false))
67
+ SQL
68
+ end
69
+
70
+ def table_exists?(name)
71
+ a=table_existsB?(name)
72
+ b=views_existsB?(name)
73
+ puts"T---->#{a}"
74
+ puts"T---->#{b}"
75
+ return a||b
76
+ end
77
+
78
+
79
+ def table_existsB?(name)
80
+ name = name.to_s
81
+ schema, table = name.split('.', 2)
82
+
83
+ unless table # A table was provided without a schema
84
+ table = schema
85
+ schema = nil
86
+ end
87
+
88
+ if name =~ /^"/ # Handle quoted table names
89
+ table = name
90
+ schema = nil
91
+ end
92
+
93
+ query(<<-SQL).first[0].to_i > 0
94
+ SELECT COUNT(*)
95
+ FROM pg_tables
96
+ WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}'
97
+ #{schema ? "AND schemaname = '#{schema}'" : ''}
98
+ SQL
99
+
100
+ end
101
+ def views_existsB?(name)
102
+ name = name.to_s
103
+ schema, table = name.split('.', 2)
104
+
105
+ unless table # A table was provided without a schema
106
+ table = schema
107
+ schema = nil
108
+ end
109
+
110
+ if name =~ /^"/ # Handle quoted table names
111
+ table = name
112
+ schema = nil
113
+ end
114
+
115
+ query(<<-SQL).first[0].to_i > 0
116
+ SELECT COUNT(*)
117
+ FROM pg_views
118
+ WHERE viewname = '#{table.gsub(/(^"|"$)/,'')}'
119
+ #{schema ? "AND schemaname = '#{schema}'" : ''}
120
+ SQL
121
+
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+
128
+
129
+ # MySQL
130
+ # No Modification needed, this essentially comes from the fact that MySQL "show" command
131
+ # lists simultaneously tables & views
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: citier
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 9
10
+ version: 0.1.9
11
+ platform: ruby
12
+ authors:
13
+ - Peter Hamilton, Originally from Laurent Buffat, Pierre-Emmanuel Jouve
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-29 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: |-
22
+ CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a solution for single and multiple class table inheritance.
23
+ For full information: http://peterhamilton.github.com/citier/
24
+ For the original version by ALTRABio see www.github.com/altrabio/
25
+ email: peter@inspiredpixel.net
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - lib/citier.rb
32
+ - lib/citier/acts_as_citier.rb
33
+ - lib/citier/core_ext.rb
34
+ - lib/citier/class_methods.rb
35
+ - lib/citier/instance_methods.rb
36
+ - lib/citier/child_instance_methods.rb
37
+ - lib/citier/root_instance_methods.rb
38
+ - lib/citier/sql_adapters.rb
39
+ files:
40
+ - Rakefile
41
+ - lib/citier.rb
42
+ - lib/citier/acts_as_citier.rb
43
+ - lib/citier/core_ext.rb
44
+ - lib/citier/class_methods.rb
45
+ - lib/citier/instance_methods.rb
46
+ - lib/citier/child_instance_methods.rb
47
+ - lib/citier/root_instance_methods.rb
48
+ - lib/citier/sql_adapters.rb
49
+ - Manifest
50
+ - citier.gemspec
51
+ homepage: https://github.com/peterhamilton/citier/
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --line-numbers
57
+ - --inline-source
58
+ - --title
59
+ - citier
60
+ - --main
61
+ - README
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 11
79
+ segments:
80
+ - 1
81
+ - 2
82
+ version: "1.2"
83
+ requirements: []
84
+
85
+ rubyforge_project: citier
86
+ rubygems_version: 1.7.2
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: "CITIER (Class Inheritance & Table Inheritance Embeddings for Rails) is a solution for single and multiple class table inheritance. For full information: http://peterhamilton.github.com/citier/ For the original version by ALTRABio see www.github.com/altrabio/"
90
+ test_files: []
91
+