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
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ Gemfile.lock
data/Gemfile CHANGED
@@ -1,4 +1,15 @@
1
- #source "http://rubygems.org"
1
+ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in ..gemspec
4
4
  gemspec
5
+
6
+ gem 'rake'
7
+ gem 'shoulda'
8
+ gem 'activerecord'
9
+ gem 'mongo_mapper'
10
+ gem 'dm-core'
11
+ gem 'dm-migrations'
12
+ gem 'dm-aggregates'
13
+ gem 'dm-sqlite-adapter'
14
+ gem 'sqlite3-ruby'
15
+ gem 'mysql2'
data/README.md CHANGED
@@ -7,119 +7,87 @@ Veneer aims to provide plugin, engine, and library authors with a consistant int
7
7
  It differs from ActiveModel in that it doesn't provide any validation support, serialization, callbacks, state machine or anything like that. Veneer is intended to work _with_ ActiveModel to provide a data store independant interface to create reusable code.
8
8
 
9
9
  Veneer instead aims to provide a simple interface for
10
+
10
11
  * querying
11
12
  * creating
12
13
  * saving
13
14
  * deleting
14
15
  * basic lifecycle hooks
15
- ** before validation
16
- ** before save
17
- ** after save
18
- ** before destroy
19
- ** after destroy
16
+ * before validation
17
+ * before save
18
+ * after save
19
+ * before destroy
20
+ * after destroy
20
21
 
21
22
  There is no interface for validations, since this interface is handled as part of ActiveModel
22
23
 
23
- ## Creating and Saving an instance
24
-
25
- To create an instance of an object wrap the class in a vaneer call and create or get a new instance.
26
-
27
- <pre><code>obj = Veneer(MyModel).new(:some => "hash", :of => "attributes")
28
- obj.save
24
+ ## Quick Intro
29
25
 
30
- # OR
31
-
32
- obj = Veneer(MyModel).create(:some => "hash", :of => "attributes")
33
- </code></pre>
26
+ <pre><code># Assume User class model
34
27
 
35
- There are also version that will raise an exception if it could not save
28
+ # Create new items
29
+ Veneer(User).new(:some => "parameters") # instantiate only
30
+ Veneer(User).create(:some => "parameters") # instantiate and persist
31
+ Veneer(User).create!(:some => "parameters") # create and raise on failure
36
32
 
37
- <pre><code>obj = Veneer(MyModel).new(:some => "attribute").save!
38
-
39
- # OR
40
-
41
- obj = Veneer(MyModel).create!(:some => "attribute")
42
- </code></pre>
33
+ # Query
43
34
 
44
- h2. Deleting
35
+ # The supported options are:
36
+ #
37
+ # * :limit
38
+ # * :offset
39
+ # * :order
40
+ # * :conditions (hash with keys => <Array|Primitive|Range>
45
41
 
46
- You can delete all the instances of a model or a single instance.
42
+ # First can be used in place of all to get a single item
43
+ # All options can be used together
47
44
 
48
- <pre><code>Veneer(MyModel).destroy\_all # delete all instances
49
-
50
- @some\_veneer\_model.destroy
51
- </code></pre>
52
-
53
- ### Updating
54
-
55
- To update an instance:
56
-
57
- <pre><code>@some\_veneer\_instance.update(:with => "attributes")
58
-
59
- OR
60
-
61
- @some\_veneer\_instance.update!(:with => "attributes") # raise on error
62
- </code></pre>
45
+ Veneer(User).all(:limit => 3, :offset => 2)
46
+ Veneer(User).all(:order => ["name", "dob desc"])
47
+ Veneer(User).all(:conditions => {:some => "condition"})
63
48
 
64
- ## Queries
49
+ # Update object
50
+ user = Veneer(User).first(:conditions => {:id => params[:id]})
51
+ user.update(:some => "new values") # updates and persists changes
65
52
 
66
- Veneer lets you query models with a simple interface. Only simple queries are supported.
53
+ # Destroy object
54
+ user = Veneer(User).first(:conditions => {:id => params[:id]})
55
+ user.destroy
67
56
 
68
- You can query a single object, or multiple objects.
57
+ Veneer(User).destroy_all
69
58
 
70
- <pre><code>Veneer(MyModel).first(:conditions => {:name => "foo"})
71
-
72
- Veneer(MyModel).all(:conditions => {:age => 18}, :limit => 5)
73
- </code></pre>
74
-
75
- The supported options are:
76
-
77
- * :limit
78
- * :offset
79
- * :order
80
- * :conditions
81
-
82
- ## Limit & Offset
83
-
84
- The :limit option will limit the number of returned results.
85
-
86
- The :offset option, when set to an integer x will begin the results at the xth result.
87
-
88
- ## Order
89
-
90
- Ordering should be set as an array. By default, the order is decending.
91
-
92
- The format of the array is:
93
-
94
- <pre><code>["<field> <directon>", "<field>"]
95
-
96
- ### Example
97
- Veneer(MyModel).all(:order => [:name, "age desc")
98
- </code></pre>
59
+ # Save
60
+ user.name = "bar"
61
+ user.save
62
+ # OR
63
+ user.save! # raise on fail
99
64
 
100
- ## Conditions
65
+ # Find associations
66
+ Veneer(User).collection_associations # grabs things like has_many and has n
67
+ Veneer(User).member_associations # grabs singular associations like belongs_to and has_one
101
68
 
102
- Conditions are a very simple set of conditions on the fields of the model. The for of the conditions are:
69
+ # ====> results in
70
+ [ { :name => "asociaiton_name", :class => AssociationClass }]
103
71
 
104
- <pre><code>:conditions => {:field => value}</code></pre>
72
+ # Find property names (NOT YET IMPLEMENTED. PATCHES WELCOME)
105
73
 
106
- <pre><code>
107
- Veneer(MyModel).all(:conditions => {:name => ["fred", "barney"], :age => (18..18)})
108
- Veneer(MyModel).first(:conditions => {:name => "fred"})
109
- </code></pre>
74
+ Veneer(User).property_names # => [:name, :login, ...]
110
75
 
111
- Condition fields should be ready to accept:
76
+ # Discover loaded model classes
77
+ Veneer.model_classes
112
78
 
113
- * String
114
- * Range (Between clauses)
115
- * Array (IN clauses)
116
- * nil
79
+ # Aggregate methods
80
+ Veneer(User).count
81
+ Veneer(User).count(conditional_options_same_as_all)
117
82
 
118
- The results of a query are all wrapped as Veneer instances.
83
+ Veneer(User).sum(:column_name)
84
+ Veneer(User).sum(:column_name, conditional_options_same_as_all)
119
85
 
120
- ## All Together
86
+ Veneer(User).min(:column_name)
87
+ Veneer(User).min(:column_name, conditional_options_same_as_all)
121
88
 
122
- <pre><code>Veneer(MyModel).all(:order => [:name, "age asc"], :limit => 5, :offset => 15, :conditions => {:name => "betty", :age => (0..18)})
89
+ Veneer(User).max(:column_name)
90
+ Veneer(User).max(:column_name, conditional_options_same_as_all)
123
91
  </code></pre>
124
92
 
125
93
  ## Object methods
@@ -128,7 +96,7 @@ All methods that aren't found on the adapter, are passed through to the instance
128
96
 
129
97
  ## Current Support
130
98
 
131
- Veneer currently has built in support for ActiveRecord and DataMapper.
99
+ Veneer currently has built in support for ActiveRecord and DataMapper and MongoMapper.
132
100
 
133
101
  Veneer works on a VeneerInterface inner module though so you can easily impelment your adapter without requiring it to be in the veneer repo (although pull requests are welcome) (see Building Your Adapter)
134
102
 
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ desc "Run test suite."
7
7
  Rake::TestTask.new("test"){ |test|
8
8
  test.pattern = "test/*_test.rb"
9
9
  test.verbose = true
10
- test.warning = true
10
+ test.warning = false
11
11
  test.libs << "test"
12
12
  test.test_files = FileList["test/test*.rb", "test/**/test*.rb"]
13
13
  }
@@ -24,11 +24,16 @@ module Veneer
24
24
  autoload :Errors, 'veneer/errors'
25
25
  autoload :Proxy, 'veneer/proxy'
26
26
  autoload :Conditional, 'veneer/base/conditional'
27
- autoload :Lint, 'veneer/lint'
27
+
28
28
 
29
29
  module Base
30
30
  autoload :ClassWrapper, 'veneer/base/class_wrapper'
31
31
  autoload :InstanceWrapper, 'veneer/base/instance_wrapper'
32
+ autoload :Property, 'veneer/base/property'
33
+ end
34
+
35
+ def self.model_classes
36
+ Base::ClassWrapper.subclasses.map{|adapter| adapter.model_classes }.flatten.compact
32
37
  end
33
38
  end
34
39
 
@@ -1,4 +1,6 @@
1
1
  if defined?(ActiveRecord::Base)
2
+ require 'veneer/adapters/activerecord/property'
3
+
2
4
  if ActiveRecord::VERSION::MAJOR < 3
3
5
  require 'veneer/adapters/activerecord/pre_3.0_class_wrapper'
4
6
  require 'veneer/adapters/activerecord/pre_3.0_instance_wrapper'
@@ -2,10 +2,84 @@ module ActiveRecord
2
2
  class Base
3
3
  module VeneerInterface
4
4
  class ClassWrapper < Veneer::Base::ClassWrapper
5
+ delegate :validators_on, :to => :klass
6
+
7
+ def self.except_classes
8
+ @@except_classes ||= [
9
+ "CGI::Session::ActiveRecordStore::Session",
10
+ "ActiveRecord::SessionStore::Session"
11
+ ]
12
+ end
13
+
14
+ def self.model_classes
15
+ klasses = ::ActiveRecord::Base.descendants
16
+ klasses.select do |klass|
17
+ !klass.abstract_class? && !except_classes.include?(klass.name)
18
+ end
19
+ end
20
+
5
21
  def new(opts = {})
6
22
  ::Kernel::Veneer(klass.new(opts))
7
23
  end
8
24
 
25
+ def collection_associations
26
+ @collection_associations ||= begin
27
+ associations = []
28
+ [:has_many, :has_and_belongs_to_many].each do |macro|
29
+ associations += klass.reflect_on_all_associations(macro)
30
+ end
31
+ associations.inject([]) do |ary, reflection|
32
+ ary << {
33
+ :name => reflection.name,
34
+ :class => reflection.class_name.constantize
35
+ }
36
+ ary
37
+ end
38
+ end
39
+ end
40
+
41
+ def member_associations
42
+ @member_associations ||= begin
43
+ associations = []
44
+ [:belongs_to, :has_one].each do |macro|
45
+ associations += klass.reflect_on_all_associations(macro)
46
+ end
47
+ associations.inject([]) do |ary, reflection|
48
+ ary << {
49
+ :name => reflection.name,
50
+ :class => reflection.class_name.constantize
51
+ }
52
+ ary
53
+ end
54
+ end
55
+ end
56
+
57
+ def properties
58
+ @properties ||= begin
59
+ klass.columns.map do |property|
60
+ name = property.name.to_sym
61
+ ::ActiveRecord::Base::VeneerInterface::Property.new(
62
+ self,
63
+ {
64
+ :name => name,
65
+ :type => property.type,
66
+ :constraints => {
67
+ :length => property.limit,
68
+ :nullable? => property.null,
69
+ :precision => property.precision,
70
+ :scale => property.scale,
71
+ },
72
+ :primary => primary_keys.include?(name),
73
+ }
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ def primary_keys
80
+ @primary_keys ||= [klass.primary_key.to_sym]
81
+ end
82
+
9
83
  def destroy_all
10
84
  klass.destroy_all
11
85
  end
@@ -15,16 +89,36 @@ module ActiveRecord
15
89
  end
16
90
 
17
91
  def find_many(opts)
18
- build_query(opts)
92
+ build_query(opts).all
93
+ end
94
+
95
+ def count(opts ={})
96
+ opts = ::Hashie::Mash.new(opts)
97
+ build_query(opts).count
98
+ end
99
+
100
+ def sum(field, opts={})
101
+ opts = ::Hashie::Mash.new(opts)
102
+ build_query(opts).sum(field)
103
+ end
104
+
105
+ def max(field, opts={})
106
+ opts = ::Hashie::Mash.new(opts)
107
+ build_query(opts).maximum(field)
108
+ end
109
+
110
+ def min(field, opts={})
111
+ opts = ::Hashie::Mash.new(opts)
112
+ build_query(opts).minimum(field)
19
113
  end
20
114
 
21
115
  private
22
116
  def build_query(opts)
23
117
  query = klass
24
- query.where(opts.conditions) if opts.conditions.present?
25
- query.limit(opts.limit) if opts.limit?
26
- query.offset(opts.offset) if opts.offset?
27
- query.order(opts.order) if opts.order?
118
+ query = query.where(opts.conditions.to_hash) if opts.conditions.present?
119
+ query = query.limit(opts.limit.to_i) if opts.limit?
120
+ query = query.offset(opts.offset.to_i) if opts.offset?
121
+ query = query.order(opts.order) if opts.order?
28
122
  query
29
123
  end
30
124
  end # ClassWrapper
@@ -7,9 +7,11 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  def save!
10
- instance.save ?
11
- true :
10
+ if instance.save
11
+ true
12
+ else
12
13
  raise ::Veneer::Errors::NotSaved
14
+ end
13
15
  end
14
16
  end # InstanceWrapper
15
17
  end # VeneerInterface
@@ -2,10 +2,56 @@ module ActiveRecord
2
2
  class Base
3
3
  module VeneerInterface
4
4
  class ClassWrapper < Veneer::Base::ClassWrapper
5
+ def self.except_classes
6
+ @@except_classes ||= [
7
+ "CGI::Session::ActiveRecordStore::Session",
8
+ "ActiveRecord::SessionStore::Session"
9
+ ]
10
+ end
11
+
12
+ def self.model_classes
13
+ klasses = ::ActiveRecord::Base.__send__(:subclasses)
14
+ klasses.select do |klass|
15
+ !klass.abstract_class? && !except_classes.include?(klass.name)
16
+ end
17
+ end
18
+
5
19
  def new(opts = {})
6
20
  ::Kernel::Veneer(klass.new(opts))
7
21
  end
8
22
 
23
+ def collection_associations
24
+ @collection_associations ||= begin
25
+ associations = []
26
+ [:has_many, :has_and_belongs_to_many].each do |macro|
27
+ associations += klass.reflect_on_all_associations(macro)
28
+ end
29
+ associations.inject([]) do |ary, reflection|
30
+ ary << {
31
+ :name => reflection.name,
32
+ :class => reflection.class_name.constantize
33
+ }
34
+ ary
35
+ end
36
+ end
37
+ end
38
+
39
+ def member_associations
40
+ @member_associations ||= begin
41
+ associations = []
42
+ [:belongs_to, :has_one].each do |macro|
43
+ associations += klass.reflect_on_all_associations(macro)
44
+ end
45
+ associations.inject([]) do |ary, reflection|
46
+ ary << {
47
+ :name => reflection.name,
48
+ :class => reflection.class_name.constantize
49
+ }
50
+ ary
51
+ end
52
+ end
53
+ end
54
+
9
55
  def destroy_all
10
56
  klass.destroy_all
11
57
  end
@@ -17,8 +63,23 @@ module ActiveRecord
17
63
  def find_many(opts)
18
64
  klass.find(:all,opts.to_hash.symbolize_keys)
19
65
  end
20
- end # ClassWrapper
21
66
 
67
+ def count(opts={})
68
+ if opts[:conditions]
69
+ klass.count(:conditions => opts[:conditions])
70
+ else
71
+ klass.count
72
+ end
73
+ end
74
+
75
+ def sum(field, opts={})
76
+ if opts[:conditions]
77
+ klass.sum(field, :conditions => opts[:conditions])
78
+ else
79
+ klass.sum(field)
80
+ end
81
+ end
82
+ end # ClassWrapper
22
83
  end
23
84
  end
24
85
  end