minimapper 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,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