Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update citier to Rails 4 #69

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f605d63
Combine Parent.save and Child.save to the same transaction.
sishen Aug 7, 2012
bdcfade
set created_at/updated_at back to view object
sishen Oct 22, 2012
bafffcf
find the deleting ids matching the condition in delete_all
sishen Oct 29, 2012
7ede690
Pass the serialized attributes to Writable class.
sishen Oct 30, 2012
4f66af5
use connection.quote_column_name to quote the column name
sishen Oct 30, 2012
bcfb9c7
refactor the ActiveRecord::Relation hack
sishen Mar 11, 2013
830647b
remove citier hack of sql_adapters
sishen Mar 13, 2013
cd58186
set default value when initialize childclass
chaofan Jan 15, 2014
b8efbda
set default value when initialize childclass
chaofan Jan 15, 2014
839be45
Merge branch 'master' of https://github.com/chaofan/citier
chaofan Jan 15, 2014
e3109a9
except('type') when copy parent class attributes
chaofan Jan 15, 2014
df91e93
correct create error
chaofan Jan 15, 2014
d39e59a
Update citier.gemspec
m7moud Apr 14, 2014
6b1b538
Remove alias for deprecated apply_finder_options
m7moud Apr 14, 2014
6286eb7
create_citier_view
chaofan Aug 7, 2014
ea7a2e3
create_citier_view
chaofan Aug 7, 2014
00a3aa1
create_citier_view
chaofan Aug 7, 2014
f33a6ba
create_citier_view
chaofan Aug 7, 2014
d66d67e
mini
chaofan Aug 7, 2014
ff000d7
recorrect
chaofan Aug 12, 2014
4ccc2ed
Merge branch 'two_types_correct'
chaofan Aug 12, 2014
477b273
mini
chaofan Aug 12, 2014
099fdb8
mini
chaofan Aug 12, 2014
0a7fb38
reset to old commit
chaofan Aug 14, 2014
4b26c8c
Merge pull request #1 from chaofan/master
m7moud Sep 18, 2014
0f18939
Fix class_reference bug
Oct 13, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions citier.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Gem::Specification.new do |s|
s.name = %q{citier}
s.version = "0.1.15"
s.version = "0.1.16"

s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
s.authors = ["Peter Hamilton, Originally from Laurent Buffat, Pierre-Emmanuel Jouve"]
Expand All @@ -18,7 +18,6 @@ Gem::Specification.new do |s|
"lib/citier/instance_methods.rb",
"lib/citier/child_instance_methods.rb",
"lib/citier/root_instance_methods.rb",
"lib/citier/sql_adapters.rb",
"lib/citier/relation_methods.rb"]
s.files = ["Rakefile",
"lib/citier.rb",
Expand All @@ -28,18 +27,16 @@ Gem::Specification.new do |s|
"lib/citier/instance_methods.rb",
"lib/citier/child_instance_methods.rb",
"lib/citier/root_instance_methods.rb",
"lib/citier/sql_adapters.rb",
"lib/citier/relation_methods.rb",
"Manifest",
"citier.gemspec"]
s.homepage = %q{https://github.com/peterhamilton/citier/}
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "citier", "--main", "README"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{citier}
s.rubygems_version = %q{1.3.7}
s.summary = s.description
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

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

if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
Expand Down
5 changes: 1 addition & 4 deletions lib/citier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ def citier_debug(s)
# Methods that will be used for the instances of the Non Root Classes
require 'citier/child_instance_methods'

# Require SQL Adapters
require 'citier/sql_adapters'

#Require acts_as_citier hook
require 'citier/acts_as_citier'

# Methods that override ActiveRecord::Relation
require 'citier/relation_methods'
require 'citier/relation_methods'
116 changes: 61 additions & 55 deletions lib/citier/child_instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ module ChildInstanceMethods

def save(options={})
return false if (options[:validate] != false && !self.valid?)

#citier_debug("Callback (#{self.inspect})")
citier_debug("SAVING #{self.class.to_s}")

#AIT NOTE: Will change any protected values back to original values so any models onwards won't see changes.
# Run save and create/update callbacks, just like ActiveRecord does
self.run_callbacks(:save) do
self.run_callbacks(self.new_record? ? :create : :update) do
#get the attributes of the class which are inherited from it's parent.
attributes_for_parent = self.attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }
changed_attributes_for_parent = self.changed_attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }
attributes = self.instance_variable_get(:@attributes)
changed_attributes = self.instance_variable_get(:@changed_attributes)
attributes_for_parent = attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }
changed_attributes_for_parent = changed_attributes.reject { |key,value| !self.class.superclass.column_names.include?(key) }

# Get the attributes of the class which are unique to this class and not inherited.
attributes_for_current = self.attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }
changed_attributes_for_current = self.changed_attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }
attributes_for_current = attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }
changed_attributes_for_current = changed_attributes.reject { |key,value| self.class.superclass.column_names.include?(key) }

citier_debug("Attributes for #{self.class.superclass.to_s}: #{attributes_for_parent.inspect}")
citier_debug("Changed attributes for #{self.class.superclass.to_s}: #{changed_attributes_for_parent.keys.inspect}")
Expand All @@ -27,76 +29,80 @@ def save(options={})
########
#
# Parent saving

#create a new instance of the superclass, passing the inherited attributes.
parent = self.class.superclass.new

parent.force_attributes(attributes_for_parent, :merge => true)
changed_attributes_for_parent["id"] = 0 # We need to change at least something to force a timestamps update.
parent.force_changed_attributes(changed_attributes_for_parent)

parent.id = self.id if id
parent.type = self.type

parent.is_new_record(new_record?)
# 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.

# 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.
# It will try and save it's parent and then save itself through the Writable constant.
parent_saved = parent.save
self.id = parent.id

if !parent_saved
# Couldn't save parent class
citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
citier_debug("Errors = #{parent.errors.to_s}")
return false # Return false and exit run_callbacks :save and :create/:update, so the after_ callback won't run.
end

#End of parent saving

######
##
## Self Saving
##

# If there are attributes for the current class (unique & not inherited), save current model
if !attributes_for_current.empty?
current = self.class::Writable.new

current.force_attributes(attributes_for_current, :merge => true)
current.force_changed_attributes(changed_attributes_for_current)

current.id = self.id
current.is_new_record(new_record?)

current_saved = current.save

current.after_save_change_request if current.respond_to?('after_save_change_request') #Specific to an app I'm building

if !current_saved
parent.transaction do
parent_saved = parent.save
self.id = parent.id
self.created_at ||= parent.created_at # should set the generated timestamps to view object
self.updated_at ||= parent.updated_at

if !parent_saved
# Couldn't save parent class
citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
citier_debug("Errors = #{current.errors.to_s}")
return false # Return false and exit run_callbacks :save and :create/:update, so the after callback won't run.
citier_debug("Errors = #{parent.errors.to_s}")
return false # Return false and exit run_callbacks :save and :create/:update, so the after_ callback won't run.
end

#End of parent saving

######
##
## Self Saving
##

# If there are attributes for the current class (unique & not inherited), save current model
if !attributes_for_current.empty?
current = self.class::Writable.new

current.force_attributes(attributes_for_current, :merge => true)
current.force_changed_attributes(changed_attributes_for_current)

current.id = self.id
current.is_new_record(new_record?)

current_saved = current.save

current.after_save_change_request if current.respond_to?('after_save_change_request') #Specific to an app I'm building

if !current_saved
citier_debug("Class (#{self.class.superclass.to_s}) could not be saved")
citier_debug("Errors = #{current.errors.to_s}")
return false # Return false and exit run_callbacks :save and :create/:update, so the after callback won't run.
end
end
end

# at this point, parent_saved && current_saved

is_new_record(false) # This is no longer a new record
# at this point, parent_saved && current_saved

is_new_record(false) # This is no longer a new record

self.force_changed_attributes({}) # Reset changed_attributes so future changes will be tracked correctly

self.force_changed_attributes({}) # Reset changed_attributes so future changes will be tracked correctly

# No return, because we want the after callback to run.
# No return, because we want the after callback to run.
end
end
end
return true
end

def save!(options={})
raise ActiveRecord::RecordInvalid.new(self) if (options[:validate] != false && !self.valid?)
self.save || raise(ActiveRecord::RecordNotSaved)
end

include InstanceMethods
end
end
end
15 changes: 12 additions & 3 deletions lib/citier/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,25 @@ def acts_as_citier(options = {})

# The the Writable. References the write-able table for the class because
# save operations etc can't take place on the views
self.const_set("Writable", create_class_writable(self))
self.const_set("Writable", create_class_writable(self)) unless self.const_defined?(:Writable)

after_initialize do
self.id = nil if self.new_record? && self.id == 0
if self.new_record?
parent = self.class.superclass.new
attributes_for_parent = parent.instance_variable_get(:@attributes)
self.or_attributes(attributes_for_parent)

current = self.class::Writable.new
attributes_for_current = current.instance_variable_get(:@attributes)
self.or_attributes(attributes_for_current)
self.id = nil if self.id == 0
end
end

# Add the functions required for children only
send :include, Citier::ChildInstanceMethods
else
# Root class
# Root class

citier_debug("Root Class")

Expand Down
30 changes: 11 additions & 19 deletions lib/citier/core_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,10 @@ def is_new_record(state)
def self.create_class_writable(class_reference) #creation of a new class which inherits from ActiveRecord::Base
Class.new(ActiveRecord::Base) do
include Citier::InstanceMethods::ForcedWriters

t_name = class_reference.table_name
t_name = t_name[5..t_name.length]

if t_name[0..5] == "view_"
t_name = t_name[5..t_name.length]
end

# set the name of the table associated to this class
# this class will be associated to the writable table of the class_reference class
self.table_name = t_name
self.table_name = class_reference.table_name.gsub(/^view_/, '')
self.serialized_attributes ||= self.class_reference.serialized_attributes.reject { |key, value| self.class_reference.superclass.column_names.include?(key) }
end
end
end
Expand All @@ -41,19 +34,20 @@ def create_citier_view(theclass) #function for creating views for migrations
reset_class = theclass::Writable
until reset_class == ActiveRecord::Base
citier_debug("Resetting column information on #{reset_class}")
# http://apidock.com/rails/ActiveRecord/Base/reset_column_information/class
reset_class.reset_column_information
reset_class = reset_class.superclass
end

self_columns = theclass::Writable.column_names.select{ |c| c != "id" }
parent_columns = theclass.superclass.column_names.select{ |c| c != "id" }
columns = parent_columns+self_columns
self_read_table = theclass.table_name
self_write_table = theclass::Writable.table_name
self_columns = theclass::Writable.column_names.select{ |c| c != "id" }
parent_columns = theclass.superclass.column_names.select{ |c| c != "id" }
columns = parent_columns + self_columns
self_read_table = theclass.table_name
self_write_table = theclass::Writable.table_name
parent_read_table = theclass.superclass.table_name
select_sql = "SELECT #{parent_read_table}.id, #{columns.map { |c| "\"#{c}\"" }.join(',')} FROM #{parent_read_table}, #{self_write_table} WHERE #{parent_read_table}.id = #{self_write_table}.id"
sql = "CREATE VIEW #{self_read_table} AS #{select_sql}"

select_sql = "SELECT #{parent_read_table}.id, #{columns.map { |c| theclass.connection.quote_column_name(c) }.join(',')} FROM #{parent_read_table}, #{self_write_table} WHERE #{parent_read_table}.id = #{self_write_table}.id"
sql = "CREATE VIEW #{self_read_table} AS #{select_sql}"
#Use our rails_sql_views gem to create the view so we get it outputted to schema
create_view "#{self_read_table}", select_sql do |v|
v.column :id
Expand All @@ -64,8 +58,6 @@ def create_citier_view(theclass) #function for creating views for migrations

citier_debug("Creating citier view -> #{sql}")
#theclass.connection.execute sql


end

def drop_citier_view(theclass) #function for dropping views for migrations
Expand Down
10 changes: 7 additions & 3 deletions lib/citier/instance_methods.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
module Citier
module InstanceMethods
module InstanceMethods
def self.included(base)
base.send :include, ForcedWriters
ActiveRecord::Base.send :include, ForcedWriters
end

module ForcedWriters
def or_attributes(new_attributes)
new_attributes.each {|k,v| @attributes[k] = v if @attributes[k].nil?}
end

def force_attributes(new_attributes, options = {})
new_attributes = @attributes.merge(new_attributes) if options[:merge]
@attributes = new_attributes
Expand All @@ -28,7 +32,7 @@ class CitierUniquenessValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
existing_record = object.class.where(attribute.to_sym => value).limit(1).first
if existing_record && existing_record.as_root != object.as_root #if prev record exist and it isn't our current obj
object.errors[attribute] << (options[:message] || "has already been taken.")
object.errors[attribute] << (options[:message] || "has already been taken.")
end
end
end
Expand Down
Loading