little_mapper 0.0.3

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
+ .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