my_active_record 0.0.1
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/.gitignore +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +32 -0
- data/lib/.DS_Store +0 -0
- data/lib/my_active_record.rb +3 -0
- data/lib/my_active_record/.DS_Store +0 -0
- data/lib/my_active_record/associations.rb +3 -0
- data/lib/my_active_record/associations/belongs_to_association.rb +12 -0
- data/lib/my_active_record/associations/has_many_association.rb +12 -0
- data/lib/my_active_record/associations/has_one_association.rb +12 -0
- data/lib/my_active_record/base.rb +80 -0
- data/lib/my_active_record/csv_adapter.rb +65 -0
- data/lib/my_active_record/database.rb +59 -0
- data/spec/base_spec.rb +30 -0
- data/spec/database/cars.csv.sample +2 -0
- data/spec/database/details.csv.sample +3 -0
- data/spec/database/users.csv.sample +3 -0
- data/spec/database_spec.rb +32 -0
- data/spec/models/car.rb +4 -0
- data/spec/models/detail.rb +3 -0
- data/spec/models/user.rb +3 -0
- data/spec/spec_helper.rb +23 -0
- metadata +67 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.13)
|
5
|
+
i18n (= 0.6.1)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
coderay (1.0.9)
|
8
|
+
diff-lcs (1.2.4)
|
9
|
+
i18n (0.6.1)
|
10
|
+
method_source (0.8.1)
|
11
|
+
multi_json (1.7.2)
|
12
|
+
pry (0.9.12)
|
13
|
+
coderay (~> 1.0.5)
|
14
|
+
method_source (~> 0.8)
|
15
|
+
slop (~> 3.4)
|
16
|
+
rspec (2.13.0)
|
17
|
+
rspec-core (~> 2.13.0)
|
18
|
+
rspec-expectations (~> 2.13.0)
|
19
|
+
rspec-mocks (~> 2.13.0)
|
20
|
+
rspec-core (2.13.1)
|
21
|
+
rspec-expectations (2.13.0)
|
22
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
23
|
+
rspec-mocks (2.13.1)
|
24
|
+
slop (3.4.4)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
activesupport (~> 3.2.13)
|
31
|
+
pry
|
32
|
+
rspec
|
data/lib/.DS_Store
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MyActiveRecord
|
2
|
+
module Associations
|
3
|
+
def belongs_to(*associations_list)
|
4
|
+
associations_list.each do |association_name|
|
5
|
+
define_method(association_name) do
|
6
|
+
association_model = Database.table_to_model(association_name.to_s.pluralize)
|
7
|
+
association_model.where(:id => self["#{association_name.to_s.singularize}_id"]).first
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MyActiveRecord
|
2
|
+
module Associations
|
3
|
+
def has_many(*associations_list)
|
4
|
+
associations_list.each do |association_name|
|
5
|
+
define_method(association_name) do
|
6
|
+
association_model = Database.table_to_model(association_name.to_s.pluralize)
|
7
|
+
association_model.where(self.class.table_name.singularize + "_id" => self.id)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module MyActiveRecord
|
2
|
+
module Associations
|
3
|
+
def has_one(*associations_list)
|
4
|
+
associations_list.each do |association_name|
|
5
|
+
define_method(association_name) do
|
6
|
+
association_model = Database.table_to_model(association_name.to_s.pluralize)
|
7
|
+
association_model.where(self.class.table_name.singularize + "_id" => self.id).first
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'my_active_record/associations'
|
4
|
+
|
5
|
+
module MyActiveRecord
|
6
|
+
class Base
|
7
|
+
extend ActiveSupport::DescendantsTracker
|
8
|
+
extend Associations
|
9
|
+
|
10
|
+
def initialize(params = {})
|
11
|
+
params.each do |key, value|
|
12
|
+
self[key] = value
|
13
|
+
end
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def table_name
|
19
|
+
name.to_s.demodulize.underscore.pluralize
|
20
|
+
end
|
21
|
+
|
22
|
+
def fields=(fields_list)
|
23
|
+
fields_list.each do |field_name|
|
24
|
+
attr_accessor field_name
|
25
|
+
end
|
26
|
+
@fields = fields_list
|
27
|
+
end
|
28
|
+
|
29
|
+
def fields
|
30
|
+
@fields
|
31
|
+
end
|
32
|
+
|
33
|
+
def find(model_id)
|
34
|
+
where(:id => model_id).first
|
35
|
+
end
|
36
|
+
|
37
|
+
def where(params)
|
38
|
+
Database.where(table_name, params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_data(data)
|
42
|
+
model = new
|
43
|
+
fields.each_with_index do |field_name,index|
|
44
|
+
model[field_name] = data[index]
|
45
|
+
end
|
46
|
+
model
|
47
|
+
end
|
48
|
+
|
49
|
+
def create(params)
|
50
|
+
new(params).save
|
51
|
+
end
|
52
|
+
|
53
|
+
def last
|
54
|
+
Database.last(table_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def save
|
59
|
+
if self.id
|
60
|
+
Database.update(self.class.table_name, self)
|
61
|
+
else
|
62
|
+
Database.insert(self.class.table_name, self)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def [](key)
|
67
|
+
send(key)
|
68
|
+
end
|
69
|
+
|
70
|
+
def []=(key, value)
|
71
|
+
send("#{key}=", value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_row
|
75
|
+
self.class.fields.map do |field|
|
76
|
+
self[field]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module MyActiveRecord
|
4
|
+
class CsvAdapter
|
5
|
+
def initialize(params)
|
6
|
+
@db_path = params[:db_path]
|
7
|
+
raise "DB path is not set" unless @db_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_table_schema(table_name)
|
11
|
+
raise("Csv file #{table_name}.csv should be created") unless File.exists?(path_to_table(table_name))
|
12
|
+
schema = CSV.table(path_to_table(table_name)).headers
|
13
|
+
raise("ID column should exist") unless schema.include?(:id)
|
14
|
+
schema
|
15
|
+
end
|
16
|
+
|
17
|
+
def where(table_name, params)
|
18
|
+
results = []
|
19
|
+
read_table(table_name).each do |row|
|
20
|
+
all_terms_succeded = params.all? do |key,value|
|
21
|
+
row[table_schema(table_name).find_index(key.to_sym)].to_s == value.to_s
|
22
|
+
end
|
23
|
+
results << row if all_terms_succeded
|
24
|
+
end
|
25
|
+
results
|
26
|
+
end
|
27
|
+
|
28
|
+
def insert(table_name, row)
|
29
|
+
CSV.open(path_to_table(table_name), "ab") do |csv|
|
30
|
+
csv << row
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def update(table_name, row)
|
35
|
+
old_csv = read_table(table_name)
|
36
|
+
CSV.open(path_to_table(table_name), "wb") do |csv|
|
37
|
+
old_csv.each do |old_row|
|
38
|
+
if row[0] == old_row[0]
|
39
|
+
csv << row
|
40
|
+
else
|
41
|
+
csv << old_row
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def last(table_name)
|
48
|
+
read_table(table_name).last
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def read_table(table_name)
|
54
|
+
CSV.read(path_to_table(table_name), :headers => true).to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
def table_schema(table_name)
|
58
|
+
load_table_schema(table_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def path_to_table(table_name)
|
62
|
+
"#{@db_path}/#{table_name}.csv"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module MyActiveRecord
|
2
|
+
class Database
|
3
|
+
class << self
|
4
|
+
def establish_connection(params)
|
5
|
+
if params.delete(:adapter) == "csv"
|
6
|
+
self.adapter = MyActiveRecord::CsvAdapter.new(params)
|
7
|
+
else
|
8
|
+
raise "Unsupported adapter type"
|
9
|
+
end
|
10
|
+
load_database_schema
|
11
|
+
end
|
12
|
+
|
13
|
+
def table_to_model(table_name)
|
14
|
+
@tables[table_name]
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_database_schema
|
18
|
+
@tables = {}
|
19
|
+
MyActiveRecord::Base.descendants.each do |descendant|
|
20
|
+
@tables[descendant.table_name] = descendant
|
21
|
+
load_table_schema(descendant.table_name) if descendant.name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_table_schema(table_name)
|
26
|
+
table_to_model(table_name).fields = adapter.load_table_schema(table_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def where(table_name, params)
|
30
|
+
results = adapter.where(table_name, params)
|
31
|
+
model = table_to_model(table_name)
|
32
|
+
results.map do |result|
|
33
|
+
model.load_data(result)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert(table_name, model)
|
38
|
+
model.id = model.class.last.id.to_i + 1
|
39
|
+
adapter.insert(table_name, model.to_row)
|
40
|
+
end
|
41
|
+
|
42
|
+
def update(table_name, model)
|
43
|
+
adapter.update(table_name, model.to_row)
|
44
|
+
end
|
45
|
+
|
46
|
+
def last(table_name)
|
47
|
+
table_to_model(table_name).load_data(adapter.last(table_name))
|
48
|
+
end
|
49
|
+
|
50
|
+
def adapter=(adapter)
|
51
|
+
@adapter = adapter
|
52
|
+
end
|
53
|
+
|
54
|
+
def adapter
|
55
|
+
@adapter || raise("Database adapter is not set")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MyActiveRecord::Base do
|
4
|
+
let(:user) { User.find(1) }
|
5
|
+
let(:user_without_car) { User.find(2) }
|
6
|
+
|
7
|
+
context "associations" do
|
8
|
+
context "#has_one" do
|
9
|
+
it "loads association from db" do
|
10
|
+
user.car.name.should == "Tata Motors Aria"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is empty by default" do
|
14
|
+
user_without_car.car.should be_blank
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "#belongs_to" do
|
19
|
+
it "loads association from db" do
|
20
|
+
Car.find(1).user.name.should == "Pedro Rodriguez Ledesma"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "#has_many" do
|
25
|
+
it "loads association from db" do
|
26
|
+
Car.find(1).details.map(&:name).should == ["Windscreen", "Brakes"]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MyActiveRecord::Database do
|
4
|
+
context "loading from db" do
|
5
|
+
it "loads schema" do
|
6
|
+
User.new.methods.should include(:age)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "finds data" do
|
10
|
+
User.find(1).name.should == "Pedro Rodriguez Ledesma"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "saving to db" do
|
15
|
+
it "creates new records" do
|
16
|
+
User.create(:name => "Test User")
|
17
|
+
User.last.name.should == "Test User"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "generates id" do
|
21
|
+
User.create(:name => "Test User")
|
22
|
+
User.last.id.should == "3"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "updates records" do
|
26
|
+
user = User.find(1)
|
27
|
+
user.name = "Updated name"
|
28
|
+
user.save
|
29
|
+
User.find(1).name.should == "Updated name"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/models/car.rb
ADDED
data/spec/models/user.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "my_active_record"
|
2
|
+
require 'models/user.rb'
|
3
|
+
require 'models/car.rb'
|
4
|
+
require 'models/detail.rb'
|
5
|
+
require 'pry'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
SPEC_ROOT = File.expand_path(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.before do
|
12
|
+
FileUtils.copy_file("#{SPEC_ROOT}/database/cars.csv.sample", "#{SPEC_ROOT}/database/cars.csv")
|
13
|
+
FileUtils.copy_file("#{SPEC_ROOT}/database/users.csv.sample", "#{SPEC_ROOT}/database/users.csv")
|
14
|
+
FileUtils.copy_file("#{SPEC_ROOT}/database/details.csv.sample", "#{SPEC_ROOT}/database/details.csv")
|
15
|
+
MyActiveRecord::Database.establish_connection(:adapter => "csv", :db_path => "spec/database")
|
16
|
+
end
|
17
|
+
|
18
|
+
config.after do
|
19
|
+
FileUtils.rm("#{SPEC_ROOT}/database/cars.csv")
|
20
|
+
FileUtils.rm("#{SPEC_ROOT}/database/users.csv")
|
21
|
+
FileUtils.rm("#{SPEC_ROOT}/database/details.csv")
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: my_active_record
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Osmanov
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-12 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ''
|
15
|
+
email: grandison@mail.ru
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- Gemfile
|
22
|
+
- Gemfile.lock
|
23
|
+
- lib/.DS_Store
|
24
|
+
- lib/my_active_record.rb
|
25
|
+
- lib/my_active_record/.DS_Store
|
26
|
+
- lib/my_active_record/associations.rb
|
27
|
+
- lib/my_active_record/associations/belongs_to_association.rb
|
28
|
+
- lib/my_active_record/associations/has_many_association.rb
|
29
|
+
- lib/my_active_record/associations/has_one_association.rb
|
30
|
+
- lib/my_active_record/base.rb
|
31
|
+
- lib/my_active_record/csv_adapter.rb
|
32
|
+
- lib/my_active_record/database.rb
|
33
|
+
- spec/base_spec.rb
|
34
|
+
- spec/database/cars.csv.sample
|
35
|
+
- spec/database/details.csv.sample
|
36
|
+
- spec/database/users.csv.sample
|
37
|
+
- spec/database_spec.rb
|
38
|
+
- spec/models/car.rb
|
39
|
+
- spec/models/detail.rb
|
40
|
+
- spec/models/user.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
homepage: http://rubygems.org/gems/my_active_record
|
43
|
+
licenses: []
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.24
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: My realization of the active record pattern
|
66
|
+
test_files: []
|
67
|
+
has_rdoc:
|