veneer 0.2.0 → 0.4.0

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