public
Description: open-source e-commerce built on merb
Clone URL: git://github.com/myabc/merb_mart.git
Click here to lend your support to: merb_mart and make a donation at www.pledgie.com !
Model refactorings and further expansion of specs

* Changed associations (except for many_to_many)
  to use new shorthand syntax, for sake of clarity.
* Changed "validate" reference to "dm-validations"
  to reflect gem renaming in dm-more.
* Added basic specs for most associations.
* Updated some :class_name options for associations
  (still needs to be gone through and tested more
  thoroughly).
* Added missing class and instance methods in
  Mart::AbstractUpload, Mart::Accounts
* Added zipcode, province aliases in Address class
  (and relevant specs)
* Added provinces alias in Country class (and
  relevant specs).
* Added validations into Country class, made name
  property unique.
* Add spec for State class.
* Pushed down image-related properties/constants,
  out of AbstractUpload and into Mart::Image class.
* Add example workaround for current lack of DM
  many-to-many support in Right class.
* Fixed finder ordering syntax in Tag.
* Re-enabled :save callback in User class.
* Made some Mart::Order specs pending, instead of
  letting them fail.
myabc (author)
Wed May 07 00:28:03 -0700 2008
commit  57a29efea95e57734df9efc14ea929d64e0bcfd6
tree    7227de35787caacdeb32add12b23d59d267c10fc
parent  cfec5c5276d1323b1cc6d81f712b9a2dbc547355
...
1
2
3
4
5
...
16
17
18
19
20
 
 
21
22
23
24
25
 
 
26
27
28
29
 
 
 
 
 
 
30
31
32
...
 
 
1
2
3
...
14
15
16
 
 
17
18
19
20
21
 
 
22
23
24
25
26
 
27
28
29
30
31
32
33
34
35
0
@@ -1,5 +1,3 @@
0
-require 'validate'
0
-
0
 class Address
0
 
0
   include DataMapper::Resource
0
@@ -16,17 +14,22 @@ class Address
0
   property :state_id, Fixnum # foreign-key
0
   property :country_code, String # foreign-key
0
 
0
- many_to_one :state
0
- many_to_one :country
0
+ belongs_to :state
0
+ belongs_to :country
0
 
0
   validates_presence_of :first_name
0
   validates_presence_of :last_name
0
- validates_presence_of :address
0
- validates_presence_of :zip
0
+ validates_presence_of :address1
0
+ validates_presence_of :postal_code
0
 
0
   validates_length_of :first_name, :maximum => 50
0
   validates_length_of :last_name, :maximum => 50
0
- validates_length_of :address, :maximum => 255
0
+ validates_length_of :address1, :maximum => 255
0
+
0
+ alias :zipcode :postal_code
0
+ alias :zipcode= :postal_code=
0
+ alias :province :state
0
+ alias :province= :state=
0
 
0
   def name
0
     "#{self.first_name} #{self.last_name}"
...
8
9
10
11
 
 
 
 
 
 
 
 
12
13
14
...
8
9
10
 
11
12
13
14
15
16
17
18
19
20
21
0
@@ -8,6 +8,13 @@ class Country
0
   include DataMapper::Validate
0
   
0
   property :code, String, :serial => true, :length => 2 # ISO 3166-1 alpha-2
0
- property :name, String, :length => 100, :nullable => false
0
+ property :name, String, :length => 100, :nullable => false, :unique => true
0
+
0
+ has n, :states
0
+
0
+ validates_presence_of :name
0
+ validates_uniqueness_of :name
0
+
0
+ alias :provinces :states
0
 
0
 end
0
\ No newline at end of file
...
2
3
4
5
6
 
7
8
9
10
11
12
13
14
...
16
17
18
 
 
 
 
 
 
 
 
 
 
 
 
19
20
...
2
3
4
 
 
5
6
7
8
 
 
9
10
11
...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0
@@ -2,13 +2,10 @@ module Mart
0
   class AbstractUpload
0
   
0
     include DataMapper::Resource
0
-
0
- IMAGE_EXTENSIONS = ['gif', 'jpg', 'jpeg', 'png', 'bmp']
0
+ include DataMapper::Validate
0
 
0
     property :id, Fixnum, :serial => true
0
     property :filename, String
0
- property :width, Fixnum, :default => 0, :nullable => false
0
- property :height, Fixnum, :default => 0, :nullable => false
0
     property :created_on, DateTime
0
     #property :parent_id
0
     property :content_type, String
0
@@ -16,5 +13,17 @@ module Mart
0
     property :size, Fixnum
0
     property :type, Class # single-table inheritance
0
 
0
+ def filename_base
0
+ filename.split('.').first
0
+ end
0
+
0
+ def extension
0
+ filename.split('.').last
0
+ end
0
+
0
+ def relative_path
0
+ filename # TODO
0
+ end
0
+
0
   end
0
 end
...
1
 
2
3
4
...
12
13
14
15
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
18
19
...
 
1
2
3
4
...
12
13
14
 
 
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
0
@@ -1,4 +1,4 @@
0
-require 'validate'
0
+require 'dm-validations'
0
 
0
 module Mart
0
   module Accounts
0
@@ -12,8 +12,21 @@ module Mart
0
       property :order_id, Fixnum # foreign-key
0
       property :customer_id, Fixnum # foreign-key
0
     
0
- one_to_one :order
0
- many_to_one :customer #, :accessor => :protected
0
+ has 1, :order
0
+ belongs_to :customer #, :accessor => :protected
0
+
0
+ def self.months
0
+ (1..12).to_a
0
+ end
0
+
0
+ def self.years
0
+ year = Date.today.year
0
+ years = Array.new
0
+ (0..9).each do |n|
0
+ years << year + n
0
+ end
0
+ years
0
+ end
0
 
0
     end
0
   end
...
1
 
2
3
4
...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
 
 
 
 
31
32
33
 
 
 
 
 
34
35
36
37
...
 
1
2
3
4
...
14
15
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
18
19
20
21
22
23
 
24
25
26
27
28
29
30
31
32
0
@@ -1,4 +1,4 @@
0
-require 'validate'
0
+require 'dm-validations'
0
 
0
 module Mart
0
   class Customer
0
@@ -14,23 +14,18 @@ module Mart
0
     property :first_name, String, :length => 50, :nullable => false
0
     property :last_name, String, :length => 50, :nullable => false
0
 
0
- one_to_many :orders #,
0
- #:dependent => :nullify,
0
- #:order => "created_on DESC"
0
- one_to_many :last_order#,
0
- #:class_name => "Order",
0
- #:order => "created_on DESC"
0
- one_to_many :order_addresses #, :dependent => :destroy
0
- one_to_many :order_accounts #, :dependent => :destroy
0
-
0
- one_to_many :wishlist_items #,
0
- #:dependent => :destroy,
0
- #:order => "created_on DESC"
0
- one_to_many :items #, :through => :wishlist_items,
0
- #:order => "wishlist_items.created_on DESC"
0
+ has n, :orders
0
+ has n, :addresses
0
+ has n, :accounts
0
+ has n, :wishlist_items
0
+ # has n, :items, :through => :wishlist_items ## FIXME
0
 
0
     validates_presence_of :email_address
0
- #validates_length_of :email_address, :maximum => 255
0
+ validates_length_of :email_address, :maximum => 255
0
+
0
+ def last_order
0
+ # TODO
0
+ end
0
 
0
   end
0
 end
0
\ No newline at end of file
...
9
10
11
12
13
14
15
 
 
 
 
16
17
18
19
...
9
10
11
 
 
 
 
12
13
14
15
16
17
18
19
0
@@ -9,10 +9,10 @@ module Mart
0
       property :created_on, DateTime
0
       property :customer_id, Fixnum # foreign-key
0
       property :store_item_id, Fixnum # foreign-key
0
-
0
- many_to_one :customer
0
- many_to_one :store_item
0
-
0
+
0
+ belongs_to :customer
0
+ belongs_to :store_item
0
+
0
     end
0
   end
0
 end
0
\ No newline at end of file
...
1
2
3
4
5
 
 
 
 
 
 
 
6
7
8
9
...
1
2
3
 
 
4
5
6
7
8
9
10
11
12
13
14
0
@@ -1,8 +1,13 @@
0
 module Mart
0
   class Image < AbstractUpload
0
 
0
- one_to_many :product_images
0
- one_to_many :products #, :through => :product_images
0
+ IMAGE_EXTENSIONS = ['gif', 'jpg', 'jpeg', 'png', 'bmp']
0
+
0
+ property :width, Fixnum, :default => 0, :nullable => false
0
+ property :height, Fixnum, :default => 0, :nullable => false
0
+
0
+ has n, :product_images
0
+ #has n, :products, :through => :product_images ## FIXME
0
 
0
   end
0
 end
0
\ No newline at end of file
...
1
 
2
3
4
...
23
24
25
26
 
 
 
27
28
29
30
31
32
33
34
35
 
 
 
 
 
36
37
38
...
 
1
2
3
4
...
23
24
25
 
26
27
28
29
 
 
 
 
 
 
 
 
30
31
32
33
34
35
36
37
0
@@ -1,4 +1,4 @@
0
-require 'validate'
0
+require 'dm-validations'
0
 
0
 module Mart
0
   class Order
0
@@ -23,16 +23,15 @@ module Mart
0
     property :status_code_id,Fixnum # foreign-key
0
     property :promotion_id, Fixnum # foreign-key
0
   
0
- one_to_many :line_items, :class_name => 'Orders::LineItem' #, :dependent => :destroy
0
+ has 1..n, :line_items, :class_name => 'Orders::LineItem' #, :dependent => :destroy
0
+ has 1, :billing_address, :class_name => 'Address' #, :foreign_key => 'billing_address_id'
0
+ has 1, :shipping_address, :class_name => 'Address' #, :foreign_key => 'shipping_address_id'
0
   
0
- one_to_one :billing_address, :class_name => 'Address' #, :foreign_key => 'billing_address_id'
0
- one_to_one :shipping_address, :class_name => 'Address' #, :foreign_key => 'shipping_address_id'
0
-
0
- many_to_one :account
0
- many_to_one :customer
0
- many_to_one :order_shipping_type
0
- many_to_one :status_code, :class_name => 'Orders::StatusCode'
0
- many_to_one :promotion
0
+ belongs_to :account
0
+ belongs_to :customer
0
+ belongs_to :order_shipping_type
0
+ belongs_to :status_code, :class_name => 'Orders::StatusCode'
0
+ belongs_to :promotion
0
   
0
     attr_accessor :promotion_code
0
   
...
11
12
13
14
15
 
 
16
17
18
...
11
12
13
 
 
14
15
16
17
18
0
@@ -11,8 +11,8 @@ module Mart
0
       property :product_id, Fixnum ## FIXME: should not be both product_id + store_item_id
0
       property :store_item_id, Fixnum # foreign-key
0
   
0
- many_to_one :product ## FIXME: should not be both product_id + store_item_id
0
- many_to_one :store_item
0
+ belongs_to :product ## FIXME: should not be both product_id + store_item_id
0
+ belongs_to :store_item
0
   
0
     end
0
   end
...
7
8
9
10
 
11
12
13
...
7
8
9
 
10
11
12
13
0
@@ -7,7 +7,7 @@ module Mart
0
       property :id, Fixnum, :serial => true
0
       property :name, String, :length => 30, :nullable => false
0
   
0
- one_to_many :orders
0
+ has n, :orders
0
   
0
     end
0
   end
...
12
13
14
15
16
 
 
17
18
19
...
12
13
14
 
 
15
16
17
18
19
0
@@ -12,8 +12,8 @@ module Mart
0
       property :is_domestic, TrueClass, :default => true, :nullable => false
0
       property :price, Float, :default => 0.0, :nullable => false
0
   
0
- one_to_many :orders
0
- one_to_many :weights, :class_name => 'OrderShippingWeight' #, :dependent => :destroy
0
+ has n, :orders
0
+ has n, :weights, :class_name => 'OrderShippingWeight' #, :dependent => :destroy
0
   
0
       def self.get_domestic
0
         all(:is_domestic => true, :order => "price ASC")
...
10
11
12
13
 
14
15
16
...
10
11
12
 
13
14
15
16
0
@@ -10,7 +10,7 @@ module Mart
0
       property :price, Float, :default => 0.0, :nullable => false
0
       property :order_shipping_type_id, Fixnum # foreign-key
0
 
0
- many_to_one :order_shipping_type
0
+ belongs_to :type
0
   
0
     end
0
   end
...
1
2
 
3
4
5
...
21
22
23
24
25
26
27
 
 
 
 
28
29
 
30
31
32
33
...
1
 
2
3
4
5
...
21
22
23
 
 
 
 
24
25
26
27
28
 
29
30
31
32
33
0
@@ -1,5 +1,5 @@
0
 require "date"
0
-require "validate"
0
+require "dm-validations"
0
 
0
 module Mart
0
   module Store
0
@@ -21,12 +21,12 @@ module Mart
0
       property :weight, Float, :default => 0.0, :nullable => false
0
       property :is_discontinued, TrueClass, :default => false, :nullable => false
0
       property :type, Class # single-table inheritance
0
-
0
- one_to_many :order_line_items
0
- one_to_many :wishlist_items #, :dependent => :destroy
0
-
0
+
0
+ has n, :line_items, :class_name => "Mart::Orders::LineItem"
0
+ has n, :wishlist_items, :class_name => "Mart::Customers::WishlistItem" #, :dependent => :destroy
0
+
0
       validates_presence_of :name, :code
0
-
0
+
0
     end
0
   end
0
 end
0
\ No newline at end of file
...
2
3
4
5
6
7
 
 
 
8
9
10
...
2
3
4
 
 
 
5
6
7
8
9
10
0
@@ -2,9 +2,9 @@ module Mart
0
   module Store
0
     class Product < AbstractItem
0
 
0
- one_to_many :product_images
0
- one_to_many :variations #, :dependent => :destroy, :order => 'name ASC'
0
- one_to_many :images #,
0
+ has n, :product_images
0
+ has n, :variations #, :dependent => :destroy, :order => 'name ASC'
0
+ #has n, :images #, ## FIXME
0
         #:through => :product_images,
0
         #:order => "-product_images.rank DESC",
0
         #:dependent => :destroy
...
8
9
10
11
12
 
 
13
14
15
...
8
9
10
 
 
11
12
13
14
15
0
@@ -8,8 +8,8 @@ module Mart
0
       property :image_id, Fixnum # foreign-key
0
       property :rank, Fixnum
0
 
0
- many_to_one :product
0
- many_to_one :image
0
+ belongs_to :product
0
+ belongs_to :image
0
 
0
     end
0
   end
...
1
 
2
3
4
...
23
24
25
26
27
 
 
28
29
30
...
 
1
2
3
4
...
23
24
25
 
 
26
27
28
29
30
0
@@ -1,4 +1,4 @@
0
-require 'validate'
0
+require 'dm-validations'
0
 
0
 module Mart
0
   module Store
0
@@ -23,8 +23,8 @@ module Mart
0
       property :description, String, :nullable => false
0
       property :store_item_id, Fixnum # foreign-key
0
 
0
- one_to_many :orders
0
- many_to_one :store_item_id
0
+ has n, :orders
0
+ belongs_to :item, :class_name => "Mart::Store::AbstractItem"
0
   
0
       validates_presence_of :code, :discount_amount, :discount_type, :description
0
       validates_numericalnes_of :discount_amount
...
4
5
6
7
 
8
9
10
...
4
5
6
 
7
8
9
10
0
@@ -4,7 +4,7 @@ module Mart
0
   
0
       property :product_id, Fixnum # foreign-key
0
   
0
- many_to_one :product
0
+ belongs_to :product
0
 
0
     end
0
   end
...
7
8
9
10
 
 
 
 
 
11
 
 
 
 
 
 
 
 
 
12
13
...
7
8
9
 
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0
@@ -7,6 +7,19 @@ class Right
0
   property :controller, String
0
   property :actions, String
0
 
0
- many_to_many :roles
0
+ # many_to_many :roles
0
+ def roles
0
+ rels = RightRoles.all(:right_id => self.id).map {|r| r.role_id}
0
+ rels.empty? ? [] : Role.all(:id => rels)
0
+ end
0
 
0
+end
0
+
0
+# FIXME:
0
+# Hack! Work-around no DM many-to-many
0
+#
0
+class RightRoles
0
+ include DataMapper::Resource
0
+ property :right_id, Fixnum, :key => true
0
+ property :role_id, Fixnum, :key => true
0
 end
0
\ No newline at end of file
...
12
13
14
15
 
16
17
18
...
12
13
14
 
15
16
17
18
0
@@ -12,6 +12,6 @@ class State
0
   property :name, String, :length => 50, :nullable => false
0
   property :country_code, String # foreign-key
0
   
0
- many_to_one :country
0
+ belongs_to :country
0
   
0
 end
0
\ No newline at end of file
...
1
 
2
3
4
...
11
12
13
14
15
16
 
 
 
17
18
19
 
 
20
21
22
23
24
 
 
 
25
26
27
28
...
 
1
2
3
4
...
11
12
13
 
 
 
14
15
16
17
 
 
18
19
20
21
 
 
 
22
23
24
25
26
27
28
0
@@ -1,4 +1,4 @@
0
-require 'validate'
0
+require 'dm-validations'
0
 
0
 class Tag
0
   
0
@@ -11,17 +11,17 @@ class Tag
0
   property :rank, Fixnum
0
   property :parent_id, Fixnum
0
 
0
- many_to_many :products #, :join_table => 'products_tags'
0
- validates_presence_of :name
0
- #validates_uniqueness_of :name
0
+ #many_to_many :products #, :join_table => 'products_tags'
0
+ validates_presence_of :name
0
+ validates_uniqueness_of :rank
0
   
0
- def self.find_alpha
0
- all(:order => [ DataMapper::Query::Direction.new(:name, :asc) ])
0
+ def self.all_ordered
0
+ all(:order => [ :name.asc ])
0
   end
0
   
0
- def self.find_ordered_parents
0
- all(:conditions => [:parent_id => nil, :parent_id => 0],
0
- :order => [ DataMapper::Query::Direction.new(:rank, :desc) ]) # [:rank.desc]
0
+ def self.all_parents
0
+ all(:conditions => [:parent_id => 0], # parent_id => nil,
0
+ :order => [ :rank.desc ])
0
   end
0
   
0
 end
0
\ No newline at end of file
...
1
2
3
 
4
5
6
...
38
39
40
41
42
43
44
45
 
 
 
 
 
46
47
48
...
1
2
 
3
4
5
6
...
38
39
40
 
 
 
 
 
41
42
43
44
45
46
47
48
0
@@ -1,6 +1,6 @@
0
 require 'digest/sha1'
0
 require "date"
0
-require 'validate'
0
+require "dm-validations"
0
 begin
0
   require File.join(File.dirname(__FILE__), '..', '..', "lib", "authenticated_system", "authenticated_dependencies")
0
 rescue
0
@@ -38,11 +38,11 @@ class User
0
   validates_presence_of :password_confirmation, :if => proc {password_required?}
0
   validates_length_of :password, :within => 4..40, :if => proc {password_required?}
0
   validates_confirmation_of :password, :groups => :create
0
-
0
- # FIXME : fix callbacks
0
- #before_save :encrypt_password
0
- #before_create :make_activation_code
0
- #after_create :send_signup_notification
0
+
0
+ before :save, :encrypt_password
0
+ # FIXME
0
+ #before :create, :make_activation_code
0
+ #after :create, :send_signup_notification
0
   
0
   def login=(value)
0
     @login = value.downcase unless value.nil?
...
3
4
5
6
7
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
10
11
12
13
14
15
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
18
19
...
44
45
46
47
48
49
50
51
52
53
...
3
4
5
 
 
 
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
 
 
 
 
 
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
76
77
78
 
 
 
 
79
80
81
0
@@ -3,17 +3,49 @@ require File.join( File.dirname(__FILE__), "..", "spec_helper" )
0
 describe Address do
0
 
0
   describe "associations" do
0
- it "should belong to a country"
0
- it "should have one order"
0
- it "should belong to a customer (OrderUser)"
0
+ it "should belong to a country" do
0
+ address = Address.new
0
+ address.should respond_to(:country)
0
+ address.should respond_to(:country=)
0
+ end
0
+
0
+ it "should belong to a state" do
0
+ address = Address.new
0
+ address.should respond_to(:state)
0
+ address.should respond_to(:state=)
0
+ end
0
+
0
+ it "should belong to a province (alias for state)" do
0
+ #state = mock("state")
0
+ address = Address.new
0
+ address.should respond_to(:province)
0
+ address.should respond_to(:province=)
0
+ #address.province = state
0
+ end
0
   end
0
   
0
- it "should require a customer"
0
- it "should require a zip"
0
- it "should require a telephone"
0
- it "should require a first name"
0
- it "should require a last name"
0
- it "should require an address"
0
+ it "should require a zip, telephone, last name, first name and address" do
0
+ address = Address.new
0
+ address.valid?
0
+ address.errors.on(:first_name).should_not be_nil
0
+ address.errors.on(:last_name).should_not be_nil
0
+ address.errors.on(:address1).should_not be_nil
0
+ address.errors.on(:postal_code).should_not be_nil
0
+ address.first_name = "John"
0
+ address.last_name = "Doe"
0
+ address.address1 = "1012 E 87th St"
0
+ address.postal_code = "10021"
0
+ address.should be_valid
0
+ end
0
+
0
+ it "should provide the zipcode alias for postal code" do
0
+ address1 = Address.new
0
+ address1.postal_code = "91210"
0
+ address1.zipcode.should == "91210"
0
+ address2 = Address.new
0
+ address2.zipcode = "10004"
0
+ address2.postal_code.should == "10004"
0
+ end
0
   
0
   it "should limit the first name to be a maximum of 50 characters" do
0
     address = Address.new
0
@@ -44,10 +76,6 @@ describe Address do
0
   it "should limit the address to a maximum of 255 characters"
0
   
0
   #it "should not allow PO Box or variants to be entered as an address"
0
-
0
- #it "should find a shipping address for a customer (OrderUser)" do
0
- # order_address = Address.find_shipping_address_for_user(1)
0
- #end
0
   
0
   it "should provide a name" do
0
     address = Address.new
...
2
3
4
 
 
 
 
 
 
 
 
 
 
 
 
5
6
 
7
8
9
...
16
17
18
 
 
 
 
 
 
 
 
 
19
20
21
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
0
@@ -2,8 +2,21 @@ require File.join( File.dirname(__FILE__), "..", "spec_helper" )
0
 
0
 describe Country do
0
 
0
+ describe "associations" do
0
+ it "should have many (0..*) states" do
0
+ country = Country.new
0
+ country.should respond_to(:states)
0
+ end
0
+
0
+ it "should have many (0..*) provinces (alias for states)" do
0
+ country = Country.new
0
+ country.should respond_to(:provinces)
0
+ end
0
+ end
0
+
0
   before(:each) do
0
     @country = Country.new
0
+ #Country.auto_migrate! # clear db table
0
   end
0
 
0
   it "should be valid" do
0
@@ -16,6 +29,15 @@ describe Country do
0
     @country.errors.on(:name).should_not be_nil
0
   end
0
   
0
+ it "should have a unique name field" do
0
+ country1 = Country.new(:code => "FR", :name => "France")
0
+ country2 = Country.new(:code => "ZZ", :name => "France")
0
+ country1.save.should be_true
0
+ country1.name = "France"
0
+ country2.save.should be_false
0
+ country2.errors.on(:name).should_not be_nil
0
+ end
0
+
0
   it "should have a code field" do
0
     @country.name = "Lithuania"
0
     @country.code = "LT"