my_active_record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rspec'
4
+ gem "activesupport", "~> 3.2.13"
5
+ gem 'pry'
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
@@ -0,0 +1,3 @@
1
+ require 'my_active_record/base'
2
+ require 'my_active_record/csv_adapter'
3
+ require 'my_active_record/database'
Binary file
@@ -0,0 +1,3 @@
1
+ require 'my_active_record/associations/belongs_to_association'
2
+ require 'my_active_record/associations/has_one_association'
3
+ require 'my_active_record/associations/has_many_association'
@@ -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,2 @@
1
+ id,name,user_id
2
+ 1,"Tata Motors Aria",1
@@ -0,0 +1,3 @@
1
+ id,name,car_id
2
+ 1,"Windscreen",1
3
+ 2,"Brakes",1
@@ -0,0 +1,3 @@
1
+ id,name,age
2
+ 1,"Pedro Rodriguez Ledesma",35
3
+ 2,"Fernando Gonzalez",32
@@ -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
@@ -0,0 +1,4 @@
1
+ class Car < MyActiveRecord::Base
2
+ belongs_to :user
3
+ has_many :details
4
+ end
@@ -0,0 +1,3 @@
1
+ class Detail < MyActiveRecord::Base
2
+ belongs_to :car
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < MyActiveRecord::Base
2
+ has_one :car
3
+ end
@@ -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: