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
@@ -0,0 +1,26 @@
1
+ module ActiveFileRecord
2
+ module AttributeMethods
3
+ module Read
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::AttributeMethods
6
+
7
+ included do
8
+ attribute_method_prefix ""
9
+ end
10
+
11
+ module ClassMethods
12
+ def define_method_attribute(attr_name)
13
+ generated_attribute_methods.send(:define_method, "#{attr_name}") do
14
+ read_attribute(attr_name.to_s)
15
+ end
16
+ end
17
+ end
18
+
19
+ def read_attribute(attr_name)
20
+ attr_name = attr_name.to_sym
21
+ @attributes[attr_name]
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ module ActiveFileRecord
2
+ module AttributeMethods
3
+ module Write
4
+ extend ActiveSupport::Concern
5
+ include ActiveModel::AttributeMethods
6
+
7
+ included do
8
+ attribute_method_suffix "="
9
+ end
10
+
11
+ module ClassMethods
12
+ protected
13
+ def define_method_attribute=(attr_name)
14
+ generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
15
+ write_attribute(attr_name, new_value)
16
+ end
17
+ end
18
+ end
19
+
20
+ def write_attribute(attr_name, value)
21
+ attr_name = attr_name.to_sym
22
+ @attributes[attr_name] = value
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,81 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module ActiveFileRecord
4
+
5
+ class MissingAttributeError < NoMethodError
6
+ end
7
+
8
+ class RecordNotFound < Exception
9
+ end
10
+
11
+ class Base
12
+ include Criteria
13
+ include Persistence
14
+ include Validations
15
+ include Callbacks
16
+ include AttributeAssignment
17
+ include AttributeMethods
18
+ include Associations
19
+ extend Criteria
20
+ include Scoping
21
+ include Inheritance
22
+ include Reflection
23
+ include Integration
24
+
25
+ extend ActiveModel::Naming
26
+
27
+ fields :id
28
+
29
+ attr_reader :attributes, :new_record, :destroyed
30
+ attr_accessor :filename
31
+
32
+
33
+ def initialize(attributes = nil, options = {})
34
+ @new_record = true
35
+ @destroyed = false
36
+ @attributes = self.class.initialize_attributes(self.class._fields.dup)
37
+ @association_cache = {}
38
+ assign_attributes(attributes, options) if attributes
39
+ end
40
+
41
+ def init_with(attributes = nil)
42
+ @association_cache = {}
43
+ @attributes = self.class.initialize_attributes(self.class._fields.dup)
44
+ assign_attributes(attributes, {:without_protection => true}) if attributes
45
+ @new_record = false
46
+ @destroyed = false
47
+ self
48
+ end
49
+
50
+ class << self
51
+ def filename
52
+ @filename ||= self.name.underscore
53
+ end
54
+
55
+ def filename=(name)
56
+ @filename = name
57
+ end
58
+
59
+ def primary_key
60
+ :id
61
+ end
62
+
63
+ def generated_feature_methods
64
+ @generated_feature_methods ||= begin
65
+ mod = const_set(:GeneratedFeatureMethods, Module.new)
66
+ include mod
67
+ mod
68
+ end
69
+ end
70
+
71
+ def file_handler
72
+ @file_handler ||= FileHandler.new(filename)
73
+ end
74
+
75
+ def relation
76
+ relation = Relation.new(self, file_handler)
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveFileRecord
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extend ActiveModel::Callbacks
7
+ include ActiveModel::Validations::Callbacks
8
+
9
+ define_model_callbacks :save, :destroy
10
+ end
11
+
12
+
13
+ def save
14
+ run_callbacks(:save) { super }
15
+ end
16
+
17
+ def destroy
18
+ run_callbacks(:destroy) { super }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module ActiveFileRecord
4
+ module Criteria
5
+ delegate :find, :first, :last, :all, :to => :scoped
6
+ delegate :select, :where, :to => :scoped
7
+
8
+ extend ActiveSupport::Concern
9
+ end
10
+ end
@@ -0,0 +1,66 @@
1
+ module ActiveFileRecord
2
+ class FileHandler
3
+ attr_reader :filename
4
+
5
+ def initialize(filename)
6
+ @filename = filename
7
+ end
8
+
9
+ def add_record(klass)
10
+ entries = file_entries(filename) || []
11
+
12
+ p entries
13
+
14
+ entries << klass.attributes
15
+ rewrite filename, entries
16
+ end
17
+
18
+ def select_all(relation)
19
+ load(relation)
20
+ end
21
+
22
+ private
23
+ def rewrite(filename, entries)
24
+ File.open(entries_file(filename), "w") do |f|
25
+ f.write(ActiveSupport::JSON.encode entries)
26
+ end
27
+ end
28
+
29
+ def load(relation)
30
+ result = []
31
+ entries = file_entries relation.klass.filename
32
+
33
+ if entries.is_a?(Array)
34
+ entries.each do |entry|
35
+ if check_entry(entry, relation.where_values)
36
+ result << relation.klass.instantiate(entry)
37
+ end
38
+ end
39
+ end
40
+
41
+ p "Searchin in #{relation.klass.filename}"
42
+
43
+ result
44
+ end
45
+
46
+ def check_entry(entry, where)
47
+ suitable = true
48
+
49
+ where.each do |condition|
50
+ suitable &&= entry[condition.operand1.name.to_s].send(condition.operator, condition.operand2)
51
+ return false if !suitable
52
+ end
53
+
54
+ true
55
+ end
56
+
57
+ def file_entries(filename)
58
+ ActiveSupport::JSON.decode(File.read(entries_file filename))
59
+ end
60
+
61
+ def entries_file(filename)
62
+ File.join(Rails.root, "records_storage", filename)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveFileRecord
2
+ class ActiveFile
3
+ attr_accessor :filename
4
+
5
+ def initialize(filename)
6
+ @filename = filename
7
+ end
8
+
9
+ def [] name
10
+ Attribute.new self, name
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveFileRecord
2
+ class Attribute < Struct.new :relation, :name
3
+ include ActiveFileRecord::Predications
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'active_support/concern'
2
+ module ActiveFileRecord
3
+ module Inheritance
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def instantiate(record)
9
+ instance = self.allocate.init_with(record)
10
+ instance
11
+ end
12
+
13
+ protected
14
+
15
+ def compute_type(type_name)
16
+ if type_name.match(/^::/)
17
+ # If the type is prefixed with a scope operator then we assume that
18
+ # the type_name is an absolute reference.
19
+ ActiveSupport::Dependencies.constantize(type_name)
20
+ else
21
+ # Build a list of candidates to search for
22
+ candidates = []
23
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
24
+ candidates << type_name
25
+
26
+ candidates.each do |candidate|
27
+ begin
28
+ constant = ActiveSupport::Dependencies.constantize(candidate)
29
+ return constant if candidate == constant.to_s
30
+ rescue NameError => e
31
+ # We don't want to swallow NoMethodError < NameError errors
32
+ raise e unless e.instance_of?(NameError)
33
+ end
34
+ end
35
+
36
+ raise NameError, "uninitialized constant #{candidates.first}"
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveFileRecord
2
+ module Integration
3
+ def to_param
4
+ id && id.to_s
5
+ end
6
+
7
+ def to_key
8
+ key = self.id
9
+ [key] if key
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_file_record/nodes/binary'
2
+ require 'active_file_record/nodes/equality'
@@ -0,0 +1,19 @@
1
+ module ActiveFileRecord
2
+ module Nodes
3
+ class Binary
4
+ attr_accessor :left, :right
5
+
6
+ def initialize left, right
7
+ @left = left
8
+ @right = right
9
+ end
10
+
11
+ def initialize_copy other
12
+ super
13
+ @left = @left.clone if @left
14
+ @right = @right.clone if @right
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveFileRecord
2
+ module Nodes
3
+ class Equality < ActiveFileRecord::Nodes::Binary
4
+ attr_reader :operator
5
+
6
+ def operator; :== end
7
+ alias :operand1 :left
8
+ alias :operand2 :right
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,53 @@
1
+ module ActiveFileRecord
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Serializers::JSON
5
+
6
+ included do
7
+ class_attribute :include_root_in_json
8
+ self.include_root_in_json = false
9
+ end
10
+
11
+ def save
12
+ create
13
+ end
14
+
15
+ def create
16
+ self.id = generate_id
17
+ self.class.file_handler.add_record(self)
18
+ end
19
+
20
+ def new_record?
21
+ @new_record
22
+ end
23
+
24
+ def destroyed?
25
+ @destroyed
26
+ end
27
+
28
+ # Returns if the record is persisted, i.e. it's not a new record and it was
29
+ # not destroyed.
30
+ def persisted?
31
+ !(new_record? || destroyed?)
32
+ end
33
+
34
+ #def update
35
+ #end
36
+
37
+ #def destroy
38
+ #end
39
+
40
+ #def update_attributes(attributes)
41
+ #@attributes.merge!(attributes)
42
+ #sanitize_attributes
43
+ #save
44
+ #end
45
+
46
+ private
47
+ def generate_id
48
+ last_entry = self.class.last
49
+ last_entry ? last_entry.id + 1 : 1
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveFileRecord
2
+ module Predications
3
+
4
+ def eq other
5
+ Nodes::Equality.new self, other
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,140 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/core_ext/object/inclusion'
3
+
4
+ module ActiveFileRecord
5
+ module Reflection
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :reflections
10
+ self.reflections = {}
11
+ end
12
+
13
+ module ClassMethods
14
+ def create_reflection(macro, name, options, active_file_record)
15
+ klass = options[:through] ? ThroughReflection : AssociationReflection
16
+ reflection = klass.new(macro, name, options, active_file_record)
17
+
18
+ self.reflections = self.reflections.merge(name => reflection)
19
+ reflection
20
+ end
21
+
22
+ def reflect_on_association(association)
23
+ reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
24
+ end
25
+ end
26
+
27
+ class MacroReflection
28
+ attr_reader :name
29
+ attr_reader :macro
30
+ attr_reader :options
31
+ attr_reader :active_file_record
32
+
33
+ def initialize(macro, name, options, active_file_record)
34
+ @macro = macro
35
+ @name = name
36
+ @options = options
37
+ @active_file_record = active_file_record
38
+ end
39
+
40
+ def klass
41
+ @klass ||= class_name.constantize
42
+ end
43
+
44
+ def class_name
45
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
46
+ end
47
+
48
+ private
49
+ def derive_class_name
50
+ name.to_s.camelize
51
+ end
52
+ end
53
+
54
+ class AssociationReflection < MacroReflection #:nodoc:
55
+ def klass
56
+ @klass ||= active_file_record.send(:compute_type, class_name)
57
+ end
58
+
59
+ def initialize(macro, name, options, active_record)
60
+ super
61
+ @collection = macro.in?([:has_many, :has_and_belongs_to_many])
62
+ end
63
+
64
+ def build_association(*options, &block)
65
+ klass.new(*options, &block)
66
+ end
67
+
68
+ def foreign_key
69
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key
70
+ end
71
+
72
+ def foreign_type
73
+ @foreign_type ||= options[:foreign_type] || "#{name}_type"
74
+ end
75
+
76
+ def type
77
+ @type ||= options[:as] && "#{options[:as]}_type"
78
+ end
79
+
80
+ def primary_key_column
81
+ @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
82
+ end
83
+
84
+ def association_foreign_key
85
+ @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
86
+ end
87
+
88
+ def association_primary_key(klass = nil)
89
+ options[:primary_key] || primary_key(klass || self.klass)
90
+ end
91
+
92
+ def chain
93
+ [self]
94
+ end
95
+
96
+ def nested?
97
+ false
98
+ end
99
+
100
+ def conditions
101
+ [[options[:conditions]].compact]
102
+ end
103
+
104
+ alias :source_macro :macro
105
+
106
+ def has_inverse?
107
+ @options[:inverse_of]
108
+ end
109
+
110
+ def inverse_of
111
+ if has_inverse?
112
+ @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
113
+ end
114
+ end
115
+
116
+ def collection?
117
+ @collection
118
+ end
119
+
120
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
121
+ def belongs_to?
122
+ macro == :belongs_to
123
+ end
124
+
125
+ def association_class
126
+ Associations::BelongsToAssociation
127
+ end
128
+
129
+ private
130
+ def derive_foreign_key
131
+ "#{name}_id"
132
+ end
133
+
134
+ def primary_key(klass)
135
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
136
+ end
137
+ end
138
+
139
+ end
140
+ end