little_mapper 0.0.3

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
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in little_mapper.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Simon Robson
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,88 @@
1
+ # LittleMapper
2
+
3
+ Early stage simple datamapper/repository backed by ActiveRecord (only, for the moment). Still working out the best DSL. Currently in use on an internal project.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'little_mapper', :github => 'simonrobson/little-mapper'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install little_mapper
18
+
19
+ ## Usage
20
+
21
+ Define one or more plain ruby classes:
22
+
23
+ class Person < OpenStruct
24
+ attr_accessor :phone_numbers
25
+ def initialize(*args)
26
+ super
27
+ @phone_numbers = []
28
+ end
29
+ end
30
+
31
+ class PhoneNumber
32
+ attr_accessor :id, :code, :number
33
+ def initialize(opts = {})
34
+ opts.each_pair {|k, v| self.send("#{k}=", v)}
35
+ end
36
+ end
37
+
38
+
39
+ Define the mappers:
40
+
41
+ class PersonMapper
42
+ include LittleMapper
43
+ entity Person
44
+ persistent_entity Persistent::Person
45
+ maps :name, :age
46
+ map :phone_numbers, :as => [PhoneNumber]
47
+ # see integration test for more complex example
48
+ # map :all_cats, :as => [Cat], :entity_collection_adder => :receive_cat,
49
+ # :to => :cats
50
+ map :spouse, :as => Person
51
+ end
52
+
53
+ class PhoneNumberMapper
54
+ include LittleMapper
55
+ entity PhoneNumber
56
+ persistent_entity Persistent::PhoneNumber
57
+ maps :code, :number
58
+ end
59
+
60
+ Define the ActiveRecord classes (and their migrations, not shown):
61
+
62
+ module Persistent
63
+ class Person < ActiveRecord::Base
64
+ has_many :phone_numbers
65
+ belongs_to :spouse, :class_name => 'Person'
66
+ end
67
+
68
+ class PhoneNumber < ActiveRecord::Base
69
+ belongs_to :person
70
+ end
71
+ end
72
+
73
+ Exercise the code:
74
+
75
+ jane = Person.new(:name => 'Jane', :age => 28)
76
+ LittleMapper[Person] << jane # stores jane
77
+ john = Person.new(:name => 'John', :age => 27, :spouse => o)
78
+ LittleMapper[Person] << john
79
+ found = LittleMapper[Person].find_by_id(john.id)
80
+ puts found.spouse.name # Jane - found and spouse are plain Ruby objects
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "minitest/unit"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'lib/little_helper'
7
+ t.libs << 'test'
8
+ t.test_files = FileList['test/*_test.rb', 'test/integration/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,46 @@
1
+ require "little_mapper/version"
2
+ require "little_mapper/dsl_class_methods"
3
+ require "little_mapper/mapper_instance_methods"
4
+ require "little_mapper/mapping_factory"
5
+ require "little_mapper/mappers/mappers"
6
+ require "little_mapper/mappers/active_record"
7
+ require "little_mapper/result/repo_response"
8
+ require "little_mapper/result/repo_success"
9
+ require "little_mapper/result/repo_failure"
10
+
11
+ module LittleMapper
12
+
13
+ # Registers a mapper under a key.
14
+ #
15
+ # Will generally be called by Little Mapper itself during inclusion of the
16
+ # LittleMapper module in your class.
17
+ #
18
+ # entity - The key under which the mapper will be held. Generally the
19
+ # the non-persistent entity class name.
20
+ # mapper - The class of the mapper.
21
+ #
22
+ # Returns the mapper
23
+ def self.register(entity, mapper)
24
+ @mappers ||= {}
25
+ @mappers[entity] = mapper
26
+ end
27
+
28
+ # Returns an instantiated mapper for the given key.
29
+ #
30
+ # The primary way of interacting with LittleMapper having set up one or more
31
+ # mappers.
32
+ #
33
+ # key - The key under which the mapper was stored. Generally the non-
34
+ # persistent entity class name.
35
+ #
36
+ # Returns a mapper instance for the key
37
+ def self.[](key)
38
+ @mappers[key].new
39
+ end
40
+
41
+ # Private: kicks off the action when this module is included
42
+ def self.included(klass)
43
+ klass.extend DslClassMethods
44
+ klass.module_eval { include MapperInstanceMethods}
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ module LittleMapper
2
+ module DslClassMethods
3
+
4
+ # Getter/Setter for the Domain Entity to persist.
5
+ def entity(klass = nil)
6
+ if klass.nil?
7
+ @_entity
8
+ else
9
+ @_entity = klass
10
+ LittleMapper.register(klass, self)
11
+ end
12
+ end
13
+
14
+ # Getter/Setter for the (Currently ActiveRecord only) class to use for
15
+ # persistence.
16
+ def persistent_entity(klass = nil)
17
+ klass.nil? ? @_persistent_entity : @_persistent_entity = klass
18
+ end
19
+
20
+ def mappings
21
+ @_mappings ||= []
22
+ end
23
+
24
+ def add_mapping(m)
25
+ mappings << m
26
+ end
27
+
28
+ def mapping_factory=(mf)
29
+ @_mapping_factory = mf
30
+ end
31
+
32
+ def mapping_factory
33
+ @_mapping_factory ||= LittleMapper::MappingFactory.new
34
+ end
35
+
36
+ # Add one or more simple one-to-one mappings.
37
+ #
38
+ # args - one or more Symbols. The names of the fields to map.
39
+ #
40
+ # Examples
41
+ #
42
+ # maps :first_name, :last_name
43
+ def maps(*args)
44
+ args.each {|f| map(f)}
45
+ end
46
+
47
+
48
+ # Add a single mapping.
49
+ #
50
+ # field - A Symbol, the name of the field to map.
51
+ # opts - A Hash of options to configure the mapper (default: {}):
52
+ # :as - The Class of Mapper to use to map an entity
53
+ # value. Can be enclosed in Array notation to
54
+ # signify a one to many relationship
55
+ # :to - Name of the field on the persistent entity
56
+ # to which we will map (default: field)
57
+ # :entity_setter - a Symbol representing the name of a function
58
+ # to use when setting the value on the entity
59
+ # (default: field=)
60
+ # :entity_collection_adder - in a one-to-many relationship, the name of
61
+ # of a method to use when adding associated objetcs
62
+ # (default: to#<<)
63
+ # Examples
64
+ #
65
+ # map :metrics, :as => [Metric]
66
+ # map :owner, :entity_setter => :assign_owner
67
+ def map(field, opts = {})
68
+ add_mapping(mapping_factory.map(self, field, opts))
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,52 @@
1
+ module LittleMapper
2
+ module MapperInstanceMethods
3
+ def to_persistent(entity)
4
+ pe = persisted?(entity) ? find_persistent_by_id(entity.id) : self.class.persistent_entity.new
5
+ self.class.mappings.each do |m|
6
+ m.from(entity).to_persistent(pe)
7
+ end
8
+ pe
9
+ end
10
+
11
+ def to_entity(persistent)
12
+ e = self.class.entity.new
13
+ e.id = persistent.id # probably set this only if configured & see duplication below
14
+ self.class.mappings.each do |m|
15
+ m.from(persistent).to_entity(e)
16
+ end
17
+ e
18
+ end
19
+
20
+ def persisted?(entity)
21
+ !entity.id.nil? # should maybe check for empty strings too?
22
+ end
23
+
24
+ def <<(entity)
25
+ pe = to_persistent(entity)
26
+ if pe.save
27
+ entity.id = pe.id unless entity.id # set this only if configured
28
+ LittleMapper::Result::RepoSuccess.new(pe)
29
+ else
30
+ LittleMapper::Result::RepoFailure.new(pe)
31
+ end
32
+ end
33
+
34
+ def find_persistent_by_id(id)
35
+ self.class.persistent_entity.find_by_id(id)
36
+ end
37
+
38
+ def find_by_id(id)
39
+ pe = find_persistent_by_id(id)
40
+ return nil unless pe
41
+ to_entity(pe)
42
+ end
43
+
44
+ def find_all
45
+ self.class.persistent_entity.all.collect { |e| to_entity(e) }
46
+ end
47
+
48
+ def last
49
+ to_entity(self.class.persistent_entity.last)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ module LittleMapper
2
+ module Mappers
3
+ module AR
4
+ class OneToMany < Base
5
+ attr_accessor :mapper, :entity_field, :persistent_field, :persistent_klass
6
+ attr_accessor :entity_collection_adder, :reflexive, :reflexive_setter
7
+ def initialize(mapper, entity_field, persistent_klass, opts = {})
8
+ @mapper = mapper
9
+ @entity_field = entity_field
10
+ @persistent_klass = persistent_klass
11
+ @persistent_field = opts[:to] || entity_field
12
+ @entity_collection_adder = opts[:entity_collection_adder]
13
+ @reflexive = opts.fetch(:reflexive, true)
14
+ @reflexive_setter = opts[:reflexive_setter] || "#{camel_to_snake(mapper.entity.to_s)}="
15
+ end
16
+
17
+ def to_persistent(target)
18
+ source.__send__(entity_field).each do |associated|
19
+ target.__send__(persistent_field).__send__(:<<, LittleMapper[persistent_klass].to_persistent(associated))
20
+ end
21
+ end
22
+ def to_entity(target)
23
+ source.__send__(persistent_field).each do |associated|
24
+ assoc_entity = LittleMapper[persistent_klass].to_entity(associated)
25
+ if reflexive
26
+ assoc_entity.__send__(reflexive_setter, target)
27
+ end
28
+ target.__send__(entity_field).__send__(:<<, assoc_entity)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ module LittleMapper
2
+ module Mappers
3
+ module AR
4
+ class OneToOne < Mappers::Base
5
+ attr_accessor :mapper, :field, :persistent_klass, :persistent_field, :entity_setter, :persistent_entity_setter
6
+ def initialize(mapper, field, opts = {})
7
+ @mapper = mapper
8
+ @field = field
9
+ @persistent_klass = opts[:as]
10
+ @persistent_field = opts[:to] || field
11
+ @entity_setter = opts[:entity_setter] || "#{field}=".to_sym
12
+ @persistent_entity_setter = opts[:persistent_entity_setter] || "#{persistent_field}=".to_sym
13
+ end
14
+
15
+ def to_persistent(target)
16
+ if persistent_klass
17
+ val = source.__send__(field)
18
+ if val
19
+ target.__send__(persistent_entity_setter, LittleMapper[persistent_klass].to_persistent(val))
20
+ end
21
+ else
22
+ target.__send__(persistent_entity_setter, source.__send__(field))
23
+ end
24
+ end
25
+
26
+ def to_entity(target)
27
+ if persistent_klass
28
+ val = source.__send__(persistent_field)
29
+ if val
30
+ target.__send__(entity_setter, LittleMapper[persistent_klass].to_entity(val))
31
+ end
32
+ else
33
+ target.__send__(entity_setter, source.__send__(persistent_field))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,2 @@
1
+ require 'little_mapper/mappers/AR/one_to_one'
2
+ require 'little_mapper/mappers/AR/one_to_many'
@@ -0,0 +1,15 @@
1
+ module LittleMapper
2
+ module Mappers
3
+ class Base
4
+ attr_accessor :source
5
+ def from(source)
6
+ @source = source
7
+ self
8
+ end
9
+
10
+ def camel_to_snake(cc)
11
+ cc.gsub(/(.)([A-Z])/,'\1_\2').downcase
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module LittleMapper
2
+ class MappingFactory
3
+
4
+ def initialize(strategy = :activerecord)
5
+ @_strategy = strategy
6
+ end
7
+
8
+ def strategy
9
+ @_strategy
10
+ end
11
+
12
+ def map(mapper, field, opts = {})
13
+ if opts[:as] && opts[:as].is_a?(Array)
14
+ as = opts.delete(:as)
15
+ one_to_many.new(mapper, field, as[0], opts)
16
+ else
17
+ one_to_one.new(mapper, field, opts)
18
+ end
19
+ end
20
+
21
+ def strategy_module
22
+ case strategy
23
+ when :activerecord
24
+ ::LittleMapper::Mappers::AR
25
+ end
26
+ end
27
+
28
+ def one_to_one
29
+ strategy_module.const_get('OneToOne')
30
+ end
31
+
32
+ def one_to_many
33
+ strategy_module.const_get('OneToMany')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ module LittleMapper
2
+ module Result
3
+ class RepoFailure < RepoResult
4
+ def after_initialize
5
+ if object && object.respond_to?(:errors)
6
+ @messages = object.errors.full_messages
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module LittleMapper
2
+ module Result
3
+ class RepoResult
4
+ attr_reader :messages, :object
5
+ def initialize(object = nil)
6
+ @messages = []
7
+ @object = object
8
+ after_initialize
9
+ end
10
+ def success?; false; end
11
+ def after_initialize; end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module LittleMapper
2
+ module Result
3
+ class RepoSuccess < RepoResult
4
+ def success?; true; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module LittleMapper
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'little_mapper/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "little_mapper"
8
+ gem.version = LittleMapper::VERSION
9
+ gem.authors = ["Simon Robson"]
10
+ gem.email = ["shrobson@gmail.com"]
11
+ gem.description = %q{Simple, ActiveRecord-backed data mapper / repository}
12
+ gem.summary = %q{Simple, ActiveRecord-backed data mapper / repository}
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
+ gem.add_development_dependency('sqlite3')
20
+ gem.add_development_dependency('activerecord')
21
+ end
@@ -0,0 +1,62 @@
1
+ require 'helper'
2
+
3
+ module LittleMapper
4
+ class DslClassMethodsTest < MiniTest::Unit::TestCase
5
+
6
+ class TestClass
7
+ extend DslClassMethods
8
+ end
9
+
10
+ class TheEntity; end
11
+
12
+ def test_enables_setting_and_getting_entity
13
+ TestClass.entity TheEntity
14
+ assert_equal TheEntity, TestClass.entity
15
+ end
16
+
17
+ def test_setting_the_entity_registers_the_mapper
18
+ TestClass.entity TheEntity
19
+ assert LittleMapper[TheEntity].is_a? TestClass
20
+ end
21
+
22
+ def test_enables_setting_and_getting_persistent_entity
23
+ TestClass.persistent_entity TheEntity
24
+ assert_equal TheEntity, TestClass.persistent_entity
25
+ end
26
+
27
+ def test_provides_mappings_array
28
+ assert TestClass.mappings.respond_to?(:each)
29
+ end
30
+
31
+ def test_enables_setting_and_getting_a_mapping_factory_instance
32
+ factory = Object.new
33
+ TestClass.mapping_factory = factory
34
+ assert_equal factory, TestClass.mapping_factory
35
+ end
36
+
37
+ def test_defaults_mapping_factory
38
+ TestClass.mapping_factory = nil
39
+ assert TestClass.mapping_factory.is_a? LittleMapper::MappingFactory
40
+ end
41
+
42
+ def test_maps_delegates_to_map_and_so_to_mapping_factory
43
+ factory = MiniTest::Mock.new
44
+ TestClass.mapping_factory = factory
45
+ args = [:a, :b, :c]
46
+ factory.expect(:map, true, [TestClass, :a, {}])
47
+ factory.expect(:map, true, [TestClass, :b, {}])
48
+ factory.expect(:map, true, [TestClass, :c, {}])
49
+ TestClass.maps(*args)
50
+ factory.verify
51
+ end
52
+
53
+ def test_delegates_map_to_mapping_factory
54
+ factory = MiniTest::Mock.new
55
+ TestClass.mapping_factory = factory
56
+ args = [:field, {:opt1 => :val, :opt2 => :val}]
57
+ factory.expect(:map, true, args.dup.unshift(TestClass))
58
+ TestClass.map(*args)
59
+ factory.verify
60
+ end
61
+ end
62
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,6 @@
1
+ unless defined? LittleMapper
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'little_mapper'
4
+ end
5
+
6
+ require 'minitest/autorun'
@@ -0,0 +1,94 @@
1
+ require 'helper'
2
+ require 'active_record'
3
+ require 'integration/helper'
4
+
5
+
6
+
7
+ class ArSqliteTest < MiniTest::Unit::TestCase
8
+ DBNAME = 'lmapper.sqlite'
9
+
10
+ def init_db
11
+ if File.directory?('tmp')
12
+ File.unlink('tmp/#{DBNAME}') if File.exists?('tmp/#{DBNAME}')
13
+ else
14
+ Dir.mkdir('tmp')
15
+ end
16
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'tmp/test.sqlite')
17
+ create_tables
18
+ @_db_initialized = true
19
+ end
20
+
21
+ def create_tables
22
+ CreatePeople.new.up
23
+ CreatePhoneNumbers.new.up
24
+ CreateCats.new.up
25
+ end
26
+
27
+ def clear_tables
28
+ Persistent::Person.delete_all
29
+ end
30
+
31
+ def setup
32
+ init_db unless @_db_initialized
33
+ clear_tables
34
+ end
35
+
36
+
37
+ def test_round_trip
38
+ p = Person.new(:name => 'John', :age => 27)
39
+ LittleMapper[Person] << p
40
+ assert_equal 1, LittleMapper[Person].find_all.length
41
+ found = LittleMapper[Person].last
42
+ assert_equal p.name, found.name
43
+ assert_equal p.age, found.age
44
+ refute_nil found.id
45
+ assert_equal p.id, found.id
46
+ end
47
+
48
+ def test_find_all
49
+ LittleMapper[Person] << Person.new(:name => 'John', :age => 27)
50
+ LittleMapper[Person] << Person.new(:name => 'Jane', :age => 67)
51
+ assert_equal 2, LittleMapper[Person].find_all.length
52
+ end
53
+
54
+ def test_simple_one_to_many_association
55
+ p = Person.new(:name => 'John', :age => 27)
56
+ p.phone_numbers << PhoneNumber.new(:code => '099', :number => '987654321')
57
+ p.phone_numbers << PhoneNumber.new(:code => '123', :number => '123456789')
58
+ LittleMapper[Person] << p
59
+ found = LittleMapper[Person].find_by_id(p.id)
60
+ assert_equal 2, found.phone_numbers.length
61
+ end
62
+
63
+ def test_one_to_many_with_custom_setters_getters
64
+ p = Person.new(:name => 'John', :age => 27)
65
+ p.receive_cat(Cat.new(:moniker => 'Meaw', :color => 'Tabby'))
66
+ LittleMapper[Person] << p
67
+ found = LittleMapper[Person].find_by_id(p.id)
68
+ assert_equal 1, found.all_cats.length
69
+ end
70
+
71
+ def test_one_to_one_with_another_entity
72
+ o = Person.new(:name => 'Jane', :age => 28)
73
+ LittleMapper[Person] << o
74
+ p = Person.new(:name => 'John', :age => 27, :spouse => o)
75
+ LittleMapper[Person] << p
76
+ found = LittleMapper[Person].find_by_id(p.id)
77
+ assert_equal found.spouse, o
78
+ end
79
+
80
+ def test_sets_up_reflexive_mapping_by_default_on_related_entities
81
+ p = Person.new(:name => 'John', :age => 27)
82
+ number1 = PhoneNumber.new(:code => '099', :number => '987654321')
83
+ p.phone_numbers << number1
84
+ p.phone_numbers << PhoneNumber.new(:code => '123', :number => '123456789')
85
+ LittleMapper[Person] << p
86
+ found = LittleMapper[Person].find_by_id(p.id)
87
+ assert_equal found, found.phone_numbers.first.person
88
+ end
89
+
90
+
91
+
92
+
93
+
94
+ end
@@ -0,0 +1,114 @@
1
+ class Person < OpenStruct
2
+
3
+ attr_accessor :phone_numbers
4
+ def initialize(*args)
5
+ super
6
+ @cats = []
7
+ @phone_numbers = []
8
+ end
9
+
10
+ def receive_cat(cat)
11
+ @cats << cat
12
+ end
13
+
14
+ def all_cats
15
+ @cats
16
+ end
17
+ end
18
+
19
+ class PhoneNumber
20
+ attr_accessor :id, :code, :number, :person
21
+ def initialize(opts = {})
22
+ opts.each_pair {|k, v| self.send("#{k}=", v)}
23
+ end
24
+ end
25
+
26
+ class Cat
27
+ attr_accessor :moniker, :color, :id
28
+ def initialize(opts = {})
29
+ opts.each_pair {|k, v| self.send("#{k}=", v)}
30
+ end
31
+
32
+ def owner=(owner)
33
+ @owner = owner
34
+ end
35
+
36
+ def owner
37
+ @owner
38
+ end
39
+ end
40
+
41
+
42
+ module Persistent
43
+ class Person < ActiveRecord::Base
44
+ has_many :phone_numbers
45
+ has_many :cats
46
+ belongs_to :spouse, :class_name => 'Person'
47
+ end
48
+
49
+ class PhoneNumber < ActiveRecord::Base
50
+ belongs_to :person
51
+ end
52
+
53
+ class Cat < ActiveRecord::Base
54
+ belongs_to :person
55
+ end
56
+ end
57
+
58
+ class PersonMapper
59
+ include LittleMapper
60
+ entity Person
61
+ persistent_entity Persistent::Person
62
+ maps :name, :age
63
+ map :phone_numbers, :as => [PhoneNumber]
64
+ map :all_cats, :as => [Cat], :entity_collection_adder => :receive_cat,
65
+ :to => :cats, :reflexive_setter => :owner=
66
+ map :spouse, :as => Person
67
+ end
68
+
69
+ class PhoneNumberMapper
70
+ include LittleMapper
71
+ entity PhoneNumber
72
+ persistent_entity Persistent::PhoneNumber
73
+ maps :code, :number
74
+ end
75
+
76
+ class CatMapper
77
+ include LittleMapper
78
+ entity Cat
79
+ persistent_entity Persistent::Cat
80
+ map :color
81
+ map :moniker, :to => :name
82
+ end
83
+
84
+ class CreatePeople < ActiveRecord::Migration
85
+ def up
86
+ create_table :people, :force => true do |t|
87
+ t.string :name
88
+ t.integer :age
89
+ t.integer :spouse_id
90
+ t.timestamps
91
+ end
92
+ end
93
+ end
94
+
95
+ class CreatePhoneNumbers < ActiveRecord::Migration
96
+ def up
97
+ create_table :phone_numbers, :force => true do |t|
98
+ t.references :person
99
+ t.string :code
100
+ t.string :number
101
+ end
102
+ end
103
+ end
104
+
105
+ class CreateCats < ActiveRecord::Migration
106
+ def up
107
+ create_table :cats, :force => true do |t|
108
+ t.references :person
109
+ t.string :name
110
+ t.string :color
111
+ end
112
+ end
113
+ end
114
+
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ class LittleMapperModuleTest < MiniTest::Unit::TestCase
4
+
5
+ class A; end
6
+ class B; end
7
+
8
+ def test_it_registers_mappers
9
+ LittleMapper.register(A, B)
10
+ assert LittleMapper[A].is_a? B
11
+ end
12
+
13
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ module LittleMapper
4
+ class MapperInstanceMethodsTest < MiniTest::Unit::TestCase
5
+ class InstanceTestClass
6
+ include MapperInstanceMethods
7
+ end
8
+
9
+ def test_implements_repo_interface
10
+ @repo = InstanceTestClass.new
11
+ assert @repo.respond_to?(:<<), "should respond to <<"
12
+ assert @repo.respond_to?(:find_by_id), "should respond to find_by_id"
13
+ assert @repo.respond_to?(:find_all), "should respond to find_by_all"
14
+ assert @repo.respond_to?(:last), "should respond to last"
15
+
16
+ assert @repo.respond_to?(:to_entity), "provides to_entity"
17
+ assert @repo.respond_to?(:to_persistent), "provides to_persistent"
18
+ end
19
+
20
+
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ require 'helper'
2
+
3
+ module LittleMapper
4
+ class MappingFactoryTest < MiniTest::Unit::TestCase
5
+ def test_defaults_to_activerecord_strategy
6
+ assert MappingFactory.new.strategy == :activerecord
7
+ end
8
+
9
+ def test_one_to_one_constant
10
+ assert_equal LittleMapper::Mappers::AR::OneToOne, MappingFactory.new.one_to_one
11
+ end
12
+
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: little_mapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Simon Robson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sqlite3
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activerecord
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Simple, ActiveRecord-backed data mapper / repository
47
+ email:
48
+ - shrobson@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - lib/little_mapper.rb
59
+ - lib/little_mapper/dsl_class_methods.rb
60
+ - lib/little_mapper/mapper_instance_methods.rb
61
+ - lib/little_mapper/mappers/AR/one_to_many.rb
62
+ - lib/little_mapper/mappers/AR/one_to_one.rb
63
+ - lib/little_mapper/mappers/active_record.rb
64
+ - lib/little_mapper/mappers/mappers.rb
65
+ - lib/little_mapper/mapping_factory.rb
66
+ - lib/little_mapper/result/repo_failure.rb
67
+ - lib/little_mapper/result/repo_response.rb
68
+ - lib/little_mapper/result/repo_success.rb
69
+ - lib/little_mapper/version.rb
70
+ - little_mapper.gemspec
71
+ - test/dsl_class_methods_test.rb
72
+ - test/helper.rb
73
+ - test/integration/ar_sqlite_test.rb
74
+ - test/integration/helper.rb
75
+ - test/little_mapper_module_test.rb
76
+ - test/mapper_instance_methods_test.rb
77
+ - test/mapping_factory_test.rb
78
+ homepage: ''
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.24
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Simple, ActiveRecord-backed data mapper / repository
102
+ test_files:
103
+ - test/dsl_class_methods_test.rb
104
+ - test/helper.rb
105
+ - test/integration/ar_sqlite_test.rb
106
+ - test/integration/helper.rb
107
+ - test/little_mapper_module_test.rb
108
+ - test/mapper_instance_methods_test.rb
109
+ - test/mapping_factory_test.rb