veneer 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +12 -1
  3. data/README.md +55 -87
  4. data/Rakefile +1 -1
  5. data/lib/veneer.rb +6 -1
  6. data/lib/veneer/adapters/activerecord.rb +2 -0
  7. data/lib/veneer/adapters/activerecord/class_wrapper.rb +99 -5
  8. data/lib/veneer/adapters/activerecord/instance_wrapper.rb +4 -2
  9. data/lib/veneer/adapters/activerecord/pre_3.0_class_wrapper.rb +62 -1
  10. data/lib/veneer/adapters/activerecord/property.rb +22 -0
  11. data/lib/veneer/adapters/datamapper.rb +1 -0
  12. data/lib/veneer/adapters/datamapper/class_wrapper.rb +90 -6
  13. data/lib/veneer/adapters/datamapper/property.rb +25 -0
  14. data/lib/veneer/adapters/mongomapper.rb +1 -0
  15. data/lib/veneer/adapters/mongomapper/class_wrapper.rb +68 -0
  16. data/lib/veneer/adapters/mongomapper/property.rb +16 -0
  17. data/lib/veneer/base/class_wrapper.rb +110 -7
  18. data/lib/veneer/base/property.rb +27 -0
  19. data/lib/veneer/lint.rb +4 -180
  20. data/lib/veneer/lint/adapter.rb +282 -0
  21. data/lib/veneer/lint/base.rb +10 -0
  22. data/lib/veneer/lint/properties.rb +78 -0
  23. data/lib/veneer/version.rb +1 -1
  24. data/test/support/helpers.rb +10 -1
  25. data/test/test_helper.rb +3 -3
  26. data/test/veneer/activerecord/test_adapter.rb +80 -0
  27. data/test/veneer/activerecord/test_properties.rb +73 -0
  28. data/test/veneer/datamapper/test_adapter.rb +71 -0
  29. data/test/veneer/datamapper/test_properties.rb +59 -0
  30. data/test/veneer/{adapters/test_mongomapper.rb → mongomapper/test_adapter.rb} +35 -11
  31. data/test/veneer/mongomapper/test_properties.rb +56 -0
  32. data/test/veneer_test.rb +35 -0
  33. data/veneer.gemspec +2 -0
  34. metadata +57 -51
  35. data/test/veneer/adapters/test_active_record.rb +0 -56
  36. data/test/veneer/adapters/test_datamapper.rb +0 -56
@@ -0,0 +1,22 @@
1
+ module ActiveRecord
2
+ class Base
3
+ module VeneerInterface
4
+ class Property < Veneer::Base::Property
5
+ def normalize(type)
6
+ case type
7
+ when :serial, :integer then Integer
8
+ when :string, :text then String
9
+ when :float then Float
10
+ when :decimal then BigDecimal
11
+ when :datetime then DateTime
12
+ when :time then Time
13
+ when :date then Date
14
+ when :binary then StringIO
15
+ when :boolean then TrueClass
16
+ else type
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,2 +1,3 @@
1
+ require 'veneer/adapters/datamapper/property'
1
2
  require 'veneer/adapters/datamapper/class_wrapper'
2
3
  require 'veneer/adapters/datamapper/instance_wrapper'
@@ -2,10 +2,69 @@ module DataMapper
2
2
  module Resource
3
3
  module VeneerInterface
4
4
  class ClassWrapper < ::Veneer::Base::ClassWrapper
5
+ def self.model_classes
6
+ ::DataMapper::Model.descendants
7
+ end
8
+
5
9
  def new(opts = {})
6
10
  ::Kernel.Veneer(klass.new(opts))
7
11
  end
8
12
 
13
+ def collection_associations
14
+ @collection_associations ||= begin
15
+ result = klass.relationships.inject([]) do |ary, rel|
16
+ if rel.max > 1
17
+ ary << {
18
+ :name => rel.name,
19
+ :class => rel.child_model
20
+ }
21
+ end
22
+ ary
23
+ end
24
+ result
25
+ end
26
+ end
27
+
28
+ def member_associations
29
+ @member_associations ||= begin
30
+ result = klass.relationships.inject([]) do |ary, rel|
31
+ if rel.max == 1
32
+ ary << {
33
+ :name => rel.name,
34
+ :class => rel.parent_model
35
+ }
36
+ end
37
+ ary
38
+ end
39
+ result
40
+ end
41
+ end
42
+
43
+ def properties
44
+ @properties ||= begin
45
+ klass.properties.map do |property|
46
+ ::DataMapper::Resource::VeneerInterface::Property.new(
47
+ self,
48
+ {
49
+ :name => property.name,
50
+ :type => property,
51
+ :constraints => {
52
+ :length => property.options[:length],
53
+ :nullable? => property.options[:allow_nil],
54
+ :precision => property.options[:precision],
55
+ :scale => property.options[:scale],
56
+ },
57
+ :primary => primary_keys.include?(property.name),
58
+ }
59
+ )
60
+ end
61
+ end
62
+ end
63
+
64
+ def primary_keys
65
+ @primary_keys ||= klass.key.map { |key| key.name }
66
+ end
67
+
9
68
  def destroy_all
10
69
  klass.all.destroy
11
70
  end
@@ -15,7 +74,32 @@ module DataMapper
15
74
  end
16
75
 
17
76
  def find_many(opts)
18
- klass.all(dm_conditions_from_opts(opts))
77
+ klass.all(dm_conditions_from_opts(opts)).to_a
78
+ end
79
+
80
+ def count(opts={})
81
+ opts[:conditions].nil? ? klass.count : klass.count(opts[:conditions])
82
+ end
83
+
84
+ def sum(field, opts={})
85
+ opts = ::Hashie::Mash.new(opts)
86
+ klass.all(dm_conditions_from_opts(opts)).sum(field)
87
+ end
88
+
89
+ def min(field, opts={})
90
+ opts = ::Hashie::Mash.new(opts)
91
+ klass.all(dm_conditions_from_opts(opts)).min(field)
92
+ end
93
+
94
+ def max(field, opts={})
95
+ opts = ::Hashie::Mash.new(opts)
96
+ klass.all(dm_conditions_from_opts(opts)).max(field)
97
+ end
98
+
99
+ # Delegate to validators_on if ActiveModel::Validations has been
100
+ # included in the model
101
+ def validators_on(name)
102
+ klass <=> ::ActiveModel::Validations ? klass.validators_on(name) : []
19
103
  end
20
104
 
21
105
  private
@@ -38,20 +122,20 @@ module DataMapper
38
122
 
39
123
  if raw.order.present?
40
124
  opts[:order] = case raw.order
41
- when Array
125
+ when ::Array
42
126
  raw.order.inject([]) do |ary, str|
43
127
  ary << order_from_string(str)
44
128
  end.compact
45
- when String
129
+ when ::String
46
130
  order_from_string(raw.order)
47
- when Symbol
131
+ when ::Symbol
48
132
  raw.order
49
133
  end
50
134
  end
51
135
 
52
136
  if raw.conditions.present?
53
- raw.conditions.each do |field, value|
54
- opts[field] = value if allowed_field?(field)
137
+ raw.conditions.each do |(field, value)|
138
+ opts[field.to_sym] = value if allowed_field?(field)
55
139
  end
56
140
  end
57
141
  opts
@@ -0,0 +1,25 @@
1
+ module DataMapper
2
+ module Resource
3
+ module VeneerInterface
4
+ class Property < Veneer::Base::Property
5
+ def normalize(type)
6
+ # Several DataMapper properties' types inherit from the Object class
7
+ # so DataMapper::Property::Object must be the last one i the case statement.
8
+ case type
9
+ when DataMapper::Property::Integer then Integer
10
+ when DataMapper::Property::String then String
11
+ when DataMapper::Property::Float then Float
12
+ when DataMapper::Property::Decimal then BigDecimal
13
+ when DataMapper::Property::DateTime then DateTime
14
+ when DataMapper::Property::Time then Time
15
+ when DataMapper::Property::Date then Date
16
+ when DataMapper::Property::Boolean then TrueClass
17
+ when DataMapper::Property::Discriminator then Class
18
+ when DataMapper::Property::Object then Object
19
+ else type
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,4 @@
1
+ require 'veneer/adapters/mongomapper/property'
1
2
  require 'veneer/adapters/mongomapper/class_wrapper'
2
3
  require 'veneer/adapters/mongomapper/instance_wrapper'
3
4
 
@@ -2,14 +2,82 @@ module MongoMapper
2
2
  module Document
3
3
  module VeneerInterface
4
4
  class ClassWrapper < Veneer::Base::ClassWrapper
5
+ delegate :validators_on, :to => :klass
6
+
7
+ PRIMARY_KEYS = [:_id]
8
+
9
+ def self.model_classes
10
+ ::MongoMapper::Document.descendants
11
+ end
12
+
5
13
  def new(opts = {})
6
14
  ::Kernel::Veneer(klass.new(opts))
7
15
  end
8
16
 
17
+ def collection_associations
18
+ @collection_associations ||= begin
19
+ klass.associations.inject([]) do |ary, (name, assoc)|
20
+ if assoc.is_a? ::MongoMapper::Plugins::Associations::ManyAssociation
21
+ ary << {
22
+ :name => name,
23
+ :class => assoc.class_name.constantize
24
+ }
25
+ end
26
+ ary
27
+ end
28
+ end
29
+ end
30
+
31
+ def member_associations
32
+ @member_associations ||= begin
33
+ types = [::MongoMapper::Plugins::Associations::BelongsToAssociation,
34
+ ::MongoMapper::Plugins::Associations::ManyAssociation]
35
+ klass.associations.inject([]) do |ary, (name, assoc)|
36
+ if types.include?(assoc.class)
37
+ ary << {
38
+ :name => name,
39
+ :class => assoc.class_name.constantize
40
+ }
41
+ end
42
+ ary
43
+ end
44
+ end
45
+ end
46
+
47
+ def properties
48
+ @properties ||= begin
49
+ klass.keys.map do |property|
50
+ property = property[1]
51
+ name = property.name.to_sym
52
+ ::MongoMapper::Document::VeneerInterface::Property.new(
53
+ self,
54
+ {
55
+ :name => name,
56
+ :type => property.type,
57
+ :constraints => {
58
+ :length => property.options[:length],
59
+ :nullable? => nil,
60
+ :precision => nil,
61
+ },
62
+ :primary => primary_keys.include?(name),
63
+ }
64
+ )
65
+ end
66
+ end
67
+ end
68
+
69
+ def primary_keys
70
+ ::MongoMapper::Document::VeneerInterface::ClassWrapper::PRIMARY_KEYS
71
+ end
72
+
9
73
  def destroy_all
10
74
  klass.destroy_all
11
75
  end
12
76
 
77
+ def count(opts={})
78
+ opts[:conditions].nil? ? klass.count : klass.count(opts[:conditions])
79
+ end
80
+
13
81
  def find_first(opts)
14
82
  klass.first(opts)
15
83
  end
@@ -0,0 +1,16 @@
1
+ module MongoMapper
2
+ module Document
3
+ module VeneerInterface
4
+ class Property < Veneer::Base::Property
5
+ def normalize(type)
6
+ case type
7
+ when MongoMapper::Extensions::ObjectId then String
8
+ when MongoMapper::Extensions::Boolean then TrueClass
9
+ when MongoMapper::Extensions::Binary then StringIO
10
+ else type
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -4,11 +4,17 @@ module Veneer
4
4
  # new
5
5
  # find_many
6
6
  # destroy_all
7
+ # model_classes
8
+ #
7
9
  #
8
10
  # Optional Methods
9
11
  # find_first
10
12
  # create!
11
13
  # create
14
+ # count
15
+ # sum
16
+ # max
17
+ # min
12
18
  #
13
19
  class ClassWrapper < BasicObject
14
20
  attr_reader :klass, :opts
@@ -16,6 +22,45 @@ module Veneer
16
22
  @klass, @opts = klass, opts
17
23
  end
18
24
 
25
+ def self.inherited(klass)
26
+ subclasses << klass
27
+ end
28
+
29
+ def self.subclasses
30
+ @subclasses ||= []
31
+ end
32
+
33
+ def self.model_classes
34
+ raise ::Veneer::Errors::NotImplemented
35
+ end
36
+
37
+ # Provides an array of associations of the format
38
+ # [
39
+ # {
40
+ # :name => :association_name,
41
+ # :class_name => 'TheClass'
42
+ # }
43
+ # ]
44
+ #
45
+ # The collection associations maps has_many, has n, embeds_many and the like.
46
+ # @api public
47
+ def collection_associations
48
+ []
49
+ end
50
+
51
+ # Provides an array of association for belongs_to type associaions
52
+ # of the format:
53
+ #
54
+ # [
55
+ # {
56
+ # :name => :assocaition_name,
57
+ # :class_name => 'TheClass'
58
+ # }
59
+ # ]
60
+ def member_associations
61
+ []
62
+ end
63
+
19
64
  # Create an instance of the object.
20
65
  # That is, instantiate and persist it in one step.
21
66
  # Raise an error if the object is not persisted
@@ -50,7 +95,7 @@ module Veneer
50
95
  # @api public
51
96
  # @see Veneer::Base::ClassWrapper.all
52
97
  def first(opts={})
53
- ::Kernel.Veneer(find_first(Hashie::Mash.new(opts)))
98
+ ::Kernel.Veneer(find_first(::Hashie::Mash.new(opts)))
54
99
  end
55
100
 
56
101
  # Provides an interface to query the objects
@@ -64,7 +109,58 @@ module Veneer
64
109
  #
65
110
  # @api public
66
111
  def all(opts={})
67
- find_many(Hashie::Mash.new(opts)).map{|element| ::Kernel.Veneer(element)}
112
+ find_many(::Hashie::Mash.new(opts))
113
+ end
114
+
115
+ # Obtains a count of all matching records
116
+ # Takes the same options as all
117
+ #
118
+ # Adapter implementers should overwrite with a more
119
+ # efficient implementation
120
+ # @see all
121
+ def count(opts={})
122
+ all(opts).size
123
+ end
124
+
125
+ # Obtains a sum of all matching records for the given field
126
+ # Takes the same options as all
127
+ #
128
+ # Adapter implementers should overwrite with a more
129
+ # efficient implementation
130
+ # @see all
131
+ def sum(field, opts={})
132
+ opts = ::Hashie::Mash.new(opts)
133
+ all(opts).inject(0){|sum, item| (item.send(field) || 0) + sum }
134
+ end
135
+
136
+ # Obtains the minimum value of the given field of all matching records
137
+ # Takes the same options as all
138
+ #
139
+ # Adapter implementers should overwrite with a more
140
+ # efficient implementation
141
+ # @see all
142
+ def min(field, opts={})
143
+ opts = ::Hashie::Mash.new(opts)
144
+ all(opts).inject(nil) do |min, item|
145
+ val = item.send(field)
146
+ min = val if !val.nil? && (min.nil? || val < min)
147
+ min
148
+ end
149
+ end
150
+
151
+ # Obtains the maximum value of the given field of all matching records
152
+ # Takes the same options as all
153
+ #
154
+ # Adapter implementers should overwrite with a more
155
+ # efficient implementation
156
+ # @see all
157
+ def max(field, opts={})
158
+ opts = ::Hashie::Mash.new(opts)
159
+ all(opts).inject(nil) do |max, item|
160
+ val = item.send(field)
161
+ max = val if !val.nil? && (max.nil? || val > max)
162
+ max
163
+ end
68
164
  end
69
165
 
70
166
  # Should return an array or array like structure with elements matching the provided conditions hash
@@ -74,15 +170,22 @@ module Veneer
74
170
  ::Kernel.raise Errors::NotImplemented
75
171
  end
76
172
 
173
+ # Should return an array of primary keys
174
+ # @api public
175
+ def primary_keys
176
+ ::Kernel.raise Errors::NotImplemented
177
+ end
178
+
179
+ # Should return an array of ActiveModel::Validator class instances for given property name
180
+ # @api public
181
+ def validators_on(name)
182
+ ::Kernel.raise Errors::NotImplemented
183
+ end
184
+
77
185
  def find_first(opts)
78
186
  opts[:limit] = 1
79
187
  find_many(opts).first
80
188
  end
81
-
82
- private
83
- def method_missing(meth, *args, &blk)
84
- @klass.__send__(meth, *args, &blk)
85
- end
86
189
  end
87
190
  end
88
191
  end