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