minimapper 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,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.3-p0-patched@minimapper --create
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ # 1.9 rubies
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - rbx-19mode
7
+ - jruby-19mode
8
+
9
+ # 1.8 rubies
10
+ - ree
11
+ - 1.8.7
12
+ - jruby-18mode
13
+ - rbx-18mode
14
+ script: "script/ci"
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # We don't require active_record to use minimapper, only to
4
+ # use minimapper/ar. We do require it for the tests though :)
5
+ gem "activerecord"
6
+ gem "rspec"
7
+
8
+ platforms :ruby do
9
+ gem "sqlite3"
10
+ gem "pg"
11
+ gem "mysql2"
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem "activerecord-jdbcsqlite3-adapter"
16
+ gem "activerecord-jdbcpostgresql-adapter"
17
+ gem "activerecord-jdbcmysql-adapter"
18
+ end
19
+
20
+ # Specify your gem's dependencies in minimapper.gemspec
21
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Joakim Kolsjö
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ ![Build status](https://secure.travis-ci.org/joakimk/minimapper.png) | [builds](https://travis-ci.org/#!/joakimk/minimapper/builds)
2
+
3
+ # Minimapper
4
+
5
+ A minimalistic way of separating your models from ORMs like ActiveRecord that allows you to swap out your persistance layer for an in-memory implementation in tests or use different persistance for different models.
6
+
7
+ If you're following good style you're probably already pushing all knowledge of your ORM down into your models or model-layer classes. This takes it a step further and let's you work with your models without depending on heavy frameworks like rails or needing a database.
8
+
9
+ Minimapper is a partial [repository-pattern](http://martinfowler.com/eaaCatalog/repository.html) implementation (it implements repositories and data mappers but not critera builders).
10
+
11
+ ## Only the most basic API
12
+
13
+ This library only implements the most basic persistance API (mostly just CRUD). Any significant additions will be made into separate gems (with names like "minimapper-FOO").
14
+
15
+ The reasons for this are:
16
+ * You should be able to feel secure about depending on the API
17
+ * It should be possible to learn all it does in a short time
18
+ * It should be simple to add an adapter for a new database
19
+ * It should be simple to maintain minimapper
20
+
21
+ ## Compatibility
22
+
23
+ This gem is tested against all major rubies in both 1.8 and 1.9, see [.travis.yml](https://github.com/joakimk/minimapper/blob/master/.travis.yml). For each ruby version, the SQL mappers are tested against SQLite3, PostgreSQL and MySQL.
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ gem 'minimapper'
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Or install it yourself as:
36
+
37
+ $ gem install minimapper
38
+
39
+ Please avoid installing directly from the github repository. Code will be pushed there that might fail in [CI](https://travis-ci.org/#!/joakimk/minimapper/builds) (because testing all permutations of ruby versions and databases locally isn't practical). Gem releases are only done when CI is green.
40
+
41
+ ## Usage
42
+
43
+ You can use the mappers directly like this:
44
+
45
+ ``` ruby
46
+ class User < Minimapper::Entity
47
+ attributes :name, :email
48
+ validates :name, presence: true
49
+ end
50
+
51
+ class UserMapper < Minimapper::Memory
52
+ end
53
+
54
+ # Creating
55
+ user = User.new(name: "Joe")
56
+ mapper = UserMapper.new
57
+ mapper.create(user)
58
+
59
+ # Finding
60
+ user = mapper.find(1)
61
+
62
+ # Updating
63
+ user.name = "Joey"
64
+ mapper.update(user)
65
+
66
+ # Deleting
67
+ mapper.delete(user)
68
+
69
+ # Deleting all
70
+ mapper.delete_all
71
+ ```
72
+
73
+ Or though a repository:
74
+
75
+ ``` ruby
76
+ repository = Minimapper::Repository.build({
77
+ users: UserMapper.new,
78
+ projects: ProjectMapper.new
79
+ })
80
+
81
+ repository.users.find(1)
82
+ ```
83
+
84
+ ## Using the ActiveRecord mapper
85
+
86
+ ``` ruby
87
+ module AR
88
+ class UserMapper < Minimapper::AR
89
+ end
90
+
91
+ class User < ActiveRecord::Base
92
+ attr_accessible :name, :email
93
+ end
94
+ end
95
+
96
+ user = User.new(name: "Joe")
97
+ mapper = AR::UserMapper.new
98
+ mapper.create(user)
99
+ ```
100
+
101
+ ## Implementing custom queries
102
+
103
+ *todo* show how, talk about shared examples.
104
+
105
+ ## Implementing another mapper
106
+
107
+ *todo*: how to use the shared examples
108
+
109
+ ## Inspiration
110
+
111
+ Jason Roelofs:
112
+ * [Designing a Rails App](http://jasonroelofs.com/2012/05/29/designing-a-rails-app-part-1/) (find the whole series of posts)
113
+
114
+ Robert "Uncle Bob" Martin:
115
+ * [Architecture: The Lost Years](http://www.confreaks.com/videos/759-rubymidwest2011-keynote-architecture-the-lost-years)
116
+ * [The Clean Architecture](http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html)
117
+
118
+ ## Running the tests
119
+
120
+ You need mysql and postgres installed (but they do not have to be running) to be able to run bundle. The sql-mapper tests use sqlite3 by default.
121
+
122
+ bundle
123
+ rake
124
+
125
+ ## Contributing
126
+
127
+ 0. Read "Only the most basic API" above
128
+ 1. Fork it
129
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
130
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
131
+ 4. Don't forget to write test
132
+ 5. Push to the branch (`git push origin my-new-feature`)
133
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ namespace :spec do
4
+ task :unit do
5
+ puts "Running unit tests."
6
+ spec_helper_path = File.expand_path("unit/spec_helper.rb")
7
+ system("rspec", "-r#{spec_helper_path}", *Dir["unit/**/*_spec.rb"]) || exit(1)
8
+ end
9
+
10
+ task :integrated do
11
+ puts "Running integrated tests."
12
+ integrated_helper_path = File.expand_path("spec/spec_helper.rb")
13
+ system("rspec", "-r#{integrated_helper_path}", *Dir["spec/**/*_spec.rb"]) || exit(1)
14
+ end
15
+ end
16
+
17
+ task :spacer do
18
+ puts
19
+ end
20
+
21
+ task :spec => [ :"spec:unit", :spacer, :"spec:integrated" ]
22
+ task :default => :spec
@@ -0,0 +1,95 @@
1
+ require "minimapper/common"
2
+
3
+ module Minimapper
4
+ class AR
5
+ # Create
6
+ def create(entity)
7
+ if entity.valid?
8
+ entity.id = record_klass.create!(entity.attributes).id
9
+ else
10
+ false
11
+ end
12
+ end
13
+
14
+ # Read
15
+ def find(id)
16
+ entity_for(find_record(id))
17
+ end
18
+
19
+ def all
20
+ record_klass.all.map { |record| entity_klass.new(record.attributes) }
21
+ end
22
+
23
+ def first
24
+ entity_for(record_klass.order("id ASC").first)
25
+ end
26
+
27
+ def last
28
+ entity_for(record_klass.order("id ASC").last)
29
+ end
30
+
31
+ def count
32
+ record_klass.count
33
+ end
34
+
35
+ # Update
36
+ def update(entity)
37
+ if entity.valid?
38
+ record_for(entity).update_attributes!(entity.attributes)
39
+ true
40
+ else
41
+ false
42
+ end
43
+ end
44
+
45
+ # Delete
46
+ def delete(entity)
47
+ delete_by_id(entity.id)
48
+ end
49
+
50
+ def delete_by_id(id)
51
+ find_record(id).delete
52
+ end
53
+
54
+ def delete_all
55
+ record_klass.delete_all
56
+ end
57
+
58
+ private
59
+
60
+ # TODO: write tests for these, they are indirectly tested by the fact
61
+ # that the test suite runs, but there could be bugs and I'll extract minimapper soon.
62
+
63
+ # Will attempt to use AR:Project as the record class
64
+ # when the mapper class name is AR::ProjectMapper
65
+ def record_klass
66
+ @record_klass ||= self.class.name.gsub(/Mapper/, '').constantize
67
+ @record_klass
68
+ end
69
+
70
+ # Will attempt to use Project as the enity class when
71
+ # the mapper class name is AR::ProjectMapper
72
+ def entity_klass
73
+ @entity_klass ||= ("::" + self.class.name.split('::').last.gsub(/Mapper/, '')).constantize
74
+ @entity_klass
75
+ end
76
+
77
+ def find_record(id)
78
+ (id && record_klass.find_by_id(id)) ||
79
+ raise(Common::CanNotFindEntity, :id => id)
80
+ end
81
+
82
+ def record_for(entity)
83
+ (entity.id && record_klass.find_by_id(entity.id)) ||
84
+ raise(Common::CanNotFindEntity, entity.inspect)
85
+ end
86
+
87
+ def entity_for(record)
88
+ if record
89
+ entity_klass.new(record.attributes)
90
+ else
91
+ nil
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+ module Minimapper
2
+ module Common
3
+ CanNotFindEntity = Class.new(StandardError)
4
+ end
5
+ end
@@ -0,0 +1,44 @@
1
+ # Minimapper does not require you to use this entity base class. It requires a
2
+ # few methods to be present, like valid?, attributes, attributes=.
3
+ #
4
+ # I plan to convert this to a module and add shared examples that cover
5
+ # the API which minimapper depends upon.
6
+ #
7
+ # This class also does some things needed for it to work well with rails.
8
+ require 'informal'
9
+
10
+ module Minimapper
11
+ class Entity
12
+ include Informal::Model
13
+
14
+ def self.attributes(*list)
15
+ list.each do |attribute|
16
+ define_method(attribute) do
17
+ instance_variable_get("@#{attribute}")
18
+ end
19
+
20
+ define_method("#{attribute}=") do |value|
21
+ instance_variable_set("@#{attribute}", value)
22
+ @attributes[attribute] = value
23
+ end
24
+ end
25
+ end
26
+
27
+ def initialize(*opts)
28
+ @attributes = {}
29
+ super(*opts)
30
+ end
31
+
32
+ def to_param
33
+ id
34
+ end
35
+
36
+ def persisted?
37
+ id
38
+ end
39
+
40
+ attributes :id, :created_at, :updated_at
41
+
42
+ attr_reader :attributes
43
+ end
44
+ end
@@ -0,0 +1,78 @@
1
+ module Minimapper
2
+ class Memory
3
+ def initialize
4
+ @store = []
5
+ @last_id = 0
6
+ end
7
+
8
+ # Create
9
+ def create(entity)
10
+ if entity.valid?
11
+ entity.id = next_id
12
+ store.push(entity.dup)
13
+ last_id
14
+ else
15
+ false
16
+ end
17
+ end
18
+
19
+ # Read
20
+ def find(id)
21
+ find_internal(id).dup
22
+ end
23
+
24
+ def all
25
+ store.map { |entity| entity.dup }
26
+ end
27
+
28
+ def first
29
+ store.first && store.first.dup
30
+ end
31
+
32
+ def last
33
+ store.last && store.last.dup
34
+ end
35
+
36
+ def count
37
+ all.size
38
+ end
39
+
40
+ # Update
41
+ def update(entity)
42
+ if entity.valid?
43
+ known_entity = find_internal(entity.id)
44
+ known_entity.attributes = entity.attributes
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ # Delete
52
+ def delete(entity)
53
+ delete_by_id(entity.id)
54
+ end
55
+
56
+ def delete_by_id(id)
57
+ entity = find_internal(id)
58
+ store.delete(entity)
59
+ end
60
+
61
+ def delete_all
62
+ store.clear
63
+ end
64
+
65
+ private
66
+
67
+ def find_internal(id)
68
+ (id && store.find { |e| e.id == id.to_i }) ||
69
+ raise(Common::CanNotFindEntity, :id => id)
70
+ end
71
+
72
+ def next_id
73
+ @last_id += 1
74
+ end
75
+
76
+ attr_reader :store, :last_id
77
+ end
78
+ end
@@ -0,0 +1,29 @@
1
+ module Minimapper
2
+ class Repository
3
+ def self.build(mappers)
4
+ new(mappers)
5
+ end
6
+
7
+ def initialize(mappers)
8
+ @mappers = mappers
9
+ define_mapper_methods
10
+ end
11
+
12
+ def delete_all!
13
+ mappers.each { |name, instance| instance.delete_all }
14
+ end
15
+
16
+ private
17
+
18
+ def define_mapper_methods
19
+ mappers.each do |name, instance|
20
+ singleton = (class << self; self end)
21
+ singleton.send(:define_method, name) do # def mapper_name
22
+ instance # instance
23
+ end # end
24
+ end
25
+ end
26
+
27
+ attr_reader :mappers
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Minimapper
2
+ VERSION = "0.0.1"
3
+ end
data/lib/minimapper.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "minimapper/version"
2
+ #require "minimapper/repository"
3
+ #require "minimapper/memory"
4
+ #require "minimapper/ar"
5
+
6
+ module Minimapper
7
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'minimapper/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "minimapper"
8
+ gem.version = Minimapper::VERSION
9
+ gem.authors = ["Joakim Kolsjö"]
10
+ gem.email = ["joakim.kolsjo@gmail.com"]
11
+ gem.description = %q{A minimalistic way of separating your models from ORMs like ActiveRecord (by implementing the repository pattern)}
12
+ gem.summary = %q{A minimalistic way of separating your models from ORMs like ActiveRecord (by implementing the repository pattern)}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "informal"
21
+ gem.add_dependency "rake"
22
+ end
data/script/ci ADDED
@@ -0,0 +1,16 @@
1
+ #!/bin/bash
2
+ set -e
3
+ export CI=true
4
+ bundle exec rake spec:unit
5
+
6
+ echo
7
+ echo "Using sqlite3:"
8
+ bundle exec rake spec:integrated
9
+
10
+ echo
11
+ echo "Using postgres:"
12
+ bundle exec rake spec:integrated DB=postgres
13
+
14
+ echo
15
+ echo "Using mysql:"
16
+ bundle exec rake spec:integrated DB=mysql
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Command to run the specs the correct way when triggered from the turbux vim plugin.
4
+
5
+ rails_spec = ARGV.first.start_with?("spec/") || ARGV.first.include?('/spec/')
6
+
7
+ if rails_spec
8
+ command = "rspec"
9
+ else
10
+ spec_helper_path = File.expand_path("unit/spec_helper.rb")
11
+
12
+ # Don't load bundler for non-rails unit tests
13
+ command = "RUBYOPT='' rspec -r#{spec_helper_path} --color --tty"
14
+ end
15
+
16
+ command += " #{ARGV.first}"
17
+ exit(1) unless system(command)
data/spec/ar_spec.rb ADDED
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+ require "minimapper/entity"
3
+ require "minimapper/ar"
4
+
5
+ class TestEntity < Minimapper::Entity
6
+ attributes :name, :github_url
7
+ validates :name, :presence => true
8
+ end
9
+
10
+ class TestMapper < Minimapper::AR
11
+ private
12
+
13
+ def entity_klass
14
+ TestEntity
15
+ end
16
+
17
+ def record_klass
18
+ Record
19
+ end
20
+
21
+ class Record < ActiveRecord::Base
22
+ attr_accessible :name
23
+ self.table_name = :projects
24
+ end
25
+ end
26
+
27
+ describe Minimapper::AR do
28
+ let(:repository) { TestMapper.new }
29
+ let(:entity_klass) { TestEntity }
30
+
31
+ include_examples :mapper
32
+ end
@@ -0,0 +1,13 @@
1
+ require "active_record"
2
+ require "minimapper"
3
+
4
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..")) unless defined?(ROOT)
5
+ Dir[File.join(ROOT, "spec/support/shared_examples/*.rb")].each { |f| require f }
6
+
7
+ require File.join(ROOT, "spec/support/database_setup")
8
+
9
+ RSpec.configure do |config|
10
+ config.before(:each) do
11
+ ActiveRecord::Base.connection.execute "DELETE FROM projects;"
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ class DB
2
+ POSTGRES_USERNAME = ENV['CI'] ? 'postgres' : ENV['USER']
3
+
4
+ def use_sqlite3
5
+ if jruby?
6
+ connect :adapter => "jdbcsqlite3", :database => ":memory:"
7
+ else
8
+ connect :adapter => "sqlite3", :database => ":memory:"
9
+ end
10
+ end
11
+
12
+ def use_postgres
13
+ system "psql -c 'create database minimapper_test;' -d postgres -U #{POSTGRES_USERNAME} 2> /dev/null"
14
+ connect :adapter => "postgresql", :database => "minimapper_test",
15
+ :username => POSTGRES_USERNAME
16
+ end
17
+
18
+ def use_mysql
19
+ system "mysql -e 'create database minimapper_test;' 2> /dev/null"
20
+ adapter = jruby? ? "jdbcmysql" : "mysql2"
21
+ connect :adapter => adapter, :database => "minimapper_test",
22
+ :username => "root"
23
+ end
24
+
25
+ private
26
+
27
+ def jruby?
28
+ RUBY_PLATFORM == "java"
29
+ end
30
+
31
+ def connect(opts)
32
+ ActiveRecord::Base.establish_connection(opts)
33
+ end
34
+ end
35
+
36
+ case ENV['DB']
37
+ when 'postgres'
38
+ DB.new.use_postgres
39
+ when 'mysql'
40
+ DB.new.use_mysql
41
+ else
42
+ DB.new.use_sqlite3
43
+ end
44
+
45
+ silence_stream(STDOUT) do
46
+ ActiveRecord::Schema.define(:version => 0) do
47
+ create_table :projects, :force => true do |t|
48
+ t.string :name
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,223 @@
1
+ shared_examples :mapper do
2
+ # expects repository and entity_klass to be defined
3
+
4
+ describe "create" do
5
+ it "sets an id on the entity" do
6
+ entity1 = build_valid_entity
7
+ entity1.id.should be_nil
8
+ repository.create(entity1)
9
+ entity1.id.should > 0
10
+
11
+ entity2 = build_valid_entity
12
+ repository.create(entity2)
13
+ entity2.id.should == entity1.id + 1
14
+ end
15
+
16
+ it "returns the id" do
17
+ id = repository.create(build_valid_entity)
18
+ id.should be_kind_of(Fixnum)
19
+ id.should > 0
20
+ end
21
+
22
+ it "does not store by reference" do
23
+ entity = build_valid_entity
24
+ repository.create(entity)
25
+ repository.last.object_id.should_not == entity.object_id
26
+ repository.last.name.should == "test"
27
+ end
28
+
29
+ it "validates the record before saving" do
30
+ entity = entity_klass.new
31
+ repository.create(entity).should be_false
32
+ end
33
+ end
34
+
35
+ describe "find" do
36
+ it "returns an entity matching the id" do
37
+ entity = build_valid_entity
38
+ repository.create(entity)
39
+ found_entity = repository.find(entity.id)
40
+ found_entity.name.should == "test"
41
+ found_entity.id.should == entity.id
42
+ found_entity.should be_kind_of(Minimapper::Entity)
43
+ end
44
+
45
+ it "supports string ids" do
46
+ entity = build_valid_entity
47
+ repository.create(entity)
48
+ repository.find(entity.id.to_s)
49
+ end
50
+
51
+ it "does not return the same instance" do
52
+ entity = build_valid_entity
53
+ repository.create(entity)
54
+ repository.find(entity.id).object_id.should_not == entity.object_id
55
+ repository.find(entity.id).object_id.should_not == repository.find(entity.id).object_id
56
+ end
57
+
58
+ it "fails when the an entity can not be found" do
59
+ lambda { repository.find(-1) }.should raise_error(Minimapper::Common::CanNotFindEntity)
60
+ end
61
+ end
62
+
63
+ describe "all" do
64
+ it "returns all entities in undefined order" do
65
+ first_created_entity = build_valid_entity
66
+ second_created_entity = build_valid_entity
67
+ repository.create(first_created_entity)
68
+ repository.create(second_created_entity)
69
+ all_entities = repository.all
70
+ all_entities.map(&:id).should include(first_created_entity.id)
71
+ all_entities.map(&:id).should include(second_created_entity.id)
72
+ all_entities.first.should be_kind_of(Minimapper::Entity)
73
+ end
74
+
75
+ it "does not return the same instances" do
76
+ entity = build_valid_entity
77
+ repository.create(entity)
78
+ repository.all.first.object_id.should_not == entity.object_id
79
+ repository.all.first.object_id.should_not == repository.all.first.object_id
80
+ end
81
+ end
82
+
83
+ describe "first" do
84
+ it "returns the first entity" do
85
+ first_created_entity = build_valid_entity
86
+ repository.create(first_created_entity)
87
+ repository.create(build_valid_entity)
88
+ repository.first.id.should == first_created_entity.id
89
+ repository.first.should be_kind_of(entity_klass)
90
+ end
91
+
92
+ it "does not return the same instance" do
93
+ entity = build_valid_entity
94
+ repository.create(entity)
95
+ repository.first.object_id.should_not == entity.object_id
96
+ repository.first.object_id.should_not == repository.first.object_id
97
+ end
98
+
99
+ it "returns nil when there is no entity" do
100
+ repository.first.should be_nil
101
+ end
102
+ end
103
+
104
+ describe "last" do
105
+ it "returns the last entity" do
106
+ last_created_entity = build_valid_entity
107
+ repository.create(build_valid_entity)
108
+ repository.create(last_created_entity)
109
+ repository.last.id.should == last_created_entity.id
110
+ repository.last.should be_kind_of(entity_klass)
111
+ end
112
+
113
+ it "does not return the same instance" do
114
+ entity = build_valid_entity
115
+ repository.create(entity)
116
+ repository.last.object_id.should_not == entity.object_id
117
+ repository.last.object_id.should_not == repository.last.object_id
118
+ end
119
+
120
+ it "returns nil when there is no entity" do
121
+ repository.last.should be_nil
122
+ end
123
+ end
124
+
125
+ describe "count" do
126
+ it "returns the number of entities" do
127
+ repository.create(build_valid_entity)
128
+ repository.create(build_valid_entity)
129
+ repository.count.should == 2
130
+ end
131
+ end
132
+
133
+ describe "update" do
134
+ it "updates" do
135
+ entity = build_valid_entity
136
+ repository.create(entity)
137
+
138
+ entity.name = "Updated"
139
+ repository.last.name.should == "test"
140
+
141
+ repository.update(entity)
142
+ repository.last.id.should == entity.id
143
+ repository.last.name.should == "Updated"
144
+ end
145
+
146
+ it "does not update and returns false when the entity isn't valid" do
147
+ entity = build_valid_entity
148
+ repository.create(entity)
149
+ entity.name = nil
150
+
151
+ repository.update(entity).should be_false
152
+ repository.last.name.should == "test"
153
+ end
154
+
155
+ it "returns true" do
156
+ entity = build_valid_entity
157
+ repository.create(entity)
158
+ repository.update(entity).should == true
159
+ end
160
+
161
+ it "fails when the entity does not have an id" do
162
+ entity = build_valid_entity
163
+ lambda { repository.update(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
164
+ end
165
+
166
+ it "fails when the entity no longer exists" do
167
+ entity = build_valid_entity
168
+ repository.create(entity)
169
+ repository.delete_all
170
+ lambda { repository.update(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
171
+ end
172
+ end
173
+
174
+ describe "delete" do
175
+ it "removes the entity" do
176
+ entity = build_valid_entity
177
+ repository.create(entity)
178
+ repository.create(build_valid_entity)
179
+ repository.delete(entity)
180
+ repository.all.size.should == 1
181
+ repository.first.id.should_not == entity.id
182
+ end
183
+
184
+ it "fails when the entity does not have an id" do
185
+ entity = entity_klass.new
186
+ lambda { repository.delete(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
187
+ end
188
+
189
+ it "fails when the entity can not be found" do
190
+ entity = entity_klass.new(:id => -1)
191
+ lambda { repository.delete(entity) }.should raise_error(Minimapper::Common::CanNotFindEntity)
192
+ end
193
+ end
194
+
195
+ describe "delete_by_id" do
196
+ it "removes the entity" do
197
+ entity = build_valid_entity
198
+ repository.create(entity)
199
+ repository.create(build_valid_entity)
200
+ repository.delete_by_id(entity.id)
201
+ repository.all.size.should == 1
202
+ repository.first.id.should_not == entity.id
203
+ end
204
+
205
+ it "fails when the an entity can not be found" do
206
+ lambda { repository.delete_by_id(-1) }.should raise_error(Minimapper::Common::CanNotFindEntity)
207
+ end
208
+ end
209
+
210
+ describe "delete_all" do
211
+ it "empties the repository" do
212
+ repository.create(build_valid_entity)
213
+ repository.delete_all
214
+ repository.all.should == []
215
+ end
216
+ end
217
+
218
+ private
219
+
220
+ def build_valid_entity
221
+ entity_klass.new(:name => 'test')
222
+ end
223
+ end
@@ -0,0 +1,41 @@
1
+ require 'minimapper/entity'
2
+
3
+ describe Minimapper::Entity do
4
+ it "handles base attributes" do
5
+ base = described_class.new
6
+ base.id = 5
7
+ base.id.should == 5
8
+
9
+ time = Time.now
10
+ base.created_at = time
11
+ base.created_at.should == time
12
+
13
+ base.updated_at = time
14
+ base.updated_at.should == time
15
+ end
16
+ end
17
+
18
+ describe Minimapper::Entity, "attributes" do
19
+ it "returns the attributes" do
20
+ base = described_class.new(:id => 5)
21
+ time = Time.now
22
+ base.created_at = time
23
+ base.attributes.should == { :id => 5, :created_at => time }
24
+ end
25
+ end
26
+
27
+ describe Minimapper::Entity, "to_param" do
28
+ it "responds with the id to be compatible with rails link helpers" do
29
+ base = described_class.new(:id => 5)
30
+ base.to_param.should == 5
31
+ end
32
+ end
33
+
34
+ describe Minimapper::Entity, "persisted?" do
35
+ it "responds true when there is an id (to be compatible with rails form helpers)" do
36
+ base = described_class.new
37
+ base.should_not be_persisted
38
+ base.id = 5
39
+ base.should be_persisted
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ require 'minimapper/memory'
2
+ require 'minimapper/common'
3
+ require 'minimapper/entity'
4
+
5
+ class TestEntity < Minimapper::Entity
6
+ attributes :name
7
+ validates :name, :presence => true
8
+ end
9
+
10
+ describe Minimapper::Memory do
11
+ let(:repository) { described_class.new }
12
+ let(:entity_klass) { TestEntity }
13
+
14
+ include_examples :mapper
15
+ end
@@ -0,0 +1,40 @@
1
+ require 'minimapper/repository'
2
+ require 'minimapper/memory'
3
+
4
+ module Test
5
+ class ProjectMapper < Minimapper::Memory
6
+ end
7
+ end
8
+
9
+ describe Minimapper::Repository, "self.build" do
10
+ it "builds a repository" do
11
+ repository = described_class.build(:projects => Test::ProjectMapper.new)
12
+ repository.should be_instance_of(Minimapper::Repository)
13
+ repository.projects.should be_instance_of(Test::ProjectMapper)
14
+ end
15
+
16
+ it "memoizes the mappers" do
17
+ repository = described_class.build(:projects => Test::ProjectMapper.new)
18
+ repository.projects.object_id.should == repository.projects.object_id
19
+ end
20
+
21
+ it "does not leak between instances" do
22
+ repository1 = described_class.build(:projects => :foo)
23
+ repository2 = described_class.build(:projects => :bar)
24
+ repository1.projects.should == :foo
25
+ repository2.projects.should == :bar
26
+ end
27
+ end
28
+
29
+ describe Minimapper::Repository, "#delete_all!" do
30
+ it "removes all records by calling delete_all on all mappers" do
31
+ project_mapper = mock
32
+ user_mapper = mock
33
+
34
+ project_mapper.should_receive(:delete_all)
35
+ user_mapper.should_receive(:delete_all)
36
+
37
+ repository2 = described_class.build(:projects => project_mapper, :users => user_mapper)
38
+ repository2.delete_all!
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
+ $: << File.join(ROOT, "lib")
3
+ $: << File.join(ROOT, "unit")
4
+
5
+ require "minimapper"
6
+
7
+ Dir[File.join(ROOT, "spec/support/shared_examples/*.rb")].each { |f| require f }
8
+
9
+ RSpec.configure do |config|
10
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minimapper
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - "Joakim Kolsj\xC3\xB6"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-10-14 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: informal
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: A minimalistic way of separating your models from ORMs like ActiveRecord (by implementing the repository pattern)
50
+ email:
51
+ - joakim.kolsjo@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - .gitignore
60
+ - .rspec
61
+ - .rvmrc
62
+ - .travis.yml
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/minimapper.rb
68
+ - lib/minimapper/ar.rb
69
+ - lib/minimapper/common.rb
70
+ - lib/minimapper/entity.rb
71
+ - lib/minimapper/memory.rb
72
+ - lib/minimapper/repository.rb
73
+ - lib/minimapper/version.rb
74
+ - minimapper.gemspec
75
+ - script/ci
76
+ - script/turbux_rspec
77
+ - spec/ar_spec.rb
78
+ - spec/spec_helper.rb
79
+ - spec/support/database_setup.rb
80
+ - spec/support/shared_examples/mapper.rb
81
+ - unit/entity_spec.rb
82
+ - unit/memory_spec.rb
83
+ - unit/repository_spec.rb
84
+ - unit/spec_helper.rb
85
+ has_rdoc: true
86
+ homepage: ""
87
+ licenses: []
88
+
89
+ post_install_message:
90
+ rdoc_options: []
91
+
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ requirements: []
113
+
114
+ rubyforge_project:
115
+ rubygems_version: 1.5.3
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: A minimalistic way of separating your models from ORMs like ActiveRecord (by implementing the repository pattern)
119
+ test_files:
120
+ - spec/ar_spec.rb
121
+ - spec/spec_helper.rb
122
+ - spec/support/database_setup.rb
123
+ - spec/support/shared_examples/mapper.rb