active_file_record 0.0.2a → 0.0.3a

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.
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