hsume2-mapped-record 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-06-07
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+
2
+ LICENSE
3
+
4
+ (The MIT License)
5
+
6
+ Copyright (c) 2009 Henry Hsu
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining
9
+ a copy of this software and associated documentation files (the
10
+ 'Software'), to deal in the Software without restriction, including
11
+ without limitation the rights to use, copy, modify, merge, publish,
12
+ distribute, sublicense, and/or sell copies of the Software, and to
13
+ permit persons to whom the Software is furnished to do so, subject to
14
+ the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be
17
+ included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
20
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,16 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/mapped-record.rb
7
+ lib/mapped-record/hash/mappable.rb
8
+ lib/mapped-record/mapping.rb
9
+ script/console
10
+ script/destroy
11
+ script/generate
12
+ test/database.yml
13
+ test/hashed-record/hash/test_mappable.rb
14
+ test/hashed-record/test_mapping.rb
15
+ test/test_helper.rb
16
+ test/test_mapped_record.rb
data/README.rdoc ADDED
@@ -0,0 +1,131 @@
1
+ = mapped-record
2
+
3
+ http://github.com/hsume2/mapped-record
4
+
5
+ Auto-magically map Hash[keys] to ActiveRecord.attributes.
6
+
7
+ Suppose you have a hash <tt>a_hash = { 'FullName' => 'Your Name' }</tt>, and an Active Record object
8
+ with a +:full_name+ attribute to create with 'Your Name'. It's easy to deal with this on a one-time basis:
9
+
10
+ model.create( :full_name => a_hash['FullName'] )
11
+
12
+ However, add in 20 other keys and it gets tiresome.
13
+
14
+ Suppose you could define hash +keys+ and their target ActiveRecord +attribute+ in one place; then, initialize
15
+ Active Record objects with the corresponding data. Continue reading.
16
+
17
+ == Getting Started
18
+
19
+ All you have to do is add +attr_mapped+ to your model.
20
+
21
+ class Person < ActiveRecord::Base
22
+ attr_mapped 'FullName' => :full_name
23
+ end
24
+
25
+ Then you can create and update like so:
26
+
27
+ p = Person.create_with(h)
28
+ h['FullName'] = 'Mr. Name'
29
+ p.update_with(h)
30
+
31
+ +mapped-record+ is more powerful than that. See Mapping Types for efficient ways to assign mappings.
32
+ See Mapping Helpers for extra-added features (e.g. post-processing data).
33
+
34
+ == Mapping Types
35
+
36
+ Mappings can be created in the following ways:
37
+
38
+ === Automatic mappings (implicit)
39
+
40
+ If you use,
41
+ attr_mapped 'FullName'
42
+ +attr_mapped+ will automatically downcase_and_underscore the key to +:full_name+, which is often useful.
43
+ Whenever used, specify these first.
44
+
45
+ === Manual mappings (explicit)
46
+
47
+ To manually set which Active Record attribute you want a key to map to, add manual mappings in the options hash.
48
+ attr_mapped 'FullName' => :full_name
49
+ attr_mapped { 'FullName' => :full_name } # same as above
50
+ attr_mapped 'FullName' => :full_name, 'Email' # will fail, because the options hash is considered the last argument
51
+
52
+ === Namespace mappings
53
+
54
+ Suppose you have a lot of keys starting with +PBType+ which you want removed. Then add <tt>:namespace => 'PBType'</tt> to remove the prefix and then map automatically (also in the options hash)
55
+
56
+ attr_mapped 'PBTypeName', 'PBTypeAddress', 'PBTypeCode', { :namespace => 'PBType' }
57
+
58
+ will map +PBTypeName+ to +:name+, +PBTypeAddress+ to +:address+, etc.
59
+
60
+ Namespaces only apply to the keys for each +attr_mapped+ call. So
61
+
62
+ class PBPerson < ActiveRecord::Base
63
+ attr_mapped 'PBTypeName', { :namespace => 'PBType' }
64
+ attr_mapped 'PBTypeAddr'
65
+ end
66
+
67
+ will map +PBTypeName+ to +:name+, but +PBTypeAddr+ to +:pb_type_addr+.
68
+
69
+ === Mapping priority
70
+
71
+ Regardless of how many times you call +attr_mapped+, mappings are overridden in increasing order of
72
+ priority:
73
+
74
+ * implicit
75
+ * namespace
76
+ * explicit
77
+
78
+ That means explicit will always override namespace and implicit, regardless of the order in which #attr_mapped is called. To illustrate this behavior:
79
+
80
+ class PBPerson < ActiveRecord::Base
81
+ attr_mapped 'PBTypeName', { :namespace => 'PBType' }
82
+ attr_mapped 'PBTypeName'
83
+ attr_mapped { 'PBTypeName' => :actually_this }
84
+
85
+ attr_mapped 'PBTypeName', { :namespace => 'PBType', 'PBTypeName' => :actually_this } # even in this confusing example
86
+ end
87
+
88
+ will map to +:actually_this+.
89
+
90
+ == Mapping Helpers
91
+
92
+ === :id
93
+
94
+ If one of the hash keys should map to the Active Record id, setting it like <tt>attr_mapped :key => :id</tt>
95
+ won't work. Active Record won't let you mass assign +:id+ anyway. Instead
96
+ attr_mapped 'PBTypeName', { :namespace => 'PBType', :id => 'PBKey' }
97
+ to force creation with +PBKey+'s value.
98
+
99
+ === :serialize
100
+
101
+ You can also specify which keys to serialize after they've been mapped. Using,
102
+ attr_mapped 'PBArray', { :namespace => 'PB', :serialize => 'PBArray' }
103
+ will map +PBArray+ to +:array+ and call <tt>serialize :array</tt> in the Active Record.
104
+
105
+ === :filter
106
+
107
+ You can add proc filters to process data from hashes before it's used by Active Record.
108
+
109
+ Suppose all the dates are in the wrong format, then,
110
+ MYDATE = Proc.new { |p| Time.at(p + 978307200) }
111
+ attr_mapped 'PBDate', { :filter => { 'PBDate' => PBPerson::MYDATE } }
112
+
113
+ == Named Mappings
114
+
115
+ If for some reason, you want to use multiple mappings on the same model, you can create named mappings
116
+ with +attr_mapped_named+, where the first argument is the mapping name, followed by the same as +attr_mapped+.
117
+
118
+ class Person < ActiveRecord::Base
119
+ attr_mapped_named :phone_record, 'FullName'
120
+ end
121
+
122
+ === Dynamic methods
123
+
124
+ +mapped-record+ will then dynamically create methods so you can:
125
+
126
+ p = Person.create_with_phone_record(h)
127
+ p.update_with_phone_record(h)
128
+
129
+ == Credit
130
+
131
+ ... where deserved. A lot of cues from thoughtbot/paperclip[http://github.com/thoughtbot/paperclip] on how to set up the gem and testing, so thanks.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ %w[rake rake/clean fileutils newgem rubigen].each { |f| require f }
3
+ require File.dirname(__FILE__) + '/lib/mapped-record'
4
+
5
+ # Generate all the Rake tasks
6
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
7
+ $hoe = Hoe.new('mapped-record', MappedRecord::VERSION) do |p|
8
+ p.developer('Henry Hsu', 'henry@qlane.com')
9
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
10
+ p.rubyforge_name = p.name
11
+ p.extra_deps = [
12
+ ['activesupport','>= 2.0.2'],
13
+ ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"],
16
+ ['thoughtbot-shoulda', '>= 0'],
17
+ ['sqlite3-ruby', '>= 0']
18
+ ]
19
+ p.summary = 'Auto-magically map Hash[keys] to ActiveRecord.attributes'
20
+ p.description = 'Auto-magically map Hash[keys] to ActiveRecord.attributes'
21
+
22
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
23
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
24
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
25
+ p.rsync_args = '-av --delete --ignore-errors'
26
+ end
27
+
28
+ require 'newgem/tasks' # load /tasks/*.rake
29
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -0,0 +1,22 @@
1
+ class Hash
2
+ # Returns a new Hash where the keys have been swapped with those defined in <tt>Mapping[named_mapping]</tt>.
3
+ def map_with(named_mapping)
4
+ if Mapping.has?(named_mapping)
5
+ m = Mapping[named_mapping]
6
+
7
+ result = self.inject({}) do |result, element|
8
+ mapping = m[element.first]
9
+
10
+ to = mapping[:to] if mapping
11
+ proc = mapping[:filter] if mapping
12
+
13
+ if to
14
+ result[to] = element.last unless proc
15
+ result[to] = proc.call(element.last) if proc
16
+ end
17
+
18
+ result
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,130 @@
1
+ module MappedRecord
2
+ class Mapping
3
+ class << self
4
+ # Assigns a named mapping accessible throughout the runtime environment.
5
+ #
6
+ # Mapping.create :my_mapping, 'Ready', 'Set', 'Go'
7
+ #
8
+ # The mapping is then accessible from <tt>Mapping[:my_mapping]</tt>
9
+ #
10
+ # Given a list of strings, those keys will be mapped automatically to downcase and
11
+ # underscored attributes. (Specify these first).
12
+ #
13
+ # Configuration options:
14
+ # [<tt>:filter</tt>]
15
+ # Specify a hash of keys and procs to call before assigning to attributes.
16
+ # attr_mapped 'Date', { :after => { 'Date' => Proc.new { ... } } }
17
+ # [<tt>:namespace</tt>]
18
+ # A prefix string to remove before automatically mapping.
19
+ # attr_mapped 'PBOne', 'PBTwo', { :namespace => 'PB' }
20
+ # [<tt>'key' => :target, 'key2' => :target2, ...</tt>]
21
+ # As many manual mappings as needed.
22
+ def create(mapping_name, *map_attrs)
23
+ raise MappingError, "Not creating mapping with nil name" if mapping_name.nil?
24
+ named_mappings[mapping_name] ||= Hash.new
25
+
26
+ options = map_attrs.extract_options!
27
+ verbose = parse_verbose(options)
28
+
29
+ serialize_mappings = []
30
+ namespace = nil
31
+ type = IMPLICIT
32
+
33
+ options.each_pair do |key, value|
34
+ case key
35
+ when :namespace
36
+ namespace = value.to_s unless value.to_s.blank?
37
+ when :filter
38
+ value.each_pair do |attr, proc|
39
+ named_mappings[mapping_name][attr.to_s] ||= Hash.new
40
+ named_mappings[mapping_name][attr.to_s][:filter] = proc
41
+ end
42
+ when String, Symbol # deals with explicit mappings
43
+ raise MappingError, "Must be symbol" unless value.kind_of?(Symbol)
44
+ update_mapping(mapping_name, key, value.to_sym, EXPLICIT)
45
+ end
46
+ end
47
+
48
+ mapping_temp = Hash.new
49
+
50
+ map_attrs.each do |attr|
51
+ raise MappingError, "Must be string or symbol." unless attr.kind_of?(String) or attr.kind_of?(Symbol)
52
+
53
+ mapped_attr = attr.to_s
54
+ if namespace
55
+ match = mapped_attr[/^#{namespace}/]
56
+ raise MappingError, "Causes mapping to be ''" if mapped_attr == match
57
+ if match
58
+ mapped_attr = mapped_attr.sub(/^#{namespace}/, '')
59
+ type = NAMESPACE
60
+ end
61
+ end
62
+ mapped_attr = mapped_attr.underscore.gsub(' ', '_')
63
+ update_mapping(mapping_name, attr, mapped_attr.to_sym, type)
64
+ end
65
+
66
+ named_mappings[mapping_name]
67
+ end
68
+
69
+ def parse_verbose(options) # :nodoc:
70
+ if !options[:verbose].nil? && (options[:verbose].kind_of?(FalseClass) || options[:verbose].kind_of?(TrueClass))
71
+ verbose = options[:verbose]
72
+ options.delete(:verbose)
73
+ verbose
74
+ end
75
+ end
76
+
77
+ # Returns true if mapping of +mapping_name+ is assigned.
78
+ def has?(mapping_name)
79
+ named_mappings.include?(mapping_name)
80
+ end
81
+
82
+ # Returns true of no mappings are assigned.
83
+ def empty?
84
+ named_mappings.empty?
85
+ end
86
+
87
+ # Returns true of no mappings are assigned.
88
+ def blank?
89
+ named_mappings.blank?
90
+ end
91
+
92
+ # Clear all mappings.
93
+ def reset
94
+ named_mappings.clear
95
+ end
96
+
97
+ # Access named mappings straight from the class.
98
+ def [](key)
99
+ named_mappings[key]
100
+ end
101
+
102
+ # Assign mappings straight from the class.
103
+ def []=(key, values)
104
+ create(key, *values)
105
+ end
106
+
107
+ attr_accessor :named_mappings # :nodoc:
108
+
109
+ def named_mappings # :nodoc:
110
+ @named_mappings ||= Hash.new
111
+ end
112
+
113
+ private :named_mappings
114
+
115
+ def update_mapping(mapping_name, key, value, type) # :nodoc:
116
+ named_mapping = named_mappings[mapping_name]
117
+ named_mapping[key] ||= Hash.new
118
+
119
+ if named_mapping[key][:to].nil? or type >= named_mapping[key][:type]
120
+ raise MappingError, "Multiple keys pointing to the same symbol" unless named_mapping.select { |key_name, mapping| key_name != key && mapping[:to] == value }.blank?
121
+ named_mapping[key][:to] = value and named_mapping[key][:type] = type
122
+ end
123
+ end
124
+
125
+ private :update_mapping
126
+ end
127
+ end
128
+ end
129
+
130
+ Mapping = MappedRecord::Mapping
@@ -0,0 +1,183 @@
1
+ # See +attr_mapped+ or the README for details.
2
+ #
3
+ #
4
+ # Created by Henry Hsu on 2009-06-07.
5
+ # Copyright 2009 Qlane. All rights reserved.
6
+ #
7
+
8
+ $:.unshift(File.dirname(__FILE__)) unless
9
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
10
+
11
+ require 'mapped-record/hash/mappable'
12
+ require 'mapped-record/mapping'
13
+
14
+ module MappedRecord
15
+ VERSION = '0.0.1' # :nodoc:
16
+
17
+ IMPLICIT = 0 # :nodoc:
18
+ NAMESPACE = 1 # :nodoc:
19
+ EXPLICIT = 2 # :nodoc:
20
+
21
+ class << self
22
+ def included base #:nodoc:
23
+ base.extend ClassMethods
24
+ end
25
+ end
26
+
27
+ #--
28
+ # TODO subclass this to signify each kind of error
29
+ #++
30
+ class MappingError < StandardError; end # :nodoc:
31
+
32
+ module ClassMethods
33
+ # Assigns a mapping for the current ActiveRecord class.
34
+ #
35
+ # class Person < ActiveRecord::Base
36
+ # attr_mapped 'PBName', 'PBAddress', 'PBEmail', { :namespace => 'Email' }
37
+ # end
38
+ #
39
+ # Given a list of strings, those keys will be mapped automatically to downcase and
40
+ # underscored attributes. (Specify these first).
41
+ #
42
+ # Configuration options:
43
+ # [<tt>:id</tt>]
44
+ # The key to map to the primary key.
45
+ # attr_mapped { :id => 'Key' }
46
+ # [<tt>:serialize</tt>]
47
+ # Any keys to serialize after mapping.
48
+ # attr_mapped 'Array', 'Blob', { :serialize => ['Array', 'Blob'] }
49
+ # [<tt>:filter</tt>]
50
+ # Specify a hash of keys and procs to call before assigning to attributes.
51
+ # attr_mapped 'Date', { :after => { 'Date' => Proc.new { ... } } }
52
+ # [<tt>:namespace</tt>]
53
+ # A prefix string to remove before automatically mapping.
54
+ # attr_mapped 'PBOne', 'PBTwo', { :namespace => 'PB' }
55
+ # [<tt>'key' => :attribute, 'key2' => :attribute2, ...</tt>]
56
+ # As many manual mappings as needed.
57
+ def attr_mapped(*map_attrs)
58
+ attr_mapped_named(class_name, *map_attrs)
59
+ end
60
+
61
+ # Assigns mappings to a name.
62
+ #
63
+ # class Person < ActiveRecord::Base
64
+ # attr_mapped_named :public, 'PBName', 'PBAddress', 'PBEmail', { :namespace => 'Email' }
65
+ # end
66
+ #
67
+ # The mapping can then be used with dynamic create and update methods.
68
+ # From the example above:
69
+ # p = Person.create_with_public({ 'PBName' => 'Mr. Name' })
70
+ # p.update_with_public({ 'PBName' => 'Full Name' })
71
+ def attr_mapped_named(named_mapping = nil, *map_attrs)
72
+ include InstanceMethods
73
+
74
+ unless self.respond_to?(:attr_mapped_serialized)
75
+ class_inheritable_accessor :attr_mapped_serialized
76
+ write_inheritable_attribute :attr_mapped_serialized, Hash.new
77
+ end
78
+
79
+ unless self.respond_to?(:attr_hashed_id)
80
+ class_inheritable_accessor :attr_hashed_id
81
+ write_inheritable_attribute :attr_hashed_id, ''
82
+ end
83
+
84
+ raise ArgumentError, "Mapping name not given." if named_mapping.nil?
85
+ raise MappingError, "No options given." if map_attrs.blank?
86
+
87
+ options = map_attrs.extract_options!
88
+
89
+ serialize_mappings = []
90
+
91
+ options.each_pair do |key, value|
92
+ case key
93
+ when :id
94
+ self.attr_hashed_id = value.to_s
95
+ when :serialize
96
+ keys = [value.to_s] unless value.kind_of?(Array) # TODO if-else blocks probably more efficient
97
+ keys = value.collect { |v| v.to_s } if value.kind_of?(Array)
98
+ serialize_mappings |= keys
99
+ end
100
+ end
101
+ options.delete(:id)
102
+ options.delete(:serialize)
103
+
104
+ map_attrs << options
105
+ Mapping.create named_mapping, *map_attrs
106
+
107
+ if Mapping.has?(named_mapping)
108
+ self.instance_eval %Q{ def create_with_#{named_mapping}(hash); create_with(hash, :#{named_mapping}); end; }
109
+ self.class_eval %Q{ def update_with_#{named_mapping}(hash); update_with(hash, :#{named_mapping}); end }
110
+ self.attr_mapped_serialized[named_mapping] ||= Hash.new
111
+ self.attr_mapped_serialized[named_mapping] = update_serialized(named_mapping)
112
+ end
113
+
114
+ serialize_mappings.each do |attr|
115
+ raise MappingError, "Serializing :id not allowed." if !self.attr_hashed_id.blank? && attr == self.attr_hashed_id
116
+ to_serialize = Mapping[named_mapping][attr][:to].to_sym
117
+
118
+ # need to know serialized attributes to 'watch'
119
+ self.attr_mapped_serialized[named_mapping][attr] = to_serialize
120
+
121
+ self.instance_eval { serialize to_serialize }
122
+ end
123
+ end
124
+
125
+ # Accepts a hash to map and creates the Active Record object with its values.
126
+ def create_with(hash = {}, named_mapping = nil)
127
+ named_mapping = self.class_name unless named_mapping
128
+
129
+ self.create(with_attributes(named_mapping, hash)) do |r|
130
+ id = hash[self.attr_hashed_id]
131
+ r.id = id if id
132
+ end
133
+ end
134
+
135
+ # A helper to check if the Active Record object responds to mapped-record methods.
136
+ def acts_like_mapped?
137
+ true
138
+ end
139
+
140
+ # Maps the values in +hash+ with +named_mapping+ for use in Active Record.
141
+ def with_attributes(named_mapping, hash)
142
+ attrs = hash.map_with(named_mapping)
143
+ attrs.delete(:id) if attrs[:id]
144
+ attrs
145
+ end
146
+
147
+ # Maintains that +serialize+ is set for correct attribute.
148
+ def update_serialized(named_mapping)
149
+ self.attr_mapped_serialized[named_mapping].inject({}) do |result, element|
150
+ key = element.first
151
+ serialized_as = element.last
152
+
153
+ to_serialize = Mapping[named_mapping][key][:to].to_sym
154
+ if to_serialize != serialized_as
155
+ warn "[MappedRecord] overriding :#{serialized_as} with :#{to_serialize}, will not remove 'serialize :#{serialized_as}'"
156
+ self.instance_eval { serialize to_serialize }
157
+ result[key] = to_serialize
158
+ end
159
+ end
160
+ end
161
+
162
+ private :update_serialized
163
+ end
164
+
165
+ module InstanceMethods
166
+ # Accepts a hash to map and update the object with.
167
+ def update_with(hash = {}, named_mapping = nil)
168
+ named_mapping = self.class.class_name unless named_mapping
169
+
170
+ self.attributes = self.class.with_attributes(named_mapping, hash)
171
+
172
+ if !self.changes.blank?
173
+ self.save
174
+ else
175
+ false
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ if Object.const_defined?("ActiveRecord")
182
+ ActiveRecord::Base.send(:include, MappedRecord)
183
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/mapped-record.rb'}"
9
+ puts "Loading mapped-record gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper.rb'
2
+
3
+ class TestMappable < Test::Unit::TestCase
4
+ context "Hash" do
5
+ setup do
6
+ @hash = {"PhotoCount"=>1, "KeyList"=>["2"], "KeyPhotoKey"=>"2", "RollID"=>3, "RollDateAsTimerInterval"=>263609145.0, "RollName"=>"May 9, 2009"}
7
+ end
8
+
9
+ should "have #map_with method" do
10
+ assert_respond_to @hash, :map_with
11
+ end
12
+
13
+ context "with mapping" do
14
+ setup do
15
+ Mapping.create :iphoto_roll2, 'PhotoCount', 'KeyList', 'RollName', 'RollID', :namespace => 'Roll', 'KeyPhotoKey' => :key_photo_id, 'RollDateAsTimerInterval' => :date, :filter => { 'RollDateAsTimerInterval' => Proc.new { |p| Time.at(p.to_f + 978307200) } }
16
+ end
17
+
18
+ should "map properly" do
19
+ mapped_hash = @hash.map_with(:iphoto_roll2)
20
+ assert_not_nil mapped_hash
21
+ assert_equal 1, mapped_hash[:photo_count]
22
+ assert_equal "2", mapped_hash[:key_photo_id]
23
+ assert_equal ["2"], mapped_hash[:key_list]
24
+ assert_equal Time.at(263609145.0 + 978307200), mapped_hash[:date]
25
+ assert_equal "May 9, 2009", mapped_hash[:name]
26
+ assert_equal 3, mapped_hash[:id]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,166 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class TestMapping < Test::Unit::TestCase
4
+ context "Mapping" do
5
+ setup do
6
+ @sample_hash = {"PhotoCount"=>1, "KeyList"=>["2"], "KeyPhotoKey"=>"2", "RollID"=>3, "RollDateAsTimerInterval"=>263609145.0, "RollName"=>"May 9, 2009"}
7
+ end
8
+
9
+ context "creating named mapping" do
10
+ setup do
11
+ @mapping_name = :test_mapping
12
+ @@proc = Proc.new { |p| 'PROC-ED' }
13
+ Mapping.create @mapping_name, 'ImplicitMapping', 'AnotherMapping', 'ForNamespaceMapping', 'ExplicitMapping' => :explicit, :namespace => 'ForNamespace', :filter => { 'ImplicitMapping' => @@proc }
14
+ end
15
+
16
+ should "have mapping named #{@mapping_name}" do
17
+ assert Mapping.has?(@mapping_name)
18
+ assert Mapping[@mapping_name].is_a?(Hash)
19
+ end
20
+
21
+ should "have #[] method" do
22
+ assert_respond_to Mapping, :[]
23
+ end
24
+
25
+ should "have #[]= method do the same as #create" do
26
+ assert_respond_to Mapping, :[]=
27
+ Mapping[:alt_test_mapping] = 'ImplicitMapping', 'AnotherMapping', 'ForNamespaceMapping', {'ExplicitMapping' => :explicit, :namespace => 'ForNamespace', :filter => { 'ImplicitMapping' => @@proc }}
28
+ assert_equal Mapping[:alt_test_mapping], Mapping[@mapping_name]
29
+ end
30
+
31
+ should_map_implicit :test_mapping, 'ImplicitMapping', :implicit_mapping
32
+ should_map_implicit :test_mapping, 'AnotherMapping', :another_mapping
33
+ should_map_namespace :test_mapping, 'ForNamespaceMapping', :mapping
34
+ should_map_explicit :test_mapping, 'ExplicitMapping', :explicit
35
+
36
+ should "map proc" do
37
+ assert_same @@proc, Mapping[@mapping_name]['ImplicitMapping'][:filter]
38
+ end
39
+ end
40
+
41
+ context "with options" do
42
+
43
+ setup { Mapping.reset }
44
+
45
+ should "allow symbol mapping" do
46
+ assert_nothing_raised(MappedRecord::MappingError) { Mapping.create :mixed, :key => :symbol }
47
+ Mapping.reset
48
+ assert_nothing_raised(MappedRecord::MappingError) { Mapping.create :mixed, :symbol }
49
+ end
50
+
51
+ should "not allow non-symbol mappings" do
52
+ assert_raise(MappedRecord::MappingError) { Mapping.create :mixed, :key => 1 }
53
+ assert_raise(MappedRecord::MappingError) { Mapping.create :mixed, :key => Fixnum }
54
+ assert_raise(MappedRecord::MappingError) { Mapping.create :mixed, :key => 'String' }
55
+ assert_raise(MappedRecord::MappingError) { Mapping.create :mixed, 1 }
56
+ assert_raise(MappedRecord::MappingError) { Mapping.create :mixed, Fixnum }
57
+ end
58
+
59
+ context "with mixed options" do
60
+ # 4 combinations of symbol:string
61
+ # validate :to is string or symbol
62
+ setup do
63
+ Mapping.create :mixed, :SymbolMapping, 'StringMapping', :symbol_key => :key, "symbol_key" => :other_key
64
+ end
65
+
66
+ should_map_explicit :mixed, :symbol_key, :key
67
+ should_map_explicit :mixed, "symbol_key", :other_key
68
+ should_map_implicit :mixed, 'StringMapping', :string_mapping
69
+ should_map_implicit :mixed, :SymbolMapping, :symbol_mapping
70
+ end
71
+ end
72
+
73
+ context "resetting mappings" do
74
+ should "have #reset method" do
75
+ assert_respond_to(Mapping, :reset)
76
+ end
77
+
78
+ should "clear mappings" do
79
+ assert(!Mapping.blank?, "Mapping is blank.")
80
+ Mapping.reset
81
+ assert(Mapping.blank?, "Mappings should be blank.")
82
+ end
83
+ end
84
+
85
+ context "called multiple times" do
86
+
87
+ # Namespaces work on a per-#attr_mapped basis
88
+ context "with different namespaces" do
89
+ setup do
90
+ Mapping.create :diff_ns, 'ForNamespaceMapping', :namespace => 'ForNamespace'
91
+ Mapping.create :diff_ns, 'ForNamespaceMapping', :namespace => 'For'
92
+ end
93
+
94
+ should_map_namespace :diff_ns, 'ForNamespaceMapping', :namespace_mapping
95
+ end
96
+
97
+ context "with all (naturally ordered) mappings" do
98
+ setup do
99
+ Mapping.create :natural, 'OrderedMapping'
100
+ Mapping.create :natural, 'OrderedMapping', :namespace => 'Ordered'
101
+ Mapping.create :natural, 'OrderedMapping' => :ordered
102
+ end
103
+
104
+ should_map_explicit :natural, 'OrderedMapping', :ordered
105
+ end
106
+
107
+ context "with all (inverse ordered) mappings" do
108
+ setup do
109
+ Mapping.create :reversed, 'OrderedMapping' => :ordered
110
+ Mapping.create :reversed, 'OrderedMapping', :namespace => 'Ordered'
111
+ Mapping.create :reversed, 'OrderedMapping'
112
+ end
113
+
114
+ should_map_explicit :reversed, 'OrderedMapping', :ordered
115
+ end
116
+ end
117
+
118
+ # ==================
119
+ # = Tests overlaps =
120
+ # ==================
121
+ context "with namespace and explicit overlap" do
122
+ setup { Mapping.create :ns_e_overlap, 'ImplicitMapping', 'AnotherMapping', 'ExplicitMapping' => :explicit_mapping, :namespace => 'Explicit' }
123
+ should_map_explicit :ns_e_overlap, 'ExplicitMapping', :explicit_mapping
124
+ end
125
+
126
+ context "with namespace and implicit overlap" do
127
+ setup { Mapping.create :ns_i_overlap, 'ImplicitMapping', 'ImplicitMappingSecond', 'ExplicitMapping' => :explicit_mapping, :namespace => 'Implicit' }
128
+ should_map_namespace :ns_i_overlap, 'ImplicitMapping', :mapping
129
+ end
130
+
131
+ context "with explicit and implicit overlap" do
132
+ setup { Mapping.create :e_i_overlap, 'ImplicitMapping', 'ImplicitMapping' => :explicit_mapping, :namespace => 'NoMatch' }
133
+ should_map_explicit :e_i_overlap, 'ImplicitMapping', :explicit_mapping
134
+ end
135
+
136
+ # ========================
137
+ # = Raising MappingError =
138
+ # ========================
139
+ should "raise MappingError when #namespace causes mapping to be ''" do
140
+ assert_raises MappedRecord::MappingError do
141
+ Mapping.create :ns_to_blank, 'ImplicitMapping', 'ImplicitMappingSecond', :namespace => 'ImplicitMapping'
142
+ end
143
+ end
144
+
145
+ should "raise MappingError with many-to-one mappings" do # because it will have unexpected results
146
+ assert_raises MappedRecord::MappingError do
147
+ Mapping.create :many_to_one, 'Root', 'MappingWithRoot', :namespace => 'MappingWith'
148
+ end
149
+ end
150
+
151
+ should "raise MappingError with un-named mapping" do
152
+ Mapping.reset
153
+ assert_raises MappedRecord::MappingError do
154
+ Mapping.create nil, 'Root', 'MappingWithRoot', :namespace => 'MappingWith'
155
+ end
156
+ assert(Mapping.blank?, "Mapping shouldn't be set.")
157
+ end
158
+
159
+ # should "raise MappingError with invalid mapping names" do
160
+ # Mapping.reset
161
+ # assert_raise(MappedRecord::MappingError) do
162
+ # Mapping.create 1, 'Root'
163
+ # end
164
+ # end
165
+ end
166
+ end
@@ -0,0 +1,93 @@
1
+ require 'stringio'
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ gem 'thoughtbot-shoulda', ">= 2.9.0"
5
+ require 'shoulda'
6
+
7
+ gem 'sqlite3-ruby'
8
+
9
+ require 'active_record'
10
+ require 'active_support'
11
+
12
+ require File.dirname(__FILE__) + '/../lib/mapped-record'
13
+
14
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
15
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
16
+ ActiveRecord::Base.establish_connection(config['test'])
17
+
18
+ def reset_class class_name
19
+ ActiveRecord::Base.send(:include, MappedRecord)
20
+ Object.send(:remove_const, class_name) rescue nil
21
+ klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
22
+ klass.class_eval{ include MappedRecord }
23
+ klass
24
+ end
25
+
26
+ def reset_table table_name, &block
27
+ block ||= lambda{ true }
28
+ ActiveRecord::Base.connection.create_table :dummies, {:force => true}, &block
29
+ end
30
+
31
+ def modify_table table_name, &block
32
+ ActiveRecord::Base.connection.change_table :dummies, &block
33
+ end
34
+
35
+ def rebuild_model(*args)
36
+ ActiveRecord::Base.connection.create_table :dummies, :force => true do |t|
37
+ t.string :name
38
+ t.datetime :date
39
+ t.references :key_photo
40
+ t.integer :photo_count
41
+ t.text :key_list
42
+ t.references :library
43
+ end
44
+ rebuild_class(*args)
45
+ end
46
+
47
+ def rebuild_class(*args)
48
+ ActiveRecord::Base.send(:include, MappedRecord)
49
+ Object.send(:remove_const, "Dummy") rescue nil
50
+ Object.const_set("Dummy", Class.new(ActiveRecord::Base))
51
+ Dummy.class_eval do
52
+ include MappedRecord
53
+ end
54
+ if args.size > 0
55
+ Dummy.class_eval do
56
+ attr_mapped_named(*args)
57
+ end
58
+ end
59
+ end
60
+
61
+ def should_map name, field, mapping, type, klass=Mapping
62
+ type_s = ''
63
+ case type
64
+ when MappedRecord::EXPLICIT
65
+ type_s = 'explicit'
66
+ when MappedRecord::IMPLICIT
67
+ type_s = 'implicit'
68
+ when MappedRecord::NAMESPACE
69
+ type_s = 'namespace'
70
+ else
71
+ raise "Unknown mapping type"
72
+ end
73
+
74
+ should "map #{type_s} from #{field} => #{mapping} for mapping :#{name}" do
75
+ assert_not_nil(klass.blank?, "Mappings not set up correctly.")
76
+ assert_not_nil(klass[name], "Mapping #{name} not set up correctly.")
77
+ assert_not_nil(klass[name][field], "Mapping #{name}'s #{field} not set up correctly.")
78
+ assert_equal(mapping, klass[name][field][:to], "Mapping doesn't match.")
79
+ assert_equal(type, klass[name][field][:type], "Mapping type doesn't match.")
80
+ end
81
+ end
82
+
83
+ def should_map_explicit name, field, mapping, klass=Mapping
84
+ should_map name, field, mapping, MappedRecord::EXPLICIT, klass
85
+ end
86
+
87
+ def should_map_implicit name, field, mapping, klass=Mapping
88
+ should_map name, field, mapping, MappedRecord::IMPLICIT, klass
89
+ end
90
+
91
+ def should_map_namespace name, field, mapping, klass=Mapping
92
+ should_map name, field, mapping, MappedRecord::NAMESPACE, klass
93
+ end
@@ -0,0 +1,260 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestMappedRecord < Test::Unit::TestCase
4
+ context "Dummy" do
5
+ setup do
6
+ rebuild_class
7
+ end
8
+
9
+ should "have #attr_mapped method" do
10
+ assert_respond_to Dummy, :attr_mapped
11
+ end
12
+
13
+ should "have #attr_mapped_named method" do
14
+ assert_respond_to Dummy, :attr_mapped_named
15
+ end
16
+
17
+ should "act like mapped" do
18
+ assert Dummy.acts_like?(:mapped)
19
+ end
20
+
21
+ should "raise ArgumentError when no mapping name and no options not given" do
22
+ assert_raises ArgumentError do
23
+ Dummy.class_eval do
24
+ attr_mapped_named
25
+ end
26
+ end
27
+ end
28
+
29
+ should "raise MappingError when name given but options not given" do
30
+ assert_raises MappedRecord::MappingError do
31
+ rebuild_class 'some_name'
32
+ end
33
+ end
34
+
35
+ # =======================
36
+ # = Setting up mappings =
37
+ # =======================
38
+ context "when #attr_mapped" do
39
+ context "untitled" do
40
+ setup do
41
+ Mapping.reset
42
+ Dummy.class_eval do
43
+ attr_mapped 'FullName', 'Email'
44
+ end
45
+ end
46
+
47
+ should "map with class_name as default mapping name" do
48
+ assert(Mapping.has?(Dummy.class_name), "Mapping name not #{Dummy.class_name}.")
49
+ end
50
+
51
+ should_map_implicit 'Dummy', 'FullName', :full_name
52
+ should_map_implicit 'Dummy', 'Email', :email
53
+ end
54
+ end
55
+
56
+ context "when #attr_mapped_named" do # TODO should not allow mapping names with spaces
57
+ context "with dummy mapping and options" do
58
+
59
+ setup do
60
+ Mapping.reset
61
+ @@proc = Proc.new { |p| 'PROC-ED' }
62
+ rebuild_class :dummy, 'ImplicitMapping', 'AnotherMapping', 'ForNamespaceMapping', 'ExplicitMapping' => :explicit, :namespace => 'ForNamespace', :id => 'ForID', :filter => { 'ImplicitMapping' => @@proc }, :serialize => 'ForNamespaceMapping'
63
+ end
64
+
65
+ should_map_implicit :dummy, 'ImplicitMapping', :implicit_mapping
66
+ should_map_implicit :dummy, 'AnotherMapping', :another_mapping
67
+ should_map_namespace :dummy, 'ForNamespaceMapping', :mapping
68
+ should_map_explicit :dummy, 'ExplicitMapping', :explicit
69
+
70
+ should "map id" do
71
+ assert_equal 'ForID', Dummy.attr_hashed_id
72
+ end
73
+
74
+ should "map proc" do
75
+ assert_same @@proc, Mapping[:dummy]['ImplicitMapping'][:filter]
76
+ end
77
+
78
+ should "serialize" do
79
+ assert Dummy.serialized_attributes.include?("mapping")
80
+ end
81
+
82
+ context "a subclass" do
83
+ setup do
84
+ class ::SubDummy < Dummy; end
85
+ end
86
+
87
+ should "map id" do
88
+ assert_equal 'ForID', SubDummy.attr_hashed_id
89
+ end
90
+
91
+ should "serialize" do
92
+ assert SubDummy.serialized_attributes.include?("mapping")
93
+ end
94
+
95
+ teardown do
96
+ Object.send(:remove_const, "SubDummy") rescue nil
97
+ end
98
+ end
99
+ end
100
+
101
+ context "with multiple serializes" do
102
+ setup do
103
+ Mapping.reset
104
+ rebuild_class :multiple_s, 'KeyOne', 'KeyTwo', :serialize => ['KeyOne', 'KeyTwo']
105
+ end
106
+
107
+ should "serialize :key_one and :key_two" do
108
+ assert Dummy.serialized_attributes.include?("key_one")
109
+ assert Dummy.serialized_attributes.include?("key_two")
110
+ end
111
+ end
112
+
113
+ # ========================
114
+ # = Raising MappingError =
115
+ # ========================
116
+
117
+ should "raise MappingError when #serialize and #id overlap" do
118
+ assert_raises MappedRecord::MappingError do
119
+ rebuild_class :ser, 'ImplicitMapping', 'AnotherMapping', 'ForNamespaceMapping', 'ExplicitMapping' => :explicit, :namespace => 'ForNamespace', :id => 'AnotherMapping', :serialize => 'AnotherMapping'
120
+ end
121
+ end
122
+
123
+ # ============================================
124
+ # = #attr_mapped_named called multiple times =
125
+ # ============================================
126
+ context "called multiple times" do
127
+
128
+ context "with serialized mapping being overridden" do
129
+ setup do
130
+ rebuild_class
131
+ Dummy.class_eval do
132
+ attr_mapped_named :overriding, 'AMapping', :serialize => 'AMapping'
133
+ attr_mapped_named :overriding, 'AMapping' => :mapping
134
+ end
135
+ end
136
+
137
+ should "update serialize" do
138
+ assert Dummy.serialized_attributes.include?("mapping")
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # ======================
145
+ # = Creating with hash =
146
+ # ======================
147
+ context "creating" do
148
+ context "with named mapping" do
149
+ setup do
150
+ Mapping.reset
151
+ rebuild_model
152
+ Dummy.class_eval do
153
+ attr_mapped_named :iphoto_roll, 'PhotoCount', 'KeyList', 'RollName', :namespace => 'Roll', 'KeyPhotoKey' => :key_photo_id, 'RollDateAsTimerInterval' => :date, :id => 'RollID', :serialize => 'KeyList', :filter => { 'RollDateAsTimerInterval' => Proc.new { |p| Time.at(p.to_f + 978307200) } }
154
+ end
155
+ @rand = rand(10000)
156
+ @sample_hash = {"PhotoCount"=>1, "KeyList"=>["2"], "KeyPhotoKey"=>"2", "RollID"=>@rand, "RollDateAsTimerInterval"=>263609145.0, "RollName"=>"May 9, 2009"}
157
+ @dummy = Dummy.create_with_iphoto_roll(@sample_hash)
158
+ assert_not_nil @dummy
159
+ end
160
+
161
+ should "have dynamic #create_with_iphoto_roll method" do
162
+ assert_respond_to Dummy, :create_with_iphoto_roll
163
+ end
164
+
165
+ should "not mass-assign #id" do
166
+ assert !Dummy.with_attributes(:iphoto_roll, @sample_hash).include?(:id)
167
+ end
168
+
169
+ should "create properly" do
170
+ assert_equal 1, @dummy.photo_count
171
+ assert_equal 2, @dummy.key_photo_id
172
+ assert_equal ["2"], @dummy.key_list
173
+ assert_equal Time.at(263609145.0 + 978307200), @dummy.date
174
+ assert_equal "May 9, 2009", @dummy.name
175
+ assert_equal @rand, @dummy.id
176
+ end
177
+
178
+ should "have #update_with method" do
179
+ assert_respond_to @dummy, :update_with
180
+ end
181
+
182
+ should "have dynamic #update_with_iphoto_roll method" do
183
+ assert_respond_to @dummy, :update_with_iphoto_roll
184
+ end
185
+
186
+ should "fail with empty update" do
187
+ assert !@dummy.update_with_iphoto_roll({})
188
+ end
189
+
190
+ should "fail updating with no changes" do
191
+ assert !@dummy.update_with_iphoto_roll(@sample_hash)
192
+ end
193
+
194
+ context "then updating" do
195
+
196
+ setup do
197
+ @update_hash = {"PhotoCount"=>2, "KeyList"=>["2", "3"], "KeyPhotoKey"=>"3", "RollID"=>rand(10000), "RollDateAsTimerInterval"=>263609245.0, "RollName"=>"NewName"}
198
+ assert_not_nil @dummy
199
+ end
200
+
201
+ should "update properly" do
202
+ assert @dummy.update_with_iphoto_roll(@update_hash)
203
+ assert_equal 2, @dummy.photo_count
204
+ assert_equal 3, @dummy.key_photo_id
205
+ assert_equal ["2", "3"], @dummy.key_list
206
+ assert_equal Time.at(263609245.0 + 978307200), @dummy.date
207
+ assert_equal "NewName", @dummy.name
208
+ assert_equal @rand, @dummy.id
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ # the rest relies on named mapping (above)
215
+ context "with unnamed mapping" do
216
+ setup do
217
+ Mapping.reset
218
+ rebuild_model
219
+ Dummy.class_eval do
220
+ attr_mapped 'PhotoCount', 'KeyList', 'RollName', :namespace => 'Roll', 'KeyPhotoKey' => :key_photo_id, 'RollDateAsTimerInterval' => :date, :id => 'RollID', :serialize => 'KeyList', :filter => { 'RollDateAsTimerInterval' => Proc.new { |p| Time.at(p.to_f + 978307200) } }
221
+ end
222
+ @rand = rand(10000)
223
+ @sample_hash = {"PhotoCount"=>1, "KeyList"=>["2"], "KeyPhotoKey"=>"2", "RollID"=>@rand, "RollDateAsTimerInterval"=>263609145.0, "RollName"=>"May 9, 2009"}
224
+ @dummy = Dummy.create_with(@sample_hash)
225
+ end
226
+
227
+ should "have #create_with method" do
228
+ assert_respond_to Dummy, :create_with
229
+ end
230
+
231
+ should "create properly" do
232
+ assert_not_nil @dummy
233
+ assert_equal 1, @dummy.photo_count
234
+ assert_equal 2, @dummy.key_photo_id
235
+ assert_equal ["2"], @dummy.key_list
236
+ assert_equal Time.at(263609145.0 + 978307200), @dummy.date
237
+ assert_equal "May 9, 2009", @dummy.name
238
+ assert_equal @rand, @dummy.id
239
+ end
240
+
241
+ context "then updating" do
242
+
243
+ setup do
244
+ @update_hash = {"PhotoCount"=>2, "KeyList"=>["2", "3"], "KeyPhotoKey"=>"3", "RollID"=>rand(10000), "RollDateAsTimerInterval"=>263609245.0, "RollName"=>"NewName"}
245
+ assert_not_nil @dummy
246
+ end
247
+
248
+ should "update properly" do
249
+ assert @dummy.update_with(@update_hash)
250
+ assert_equal 2, @dummy.photo_count
251
+ assert_equal 3, @dummy.key_photo_id
252
+ assert_equal ["2", "3"], @dummy.key_list
253
+ assert_equal Time.at(263609245.0 + 978307200), @dummy.date
254
+ assert_equal "NewName", @dummy.name
255
+ assert_equal @rand, @dummy.id
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hsume2-mapped-record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Henry Hsu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-07 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: newgem
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: thoughtbot-shoulda
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: sqlite3-ruby
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: hoe
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.8.0
64
+ version:
65
+ description: Auto-magically map Hash[keys] to ActiveRecord.attributes
66
+ email:
67
+ - henry@qlane.com
68
+ executables: []
69
+
70
+ extensions: []
71
+
72
+ extra_rdoc_files:
73
+ - History.txt
74
+ - Manifest.txt
75
+ - README.rdoc
76
+ files:
77
+ - History.txt
78
+ - LICENSE
79
+ - Manifest.txt
80
+ - README.rdoc
81
+ - Rakefile
82
+ - lib/mapped-record.rb
83
+ - lib/mapped-record/hash/mappable.rb
84
+ - lib/mapped-record/mapping.rb
85
+ - script/console
86
+ - script/destroy
87
+ - script/generate
88
+ - test/database.yml
89
+ - test/hashed-record/hash/test_mappable.rb
90
+ - test/hashed-record/test_mapping.rb
91
+ - test/test_helper.rb
92
+ - test/test_mapped_record.rb
93
+ has_rdoc: false
94
+ homepage: http://github.com/hsume2/mapped-record
95
+ post_install_message:
96
+ rdoc_options:
97
+ - --main
98
+ - README.rdoc
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: "0"
106
+ version:
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ version:
113
+ requirements: []
114
+
115
+ rubyforge_project: mapped-record
116
+ rubygems_version: 1.2.0
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Auto-magically map Hash[keys] to ActiveRecord.attributes
120
+ test_files:
121
+ - test/hashed-record/hash/test_mappable.rb
122
+ - test/hashed-record/test_mapping.rb
123
+ - test/test_helper.rb
124
+ - test/test_mapped_record.rb