citier4 0.0.1
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +32 -0
- data/lib/citier4/acts_as_citier4.rb +95 -0
- data/lib/citier4/child_instance_methods.rb +111 -0
- data/lib/citier4/core_ext.rb +118 -0
- data/lib/citier4/instance_methods.rb +41 -0
- data/lib/citier4/relation_methods.rb +139 -0
- data/lib/citier4/root_class_methods.rb +36 -0
- data/lib/citier4/root_instance_methods.rb +47 -0
- data/lib/citier4/version.rb +3 -0
- data/lib/citier4.rb +34 -0
- data/lib/tasks/citier4_tasks.rake +4 -0
- data/test/acts_as_citier4_test.rb +10 -0
- data/test/child_instance_methods_test.rb +70 -0
- data/test/citier4_test.rb +7 -0
- data/test/core_ext_test.rb +7 -0
- data/test/deleting_methods_test.rb +101 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/animal.rb +16 -0
- data/test/dummy/app/models/bidule.rb +2 -0
- data/test/dummy/app/models/book.rb +7 -0
- data/test/dummy/app/models/dictionary.rb +3 -0
- data/test/dummy/app/models/fiction.rb +3 -0
- data/test/dummy/app/models/product.rb +8 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +78 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20141228200436_create_products.rb +10 -0
- data/test/dummy/db/migrate/20141228200756_create_books.rb +17 -0
- data/test/dummy/db/migrate/20150101181703_create_dictionaries.rb +16 -0
- data/test/dummy/db/migrate/20150101181916_create_fictions.rb +14 -0
- data/test/dummy/db/migrate/20150102200342_create_bidules.rb +9 -0
- data/test/dummy/db/migrate/20150102200786_create_animals.rb +10 -0
- data/test/dummy/db/schema.rb +81 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +2695 -0
- data/test/dummy/log/test.log +152338 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/test/fixtures/bidules.yml +7 -0
- data/test/dummy/test/fixtures/books.yml +9 -0
- data/test/dummy/test/fixtures/dictionaries.yml +7 -0
- data/test/dummy/test/fixtures/fictions.yml +7 -0
- data/test/dummy/test/fixtures/products.yml +7 -0
- data/test/dummy/test/models/bidule_test.rb +7 -0
- data/test/dummy/test/models/book_test.rb +7 -0
- data/test/dummy/test/models/dictionary_test.rb +7 -0
- data/test/dummy/test/models/fiction_test.rb +7 -0
- data/test/dummy/test/models/product_test.rb +7 -0
- data/test/model_test.rb +34 -0
- data/test/retrieving_methods_test.rb +153 -0
- data/test/root_instance_methods_test.rb +27 -0
- data/test/test_helper.rb +60 -0
- metadata +227 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 6d8e70049fb3abef0baa93d5e1c89e5ca2272cb4
|
|
4
|
+
data.tar.gz: fdef4f7bf35e8fa1d57b2f0b458f931375eb3bf9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0a6fab3629ac0c6b7fd3c19badefe18c9354ebb9c3071fc5d714616bade91000289eedfcce376bbac372ccecaed473c80e91400411798ab52d02fcd9c0118ae6
|
|
7
|
+
data.tar.gz: 7e056a783abbd3827b4a5cbc5440288f61be57bf4f5595759650b43a508ce0483aceed1aead49a61bc625fe208369983b52a05b04b65ecf4a82c36969c56c434
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2014 YOURNAME
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'Citier4'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Bundler::GemHelper.install_tasks
|
|
21
|
+
|
|
22
|
+
require 'rake/testtask'
|
|
23
|
+
|
|
24
|
+
Rake::TestTask.new(:test) do |t|
|
|
25
|
+
t.libs << 'lib'
|
|
26
|
+
t.libs << 'test'
|
|
27
|
+
t.pattern = 'test/**/*_test.rb'
|
|
28
|
+
t.verbose = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
task default: :test
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
module Citier4
|
|
3
|
+
module ActsAsCitier4
|
|
4
|
+
|
|
5
|
+
citier_debug("Including Citier ActAsCitier4")
|
|
6
|
+
|
|
7
|
+
# your code will go here
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
citier_debug("ActAsCitier included")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.included(base)
|
|
15
|
+
# When a class includes a module the module’s self.included method will be invoked.
|
|
16
|
+
# base.send :extend, Citier4::ClassMethods
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
@@first_call=true
|
|
21
|
+
|
|
22
|
+
def acts_as_citier4(options = {})
|
|
23
|
+
# your code will go here
|
|
24
|
+
# First call, in the root class, then in a child, then in a child of child, etc ...
|
|
25
|
+
|
|
26
|
+
set_acts_as_citier(true)
|
|
27
|
+
|
|
28
|
+
# SomesOptions are authorize only in root class, not in child
|
|
29
|
+
# but is_root? is not yet implemented
|
|
30
|
+
# but due to the inheritance cascade, the first call of "acts_as_citier" is perform in the root class
|
|
31
|
+
|
|
32
|
+
#:table_name = option for setting the name of the current class table_name, default value = 'tableized(current class name)'
|
|
33
|
+
# option table_name is authorised in root and child
|
|
34
|
+
|
|
35
|
+
table_name = (options[:table_name] || self.name.tableize.gsub(/\//,'_')).to_s
|
|
36
|
+
|
|
37
|
+
if @@first_call
|
|
38
|
+
# Unfortunatly, first_call is not a good solution when we are in developpement : acts_as_citier4 could be reload for the root class ...
|
|
39
|
+
# if ! superclass.acts_as_citier?
|
|
40
|
+
# Root class
|
|
41
|
+
# Option for setting the inheritance columns, default value = 'type'
|
|
42
|
+
# authorized only in root
|
|
43
|
+
db_type_field = (options[:db_type_field] || :type).to_s
|
|
44
|
+
puts "DODO : In Citier, we have to verify the presence of inheritance_column : #{db_type_field}"
|
|
45
|
+
self.inheritance_column = "#{db_type_field}"
|
|
46
|
+
|
|
47
|
+
# Option for setting the super_class. Only in root
|
|
48
|
+
# maby it is not usefull any more ? TODO remove super_class
|
|
49
|
+
super_class = (options[:super_class] || ActiveRecord::Base) # add
|
|
50
|
+
|
|
51
|
+
citier_debug("Root Class, in home directory")
|
|
52
|
+
self.table_name = "#{table_name}"
|
|
53
|
+
citier_debug("table_name -> #{self.table_name}")
|
|
54
|
+
|
|
55
|
+
# Add the functions required for root classes only
|
|
56
|
+
# include is for adding methods to an instance of a class
|
|
57
|
+
# extend is for adding class methods.
|
|
58
|
+
send :extend, Citier4::RootClassMethods
|
|
59
|
+
send :include, Citier4::RootInstanceMethods
|
|
60
|
+
else
|
|
61
|
+
citier_debug("In Child Class, not Root Class")
|
|
62
|
+
|
|
63
|
+
# setting the inheritance columns in the child.
|
|
64
|
+
# do we have to do_it ?
|
|
65
|
+
# - in the writeable part ?
|
|
66
|
+
# - in the readable part ?
|
|
67
|
+
|
|
68
|
+
# debugger
|
|
69
|
+
self.inheritance_column = superclass.inheritance_column
|
|
70
|
+
|
|
71
|
+
citier_debug("table_name, writable -> #{table_name}")
|
|
72
|
+
# Set up the table which contains ALL attributes we want for this class
|
|
73
|
+
self.table_name = "view_#{table_name}"
|
|
74
|
+
citier_debug("tablename (view), readable -> #{self.table_name}")
|
|
75
|
+
|
|
76
|
+
# The the Writeable. References the write-able table for the class because
|
|
77
|
+
# save operations etc can't take place on the views
|
|
78
|
+
self.const_set("Writeable", create_class_writable(self))
|
|
79
|
+
# pour access : self.const_get :Writeable
|
|
80
|
+
|
|
81
|
+
after_initialize do
|
|
82
|
+
self.id = nil if self.new_record? && self.id == 0
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Add the functions required for children only
|
|
86
|
+
send :include, Citier4::ChildInstanceMethods
|
|
87
|
+
end
|
|
88
|
+
@@first_call = false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
ActiveRecord::Base.send :include, Citier4::ActsAsCitier4
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
module Citier4
|
|
2
|
+
module ChildInstanceMethods
|
|
3
|
+
|
|
4
|
+
citier_debug("Including Citier Child Instance")
|
|
5
|
+
|
|
6
|
+
def save(options={})
|
|
7
|
+
return false if (options[:validate] != false && !self.valid?)
|
|
8
|
+
|
|
9
|
+
#citier_debug("Callback (#{self.inspect})")
|
|
10
|
+
citier_debug("SAVING #{self.class.to_s}")
|
|
11
|
+
|
|
12
|
+
#AIT NOTE: Will change any protected values back to original values so any models onwards won't see changes.
|
|
13
|
+
# Run save and create/update callbacks, just like ActiveRecord does
|
|
14
|
+
self.run_callbacks(:save) do
|
|
15
|
+
self.run_callbacks(self.new_record? ? :create : :update) do
|
|
16
|
+
# debugger
|
|
17
|
+
#ici
|
|
18
|
+
#get the attributes of the class which are inherited from it's parent.
|
|
19
|
+
attributes_for_parent = self.attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }
|
|
20
|
+
changed_attributes_for_parent = self.changed_attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }
|
|
21
|
+
|
|
22
|
+
# Get the attributes of the class which are unique to this class and not inherited.
|
|
23
|
+
attributes_for_current = self.attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }
|
|
24
|
+
changed_attributes_for_current = self.changed_attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }
|
|
25
|
+
|
|
26
|
+
citier_debug("Attributes for #{self.class.superclass.to_s}: #{attributes_for_parent.inspect}")
|
|
27
|
+
citier_debug("Changed attributes for #{self.class.superclass.to_s}: #{changed_attributes_for_parent.keys.inspect}")
|
|
28
|
+
citier_debug("Attributes for #{self.class.to_s}: #{attributes_for_current.inspect}")
|
|
29
|
+
citier_debug("Changed attributes for #{self.class.to_s}: #{changed_attributes_for_current.keys.inspect}")
|
|
30
|
+
|
|
31
|
+
########
|
|
32
|
+
#
|
|
33
|
+
# Parent saving
|
|
34
|
+
|
|
35
|
+
#create a new instance of the superclass, passing the inherited attributes.
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
parent = self.class.superclass.new
|
|
39
|
+
parent.force_attributes(attributes_for_parent, :merge => true)
|
|
40
|
+
# Ceci ne marche pas : parent = self.class.superclass.new attributes_for_parent
|
|
41
|
+
|
|
42
|
+
changed_attributes_for_parent["id"] = 0 # We need to change at least something to force a timestamps update.
|
|
43
|
+
parent.force_changed_attributes(changed_attributes_for_parent)
|
|
44
|
+
|
|
45
|
+
parent.id = self.id if id
|
|
46
|
+
parent.type = self.type
|
|
47
|
+
|
|
48
|
+
parent.is_new_record(new_record?)
|
|
49
|
+
|
|
50
|
+
# If we're root (AR subclass) this will just be saved as normal through AR. If we're a child it will call this method again.
|
|
51
|
+
# It will try and save it's parent and then save itself through the Writeable constant.
|
|
52
|
+
parent_saved = parent.save
|
|
53
|
+
self.id = parent.id
|
|
54
|
+
|
|
55
|
+
if !parent_saved
|
|
56
|
+
# Couldn't save parent class
|
|
57
|
+
citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
|
|
58
|
+
citier_debug("Errors = #{parent.errors.to_s}")
|
|
59
|
+
return false # Return false and exit run_callbacks :save and :create/:update, so the after_ callback won't run.
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#End of parent saving
|
|
63
|
+
|
|
64
|
+
######
|
|
65
|
+
##
|
|
66
|
+
## Self Saving
|
|
67
|
+
##
|
|
68
|
+
|
|
69
|
+
# If there are attributes for the current class (unique & not inherited), save current model
|
|
70
|
+
if !attributes_for_current.empty?
|
|
71
|
+
|
|
72
|
+
current = self.class::Writeable.new
|
|
73
|
+
current.force_attributes(attributes_for_current, :merge => true)
|
|
74
|
+
# marche pas non plus current = self.class::Writeable.new attributes_for_current
|
|
75
|
+
|
|
76
|
+
current.force_changed_attributes(changed_attributes_for_current)
|
|
77
|
+
|
|
78
|
+
current.id = self.id
|
|
79
|
+
current.is_new_record(new_record?)
|
|
80
|
+
|
|
81
|
+
current_saved = current.save
|
|
82
|
+
|
|
83
|
+
current.after_save_change_request if current.respond_to?('after_save_change_request') #Specific to an app I'm building
|
|
84
|
+
|
|
85
|
+
if !current_saved
|
|
86
|
+
citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
|
|
87
|
+
citier_debug("Errors = #{current.errors.to_s}")
|
|
88
|
+
return false # Return false and exit run_callbacks :save and :create/:update, so the after callback won't run.
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# at this point, parent_saved && current_saved
|
|
93
|
+
|
|
94
|
+
is_new_record(false) # This is no longer a new record
|
|
95
|
+
|
|
96
|
+
self.force_changed_attributes({}) # Reset changed_attributes so future changes will be tracked correctly
|
|
97
|
+
|
|
98
|
+
# No return, because we want the after callback to run.
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
return true
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def save!(options={})
|
|
105
|
+
raise ActiveRecord::RecordInvalid.new(self) if (options[:validate] != false && !self.valid?)
|
|
106
|
+
self.save || raise(ActiveRecord::RecordNotSaved)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
include InstanceMethods
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
class ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
citier_debug("Including Citier Core Ext")
|
|
4
|
+
|
|
5
|
+
def self.set_acts_as_citier(citier)
|
|
6
|
+
@acts_as_citier = citier
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.acts_as_citier?
|
|
10
|
+
@acts_as_citier || false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.[](column_name)
|
|
14
|
+
arel_table[column_name]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def is_new_record(state)
|
|
18
|
+
@new_record = state
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#create the writeable class which inherits from ActiveRecord::Base
|
|
22
|
+
def self.create_class_writable(class_reference)
|
|
23
|
+
Class.new(ActiveRecord::Base) do
|
|
24
|
+
include Citier4::InstanceMethods::ForcedWriters
|
|
25
|
+
|
|
26
|
+
view_name = class_reference.table_name
|
|
27
|
+
# set the name of the table associated to this class
|
|
28
|
+
# this class will be associated to the writable table of the class_reference class
|
|
29
|
+
self.table_name = view_name[5..view_name.length]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class ActiveRecord::Migration
|
|
35
|
+
#function for creating views for migrations
|
|
36
|
+
def create_citier_view(theclass)
|
|
37
|
+
# flush any column info in memory
|
|
38
|
+
# Loops through and stops once we've cleaned up to our root class.
|
|
39
|
+
# We MUST user Writeable as that is the place where changes might reside!
|
|
40
|
+
reset_class = theclass::Writeable
|
|
41
|
+
until reset_class == ActiveRecord::Base # Refinery::Core::BaseModel # ActiveRecord::Base eoz
|
|
42
|
+
citier_debug("Resetting column information on #{reset_class}")
|
|
43
|
+
reset_class.reset_column_information
|
|
44
|
+
reset_class = reset_class.superclass
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
self_columns = theclass::Writeable.column_names.select{ |c| c != "id" }
|
|
48
|
+
parent_columns = theclass.superclass.column_names.select{ |c| c != "id" }
|
|
49
|
+
columns = parent_columns+self_columns
|
|
50
|
+
self_read_table = theclass.table_name
|
|
51
|
+
self_write_table = theclass::Writeable.table_name
|
|
52
|
+
parent_read_table = theclass.superclass.table_name
|
|
53
|
+
# 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"
|
|
54
|
+
debugger
|
|
55
|
+
#Use our rails_sql_views gem to create the view so we get it outputted to schema
|
|
56
|
+
create_view "#{self_read_table}", "SELECT #{parent_read_table}.id, #{columns.join(',')} FROM #{parent_read_table}, #{self_write_table} WHERE #{parent_read_table}.id = #{self_write_table}.id" do |v|
|
|
57
|
+
v.column :id
|
|
58
|
+
columns.each do |c|
|
|
59
|
+
v.column c.to_sym
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# citier_debug("Creating citier view -> #{sql}")
|
|
64
|
+
#theclass.connection.execute sql
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def drop_citier_view(theclass) #function for dropping views for migrations
|
|
70
|
+
self_read_table = theclass.table_name
|
|
71
|
+
# sql = "DROP VIEW #{self_read_table}"
|
|
72
|
+
|
|
73
|
+
drop_view(self_read_table.to_sym) #drop using our rails sql views gem
|
|
74
|
+
|
|
75
|
+
citier_debug("Dropping citier view -> #{sql}")
|
|
76
|
+
#theclass.connection.execute sql
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def update_citier_view(theclass) #function for updating views for migrations
|
|
80
|
+
citier_debug("Updating citier view for #{theclass}")
|
|
81
|
+
if theclass.table_exists?
|
|
82
|
+
drop_citier_view(theclass)
|
|
83
|
+
create_citier_view(theclass)
|
|
84
|
+
else
|
|
85
|
+
citier_debug("Error: #{theclass} VIEW doesn't exist.")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def create_or_update_citier_view(theclass) #Convienience function for updating or creating views for migrations
|
|
90
|
+
citier_debug("Create or Update citier view for #{theclass}")
|
|
91
|
+
if theclass.table_exists?
|
|
92
|
+
update_citier_view(theclass)
|
|
93
|
+
else
|
|
94
|
+
citier_debug("VIEW DIDN'T EXIST. Now creating for #{theclass}")
|
|
95
|
+
create_citier_view(theclass)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
module ActiveRecord
|
|
102
|
+
# = Active Record Relation
|
|
103
|
+
class Relation
|
|
104
|
+
|
|
105
|
+
# Causes the records to be loaded from the database if they have not
|
|
106
|
+
# been loaded already. You can use this if for some reason you need
|
|
107
|
+
# to explicitly load some records before actually using them. The
|
|
108
|
+
# return value is the relation itself, not the records.
|
|
109
|
+
#
|
|
110
|
+
# Post.where(published: true).load # => #<ActiveRecord::Relation>
|
|
111
|
+
def load
|
|
112
|
+
puts "in load de citier4"
|
|
113
|
+
exec_queries unless loaded?
|
|
114
|
+
|
|
115
|
+
self
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Citier4
|
|
2
|
+
module InstanceMethods
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.send :include, ForcedWriters
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ForcedWriters
|
|
8
|
+
def force_attributes(new_attributes, options = {})
|
|
9
|
+
# debugger
|
|
10
|
+
# TODO new_attributes = @attributes.merge(new_attributes) if options[:merge]
|
|
11
|
+
new_attributes = self.attributes.merge(new_attributes) if options[:merge]
|
|
12
|
+
|
|
13
|
+
# TODO @attributes = new_attributes
|
|
14
|
+
self.attributes = new_attributes
|
|
15
|
+
|
|
16
|
+
if options[:clear_caches] != false
|
|
17
|
+
@aggregation_cache = {}
|
|
18
|
+
@association_cache = {}
|
|
19
|
+
@attributes_cache = {}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def force_changed_attributes(new_changed_attributes, options = {})
|
|
24
|
+
new_changed_attributes = @attributes.merge(new_changed_attributes) if options[:merge]
|
|
25
|
+
@changed_attributes = new_changed_attributes
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# USAGE validates :attribute, :citier_uniqueness => true
|
|
30
|
+
# Needed because validates :attribute, :uniqueness => true Won't work because it tries to call child_class.attribute on parents table
|
|
31
|
+
class CitierUniquenessValidator < ActiveModel::EachValidator
|
|
32
|
+
def validate_each(object, attribute, value)
|
|
33
|
+
existing_record = object.class.where(attribute.to_sym => value).limit(1).first
|
|
34
|
+
if existing_record && existing_record.as_root != object.as_root #if prev record exist and it isn't our current obj
|
|
35
|
+
object.errors[attribute] << (options[:message] || "has already been taken.")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
class Relation
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# instance.delete -> class.delete(id) -> class.where(id).delete_all
|
|
6
|
+
# donc tout finit dans delete_all
|
|
7
|
+
|
|
8
|
+
citier_debug("Including Citier Relation")
|
|
9
|
+
|
|
10
|
+
# http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-delete
|
|
11
|
+
# delete_all(conditions = nil))
|
|
12
|
+
# Deletes the records matching conditions without instantiating the records first,
|
|
13
|
+
# and hence not calling the destroy method nor invoking callbacks.
|
|
14
|
+
# This is a single SQL DELETE statement that goes straight to the database,
|
|
15
|
+
# much more efficient than destroy_all.
|
|
16
|
+
# Be careful with relations though, in particular :dependent rules defined on associations
|
|
17
|
+
# are not honored. Returns the number of rows affected.
|
|
18
|
+
|
|
19
|
+
alias_method :relation_delete_all, :delete_all
|
|
20
|
+
|
|
21
|
+
def delete_all(conditions = nil)
|
|
22
|
+
|
|
23
|
+
return relation_delete_all(conditions) if !@klass.acts_as_citier?
|
|
24
|
+
return relation_delete_all(conditions) if conditions
|
|
25
|
+
|
|
26
|
+
deleted = true
|
|
27
|
+
ids = nil
|
|
28
|
+
c = @klass
|
|
29
|
+
|
|
30
|
+
bind_values.each do |bind_value|
|
|
31
|
+
if bind_value[0].name == "id"
|
|
32
|
+
ids = bind_value[1]
|
|
33
|
+
break
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
ids ||= where_values_hash["id"] || where_values_hash[:id]
|
|
38
|
+
where_hash = ids ? { :id => ids } : nil
|
|
39
|
+
|
|
40
|
+
# debugger
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ici, si on a un dictionaire avec id 10
|
|
44
|
+
# Product.delete 10
|
|
45
|
+
# Book.delete 10
|
|
46
|
+
# Dictionary.delete 10
|
|
47
|
+
# ne vont pas faire la meme chose.
|
|
48
|
+
# Avec Product.delete 10, les Book::Writeable et Dictionary::Writeable ne sont pas déleter pour l'ID 10
|
|
49
|
+
# il faudrait pouvoir attaquer la boucle avec c = Dictionary et pas c = Product
|
|
50
|
+
|
|
51
|
+
# ne marche que si on a que un ids et pas un tableau d'ids ...
|
|
52
|
+
if ids.class == Fixnum
|
|
53
|
+
# debugger
|
|
54
|
+
c = @klass.find(ids).class
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
deleted &= c.base_class.where(where_hash).relation_delete_all
|
|
58
|
+
|
|
59
|
+
while c.superclass != ActiveRecord::Base # Refinery::Core::BaseModel # eoz ActiveRecord::Base
|
|
60
|
+
if c.const_defined?(:Writeable)
|
|
61
|
+
citier_debug("Deleting back up hierarchy #{c}")
|
|
62
|
+
deleted &= c::Writeable.where(where_hash).delete_all
|
|
63
|
+
end
|
|
64
|
+
c = c.superclass
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
deleted
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# TODO : remove to_a ..., will be obsolete ? and doen't work with rails 4.2 (ok for 4.1)
|
|
71
|
+
# to_a :
|
|
72
|
+
# Returns an array representation of obj. For objects of class Object and others that don’t explicitly override the method,
|
|
73
|
+
# the return value is an array containing self. However, this latter behavior will soon be obsolete.
|
|
74
|
+
|
|
75
|
+
alias_method :relation_to_a, :to_a
|
|
76
|
+
def to_a
|
|
77
|
+
citier_debug("citier -> to_a")
|
|
78
|
+
return relation_to_a if !@klass.acts_as_citier?
|
|
79
|
+
records = relation_to_a
|
|
80
|
+
|
|
81
|
+
c = @klass
|
|
82
|
+
|
|
83
|
+
if records.all? { |record| record.class == c }
|
|
84
|
+
return records
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
full_records = []
|
|
88
|
+
ids_wanted = {}
|
|
89
|
+
|
|
90
|
+
# Map all the ids wanted per type
|
|
91
|
+
records.each do |record|
|
|
92
|
+
if record.class == c # We don't need to find the record again if this is already the correct one
|
|
93
|
+
full_records << record
|
|
94
|
+
next
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
ids_wanted[record.class] ||= []
|
|
98
|
+
ids_wanted[record.class] << record.id
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Find all wanted records
|
|
102
|
+
ids_wanted.each do |type_class, ids|
|
|
103
|
+
full_records.push(*type_class.find(ids))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Make a new array with the found records at the right places
|
|
107
|
+
records.each do |record|
|
|
108
|
+
full_record = full_records.find { |full_record| full_record.id == record.id }
|
|
109
|
+
# debugger
|
|
110
|
+
att = full_record.instance_variable_get(:@attributes)
|
|
111
|
+
record.force_attributes(att, :merge => true, :clear_caches => false)
|
|
112
|
+
# TODO record.force_attributes(full_record.instance_variable_get(:@attributes), :merge => true, :clear_caches => false)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
return records
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# TODO
|
|
119
|
+
# apply_finder_options is depreciated in rails 4
|
|
120
|
+
# use by paginate
|
|
121
|
+
# TODO test page
|
|
122
|
+
# alias_method :relation_apply_finder_options, :apply_finder_options
|
|
123
|
+
# def apply_finder_options(options)
|
|
124
|
+
# return relation_apply_finder_options(options) if !@klass.acts_as_citier?
|
|
125
|
+
# relation = self
|
|
126
|
+
# # With option :no_children set to true, only records of type self will be returned.
|
|
127
|
+
# # So Root.all(:no_children => true) won't return Child records.
|
|
128
|
+
# no_children = options.delete(:no_children)
|
|
129
|
+
# if no_children
|
|
130
|
+
# relation = clone
|
|
131
|
+
# c = @klass
|
|
132
|
+
# self_type = c.superclass == ActiveRecord::Base ? nil : c.name
|
|
133
|
+
# relation = relation.where(:type => self_type)
|
|
134
|
+
# end
|
|
135
|
+
# relation.relation_apply_finder_options(options)
|
|
136
|
+
# end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Citier4
|
|
2
|
+
module RootClassMethods
|
|
3
|
+
|
|
4
|
+
citier_debug("Including Citier Class Methods")
|
|
5
|
+
|
|
6
|
+
# any method placed here will apply to classes
|
|
7
|
+
# remove the acts_as_citier definition. see file acts_as_citer
|
|
8
|
+
# def acts_as_citier(options = {})
|
|
9
|
+
# ...
|
|
10
|
+
# end
|
|
11
|
+
#def acts_as_citier?
|
|
12
|
+
# true
|
|
13
|
+
# end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# find_by and find_by! are exaclty the same defintion that ActiveRecord::FinderMethods
|
|
18
|
+
# see finder_methods.rb
|
|
19
|
+
# but it is not these function that are call by
|
|
20
|
+
# Post.find_by name: 'Spartacus', rating: 4
|
|
21
|
+
# but the one that are define in ActiveRecord::Core
|
|
22
|
+
# So, theses methods are here just to overwrite the one in ActiveRecord::Core
|
|
23
|
+
def find_by(*args)
|
|
24
|
+
where(*args).take
|
|
25
|
+
rescue RangeError
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
def find_by!(*args)
|
|
29
|
+
where(*args).take!
|
|
30
|
+
rescue RangeError
|
|
31
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range value"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Citier4
|
|
2
|
+
module RootInstanceMethods
|
|
3
|
+
|
|
4
|
+
citier_debug("Including Citier Root")
|
|
5
|
+
|
|
6
|
+
include InstanceMethods
|
|
7
|
+
|
|
8
|
+
# Instantiates the instance as it's lowest root class. Used when destroying a root class to
|
|
9
|
+
# make sure we're not leaving children behind
|
|
10
|
+
def as_child
|
|
11
|
+
#instance_class = Object.const_get(self.type)
|
|
12
|
+
return bottom_class_instance = Kernel.const_get(self.type).where(:id => self.id).first
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Access the root class if ever you need.
|
|
16
|
+
def as_root
|
|
17
|
+
if !self.is_root?
|
|
18
|
+
root_class = self.class.base_class
|
|
19
|
+
|
|
20
|
+
#get the attributes of the class which are inherited from it's parent.
|
|
21
|
+
attributes_for_parent = self.attributes.reject{|key,value| !root_class.column_names.include?(key) }
|
|
22
|
+
|
|
23
|
+
#create a new instance of the superclass, passing the inherited attributes.
|
|
24
|
+
parent = root_class.new(attributes_for_parent)
|
|
25
|
+
parent.id = self.id
|
|
26
|
+
parent.type = self.type
|
|
27
|
+
|
|
28
|
+
parent.is_new_record(new_record?)
|
|
29
|
+
|
|
30
|
+
parent
|
|
31
|
+
else
|
|
32
|
+
self #just return self if we are the root
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#For testing whther we are using the framework or not
|
|
37
|
+
def acts_as_citier?
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def is_root?
|
|
42
|
+
self.class.superclass==ActiveRecord::Base # eoz
|
|
43
|
+
# self.class.superclass==Refinery::Core::BaseModel
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|