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.
- data/active_file_record.gemspec +1 -1
- data/lib/active_file_record/associations.rb +57 -0
- data/lib/active_file_record/associations/association.rb +101 -0
- data/lib/active_file_record/associations/association_scope.rb +29 -0
- data/lib/active_file_record/associations/belongs_to_association.rb +78 -0
- data/lib/active_file_record/associations/builder/association.rb +46 -0
- data/lib/active_file_record/associations/builder/belongs_to.rb +13 -0
- data/lib/active_file_record/associations/singular_association.rb +64 -0
- data/lib/active_file_record/attribute_assignment.rb +25 -0
- data/lib/active_file_record/attribute_methods.rb +48 -0
- data/lib/active_file_record/attribute_methods/read.rb +26 -0
- data/lib/active_file_record/attribute_methods/write.rb +27 -0
- data/lib/active_file_record/base.rb +81 -0
- data/lib/active_file_record/callbacks.rb +21 -0
- data/lib/active_file_record/criteria.rb +10 -0
- data/lib/active_file_record/file_handler.rb +66 -0
- data/lib/active_file_record/file_handler/active_file.rb +13 -0
- data/lib/active_file_record/file_handler/attribute.rb +5 -0
- data/lib/active_file_record/inheritance.rb +42 -0
- data/lib/active_file_record/integration.rb +13 -0
- data/lib/active_file_record/nodes.rb +2 -0
- data/lib/active_file_record/nodes/binary.rb +19 -0
- data/lib/active_file_record/nodes/equality.rb +11 -0
- data/lib/active_file_record/persistence.rb +53 -0
- data/lib/active_file_record/predications.rb +9 -0
- data/lib/active_file_record/reflection.rb +140 -0
- data/lib/active_file_record/relation.rb +73 -0
- data/lib/active_file_record/relation/finder_methods.rb +88 -0
- data/lib/active_file_record/relation/predicate_builder.rb +21 -0
- data/lib/active_file_record/relation/search_methods.rb +65 -0
- data/lib/active_file_record/scoping.rb +28 -0
- data/lib/active_file_record/scoping/named.rb +72 -0
- data/lib/active_file_record/validations.rb +12 -0
- data/lib/active_file_record/version.rb +1 -1
- 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,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,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,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,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,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
|