citier 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+