hsume2-mapped-record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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