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