active_file_record 0.0.2a → 0.0.3a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/active_file_record.gemspec +1 -1
  2. data/lib/active_file_record/associations.rb +57 -0
  3. data/lib/active_file_record/associations/association.rb +101 -0
  4. data/lib/active_file_record/associations/association_scope.rb +29 -0
  5. data/lib/active_file_record/associations/belongs_to_association.rb +78 -0
  6. data/lib/active_file_record/associations/builder/association.rb +46 -0
  7. data/lib/active_file_record/associations/builder/belongs_to.rb +13 -0
  8. data/lib/active_file_record/associations/singular_association.rb +64 -0
  9. data/lib/active_file_record/attribute_assignment.rb +25 -0
  10. data/lib/active_file_record/attribute_methods.rb +48 -0
  11. data/lib/active_file_record/attribute_methods/read.rb +26 -0
  12. data/lib/active_file_record/attribute_methods/write.rb +27 -0
  13. data/lib/active_file_record/base.rb +81 -0
  14. data/lib/active_file_record/callbacks.rb +21 -0
  15. data/lib/active_file_record/criteria.rb +10 -0
  16. data/lib/active_file_record/file_handler.rb +66 -0
  17. data/lib/active_file_record/file_handler/active_file.rb +13 -0
  18. data/lib/active_file_record/file_handler/attribute.rb +5 -0
  19. data/lib/active_file_record/inheritance.rb +42 -0
  20. data/lib/active_file_record/integration.rb +13 -0
  21. data/lib/active_file_record/nodes.rb +2 -0
  22. data/lib/active_file_record/nodes/binary.rb +19 -0
  23. data/lib/active_file_record/nodes/equality.rb +11 -0
  24. data/lib/active_file_record/persistence.rb +53 -0
  25. data/lib/active_file_record/predications.rb +9 -0
  26. data/lib/active_file_record/reflection.rb +140 -0
  27. data/lib/active_file_record/relation.rb +73 -0
  28. data/lib/active_file_record/relation/finder_methods.rb +88 -0
  29. data/lib/active_file_record/relation/predicate_builder.rb +21 -0
  30. data/lib/active_file_record/relation/search_methods.rb +65 -0
  31. data/lib/active_file_record/scoping.rb +28 -0
  32. data/lib/active_file_record/scoping/named.rb +72 -0
  33. data/lib/active_file_record/validations.rb +12 -0
  34. data/lib/active_file_record/version.rb +1 -1
  35. metadata +33 -1
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.summary = %q{Gem summary}
9
9
  gem.homepage = ""
10
10
 
11
- gem.files = `git ls-files`.split($\)
11
+ gem.files = `git ls-files`.split("\n")
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
13
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
14
  gem.name = "active_file_record"
@@ -0,0 +1,57 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/enumerable'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/string/conversions'
6
+ require 'active_support/core_ext/module/remove_method'
7
+ require 'active_support/core_ext/class/attribute'
8
+
9
+ module ActiveFileRecord
10
+ module Associations
11
+ extend ActiveSupport::Autoload
12
+ extend ActiveSupport::Concern
13
+
14
+ autoload :Association
15
+ autoload :SingularAssociation
16
+ autoload :BelongsToAssociation
17
+
18
+ module Builder
19
+ autoload :Association, 'active_file_record/associations/builder/association'
20
+ autoload :SingularAssociation, 'active_file_record/associations/builder/singular_association'
21
+ autoload :BelongsTo, 'active_file_record/associations/builder/belongs_to'
22
+ end
23
+
24
+ eager_autoload do
25
+ autoload :AssociationScope, 'active_file_record/associations/association_scope'
26
+ end
27
+
28
+ attr_reader :association_cache
29
+
30
+ def association(name)
31
+ association = association_instance_get(name)
32
+ if association.nil?
33
+ reflection = self.class.reflect_on_association(name)
34
+ association = reflection.association_class.new(self, reflection)
35
+ association_instance_set(name, association)
36
+ end
37
+
38
+ association
39
+ end
40
+
41
+ private
42
+ def association_instance_get(name)
43
+ @association_cache[name.to_sym]
44
+ end
45
+
46
+ def association_instance_set(name, association)
47
+ @association_cache[name] = association
48
+ end
49
+
50
+ module ClassMethods
51
+ def belongs_to(name, options = {})
52
+ Builder::BelongsTo.build(self, name, options)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,101 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/object/inclusion'
3
+
4
+ module ActiveFileRecord
5
+ module Associations
6
+
7
+ class Association
8
+ attr_reader :owner, :target, :reflection
9
+ delegate :options, :to => :reflection
10
+
11
+ def initialize(owner, reflection)
12
+ #reflection.check_validity!
13
+
14
+ @target = nil
15
+ @owner, @reflection = owner, reflection
16
+ @updated = false
17
+
18
+ reset
19
+ reset_scope
20
+ end
21
+
22
+ def reset
23
+ @loaded = false
24
+ @target = nil
25
+ end
26
+
27
+ def reset_scope
28
+ @association_scope = nil
29
+ end
30
+
31
+ def reload
32
+ reset
33
+ reset_scope
34
+ load_target
35
+ self unless target.nil?
36
+ end
37
+
38
+ def klass
39
+ reflection.klass
40
+ end
41
+
42
+ def scoped
43
+ target_scope.merge(association_scope)
44
+ end
45
+
46
+ def target_scope
47
+ klass.scoped
48
+ end
49
+
50
+ def association_scope
51
+ if klass
52
+ @association_scope ||= AssociationScope.new(self).scope
53
+ end
54
+ end
55
+
56
+ def set_inverse_instance(record)
57
+ if record && invertible_for?(record)
58
+ inverse = record.association(inverse_reflection_for(record).name)
59
+ inverse.target = owner
60
+ end
61
+ end
62
+
63
+ def inverse_reflection_for(record)
64
+ reflection.inverse_of
65
+ end
66
+
67
+ def stale_target?
68
+ loaded? && @stale_state != stale_state
69
+ end
70
+
71
+ def loaded?
72
+ @loaded
73
+ end
74
+
75
+ def load_target
76
+ if find_target?
77
+ #begin
78
+ # if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
79
+ # @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
80
+ # end
81
+ #rescue NameError
82
+ # nil
83
+ #ensure
84
+ @target ||= find_target
85
+ #end
86
+ end
87
+
88
+ loaded! unless loaded?
89
+ target
90
+ rescue ActiveFileRecord::RecordNotFound
91
+ reset
92
+ end
93
+
94
+ def loaded!
95
+ @loaded = true
96
+ @stale_state = stale_state
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveFileRecord
2
+ module Associations
3
+ class AssociationScope
4
+
5
+ attr_reader :association
6
+
7
+ delegate :klass, :owner, :reflection, :to => :association
8
+ delegate :chain, :conditions, :to => :reflection
9
+
10
+ def initialize(association)
11
+ @association = association
12
+ end
13
+
14
+ def scope
15
+ scope = klass.unscoped
16
+ add_constraints(scope)
17
+ end
18
+
19
+ private
20
+
21
+ def add_constraints(scope)
22
+ key = reflection.association_primary_key
23
+ foreign_key = reflection.foreign_key
24
+ scope.where({key => {:eq => owner[foreign_key].to_i}})
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,78 @@
1
+ module ActiveFileRecord
2
+ module Associations
3
+ class BelongsToAssociation < SingularAssociation
4
+ def replace(record)
5
+ raise_on_type_mismatch(record) if record
6
+
7
+ update_counters(record)
8
+ replace_keys(record)
9
+ set_inverse_instance(record)
10
+
11
+ @updated = true if record
12
+
13
+ self.target = record
14
+ end
15
+
16
+ def updated?
17
+ @updated
18
+ end
19
+
20
+ private
21
+
22
+ def find_target?
23
+ !loaded? && foreign_key_present? && klass
24
+ end
25
+
26
+ #def update_counters(record)
27
+ # counter_cache_name = reflection.counter_cache_column
28
+ #
29
+ # if counter_cache_name && owner.persisted? && different_target?(record)
30
+ # if record
31
+ # record.class.increment_counter(counter_cache_name, record.id)
32
+ # end
33
+ #
34
+ # if foreign_key_present?
35
+ # klass.decrement_counter(counter_cache_name, target_id)
36
+ # end
37
+ # end
38
+ #end
39
+
40
+ # Checks whether record is different to the current target, without loading it
41
+ def different_target?(record)
42
+ record.nil? && owner[reflection.foreign_key] ||
43
+ record && record.id != owner[reflection.foreign_key]
44
+ end
45
+
46
+ def replace_keys(record)
47
+ if record
48
+ owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
49
+ else
50
+ owner[reflection.foreign_key] = nil
51
+ end
52
+ end
53
+
54
+ def foreign_key_present?
55
+ owner[reflection.foreign_key]
56
+ end
57
+
58
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
59
+ # has_one associations.
60
+ def invertible_for?(record)
61
+ inverse = inverse_reflection_for(record)
62
+ inverse && inverse.macro == :has_one
63
+ end
64
+
65
+ def target_id
66
+ if options[:primary_key]
67
+ owner.send(reflection.name).try(:id)
68
+ else
69
+ owner[reflection.foreign_key]
70
+ end
71
+ end
72
+
73
+ def stale_state
74
+ owner[reflection.foreign_key].to_s
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveFileRecord::Associations::Builder
2
+ class Association
3
+ class_attribute :macro
4
+
5
+ attr_reader :model, :name, :options, :reflection
6
+
7
+ def self.build(model, name, options)
8
+ new(model, name, options).build
9
+ end
10
+
11
+ def initialize(model, name, options)
12
+ @model, @name, @options = model, name, options
13
+ end
14
+
15
+ def mixin
16
+ @model.generated_feature_methods
17
+ end
18
+
19
+ def build
20
+ reflection = model.create_reflection(self.class.macro, name, options, model)
21
+ define_accessors
22
+ reflection
23
+ end
24
+
25
+ private
26
+
27
+ def define_accessors
28
+ define_readers
29
+ define_writers
30
+ end
31
+
32
+ def define_readers
33
+ name = self.name
34
+ mixin.redefine_method(name) do |*params|
35
+ association(name).reader(*params)
36
+ end
37
+ end
38
+
39
+ def define_writers
40
+ name = self.name
41
+ mixin.redefine_method("#{name}=") do |value|
42
+ association(name).writer(value)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
3
+ module ActiveFileRecord::Associations::Builder
4
+ class BelongsTo < Association #< SingularAssociation #:nodoc:
5
+ self.macro = :belongs_to
6
+
7
+ def build
8
+ reflection = super
9
+ reflection
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveFileRecord
2
+ module Associations
3
+ class SingularAssociation < Association #:nodoc:
4
+ # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
+ def reader(force_reload = false)
6
+ if force_reload
7
+ klass.uncached { reload }
8
+ elsif !loaded? || stale_target?
9
+ reload
10
+ end
11
+
12
+ target
13
+ end
14
+
15
+ # Implements the writer method, e.g. foo.items= for Foo.has_many :items
16
+ def writer(record)
17
+ replace(record)
18
+ end
19
+
20
+ def create(attributes = {}, options = {}, &block)
21
+ create_record(attributes, options, &block)
22
+ end
23
+
24
+ def create!(attributes = {}, options = {}, &block)
25
+ create_record(attributes, options, true, &block)
26
+ end
27
+
28
+ def build(attributes = {}, options = {})
29
+ record = build_record(attributes, options)
30
+ yield(record) if block_given?
31
+ set_new_record(record)
32
+ record
33
+ end
34
+
35
+ private
36
+
37
+ def create_scope
38
+ scoped.scope_for_create.stringify_keys.except(klass.primary_key)
39
+ end
40
+
41
+ def find_target
42
+ scoped.first.tap { |record| set_inverse_instance(record) }
43
+ end
44
+
45
+ # Implemented by subclasses
46
+ def replace(record)
47
+ raise NotImplementedError, "Subclasses must implement a replace(record) method"
48
+ end
49
+
50
+ def set_new_record(record)
51
+ replace(record)
52
+ end
53
+
54
+ def create_record(attributes, options, raise_error = false)
55
+ record = build_record(attributes, options)
56
+ yield(record) if block_given?
57
+ saved = record.save
58
+ set_new_record(record)
59
+ raise RecordInvalid.new(record) if !saved && raise_error
60
+ record
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveFileRecord
2
+ module AttributeAssignment
3
+
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::MassAssignmentSecurity
6
+
7
+ def attributes=(new_attributes)
8
+ return unless new_attributes.is_a?(Hash)
9
+
10
+ assign_attributes(new_attributes)
11
+ end
12
+
13
+ def assign_attributes(new_attributes, options = {})
14
+ return if new_attributes.blank?
15
+
16
+ attributes = new_attributes.stringify_keys
17
+ attributes = sanitize_for_mass_assignment(attributes) unless options[:without_protection]
18
+
19
+ attributes.each do |k, v|
20
+ send("#{k}=", v)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ module ActiveFileRecord
2
+ module AttributeMethods
3
+
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::AttributeMethods
6
+
7
+ included do
8
+ include Read
9
+ include Write
10
+
11
+ class_attribute :_fields
12
+ self._fields = []
13
+
14
+ def [](attr_name)
15
+ read_attribute(attr_name)
16
+ end
17
+
18
+ def []=(attr_name, value)
19
+ write_attribute(attr_name, value)
20
+ end
21
+ end
22
+
23
+ def attribute_names
24
+ @attributes.keys
25
+ end
26
+
27
+
28
+ module ClassMethods
29
+ def fields(*names)
30
+ self._fields += names
31
+ define_attribute_methods names
32
+ end
33
+
34
+ def initialize_attributes(fields_names)
35
+ Hash[fields_names.map {|attribute| [attribute, nil]}]
36
+ end
37
+
38
+ def generated_methods
39
+ @generated_methods ||= begin
40
+ mod = Module.new
41
+ include(mod)
42
+ mod
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+ end