trax_model 0.0.92 → 0.0.93

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/README.md +227 -86
  4. data/lib/trax.rb +0 -1
  5. data/lib/trax/model.rb +23 -29
  6. data/lib/trax/model/attributes.rb +3 -1
  7. data/lib/trax/model/attributes/attribute.rb +11 -0
  8. data/lib/trax/model/attributes/definitions.rb +16 -0
  9. data/lib/trax/model/attributes/errors.rb +8 -0
  10. data/lib/trax/model/attributes/fields.rb +74 -0
  11. data/lib/trax/model/attributes/mixin.rb +48 -19
  12. data/lib/trax/model/attributes/type.rb +4 -0
  13. data/lib/trax/model/attributes/types/array.rb +8 -25
  14. data/lib/trax/model/attributes/types/boolean.rb +51 -0
  15. data/lib/trax/model/attributes/types/enum.rb +53 -12
  16. data/lib/trax/model/attributes/types/json.rb +36 -33
  17. data/lib/trax/model/attributes/types/string.rb +50 -0
  18. data/lib/trax/model/attributes/types/uuid_array.rb +17 -28
  19. data/lib/trax/model/attributes/value.rb +16 -0
  20. data/lib/trax/model/errors.rb +7 -0
  21. data/lib/trax/model/mixins.rb +11 -0
  22. data/lib/trax/model/mixins/field_scopes.rb +60 -0
  23. data/lib/trax/model/mixins/id_scopes.rb +36 -0
  24. data/lib/trax/model/mixins/sort_by_scopes.rb +25 -0
  25. data/lib/trax/model/railtie.rb +1 -0
  26. data/lib/trax/model/scopes.rb +16 -0
  27. data/lib/trax/model/struct.rb +168 -14
  28. data/lib/trax/model/unique_id.rb +14 -21
  29. data/lib/trax/model/uuid.rb +1 -1
  30. data/lib/trax/validators/enum_attribute_validator.rb +9 -0
  31. data/lib/trax/validators/future_validator.rb +1 -1
  32. data/lib/trax/validators/json_attribute_validator.rb +3 -3
  33. data/lib/trax/validators/string_attribute_validator.rb +17 -0
  34. data/lib/trax_model/version.rb +1 -1
  35. data/spec/db/database.yml +16 -0
  36. data/spec/db/schema/default_tables.rb +68 -0
  37. data/spec/db/schema/pg_tables.rb +27 -0
  38. data/spec/spec_helper.rb +20 -3
  39. data/spec/support/models.rb +123 -0
  40. data/spec/support/pg/models.rb +103 -0
  41. data/spec/trax/model/attributes/fields_spec.rb +88 -0
  42. data/spec/trax/model/attributes/types/enum_spec.rb +51 -0
  43. data/spec/trax/model/attributes/types/json_spec.rb +107 -0
  44. data/spec/trax/model/attributes_spec.rb +13 -0
  45. data/spec/trax/model/errors_spec.rb +1 -2
  46. data/spec/trax/model/mixins/field_scopes_spec.rb +7 -0
  47. data/spec/trax/model/struct_spec.rb +1 -1
  48. data/spec/trax/model/unique_id_spec.rb +1 -3
  49. data/spec/trax/validators/url_validator_spec.rb +1 -1
  50. data/trax_model.gemspec +4 -4
  51. metadata +57 -19
  52. data/lib/trax/model/config.rb +0 -16
  53. data/lib/trax/model/validators.rb +0 -15
  54. data/lib/trax/validators/enum_validator.rb +0 -16
  55. data/spec/support/schema.rb +0 -151
  56. data/spec/trax/model/config_spec.rb +0 -13
@@ -8,17 +8,10 @@ module Trax
8
8
  option :uuid_map, :default => {}
9
9
  end
10
10
 
11
- included do
11
+ after_included do |options|
12
12
  define_configuration_options!(:unique_id) do
13
+
13
14
  option :uuid_prefix,
14
- :setter => lambda{ |prefix|
15
- if(::Trax::Model::UniqueId.config.uuid_map.values.include?(prefix) && ::Trax::Model::UniqueId.config.uuid_map[self.source.name] != prefix)
16
- raise ::Trax::Model::Errors::DuplicatePrefixRegistered.new(:prefix => prefix, :model => self.source.name)
17
- end
18
-
19
- ::Trax::Model::UniqueId.config.uuid_map[self.source.name] = prefix
20
- ::Trax::Model::UUIDPrefix.new(prefix)
21
- },
22
15
  :validates => {
23
16
  :exclusion => {
24
17
  :in => ::Trax::Model::Registry.uuid_map.values
@@ -31,15 +24,25 @@ module Trax
31
24
  }
32
25
 
33
26
  option :uuid_column, :default => ::Trax::Model::UniqueId.config.uuid_column
27
+
28
+ klass do
29
+ def uuid_prefix=(prefix)
30
+ if(::Trax::Model::UniqueId.config.uuid_map.values.include?(prefix) && ::Trax::Model::UniqueId.config.uuid_map[self.source.name] != prefix)
31
+ raise ::Trax::Model::Errors::DuplicatePrefixRegistered.new(:prefix => prefix, :model => self.source.name)
32
+ end
33
+
34
+ ::Trax::Model::UniqueId.config.uuid_map[self.source.name] = prefix
35
+ super(::Trax::Model::UUIDPrefix.new(prefix))
36
+ end
37
+ end
38
+
34
39
  end
35
40
 
36
41
  #grab prefix from uuid registry if uuids are defined in an initializer
37
42
  if ::Trax::Model.mixin_registry.key?(:unique_id) && ::Trax::Model::UUID.klass_prefix_map.key?(self.name)
38
43
  self.unique_id_config.uuid_prefix = ::Trax::Model::UUID.klass_prefix_map[self.name]
39
44
  end
40
- end
41
45
 
42
- after_included do |options|
43
46
  self.unique_id_config.merge!(options)
44
47
 
45
48
  if(self.unique_id_config.uuid_prefix)
@@ -76,16 +79,6 @@ module Trax
76
79
  self.unique_id_config.uuid_prefix
77
80
  end
78
81
  end
79
-
80
- # def self.apply_mixin(target, options)
81
- # target.unique_id_config.merge!(options)
82
- #
83
- # if(target.unique_id_config.uuid_prefix)
84
- # target.default_value_for(:"#{target.unique_id_config.uuid_column}") {
85
- # ::Trax::Model::UUID.generate(target.unique_id_config.uuid_prefix)
86
- # }
87
- # end
88
- # end
89
82
  end
90
83
  end
91
84
  end
@@ -28,7 +28,7 @@ module Trax
28
28
  end
29
29
 
30
30
  def record
31
- @record ||= record_type ? record_type.find_by(:"#{record_type.uuid_column}" => self) : nil
31
+ @record ||= record_type ? record_type.find_by(:"#{record_type.unique_id_config.uuid_column}" => self) : nil
32
32
  end
33
33
 
34
34
  def record_type
@@ -0,0 +1,9 @@
1
+ class EnumAttributeValidator < ActiveModel::EachValidator
2
+ def validate_each(object, attribute, value)
3
+ enum_attribute = object.class.fields_module[attribute]
4
+
5
+ unless value.is_a?(enum_attribute) && enum_attribute === value
6
+ object.errors[attribute] = "#{value} is not an allowed value"
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  class FutureValidator < ActiveModel::EachValidator
2
2
  def validate_each(object, attribute, value)
3
- object.errors[attribute] << 'Must be in future' if value < ::DateTime.now
3
+ object.errors[attribute] << 'Must be in future' if value && (value < ::DateTime.now)
4
4
  end
5
5
  end
@@ -3,13 +3,13 @@
3
3
  # that a hash is not provided
4
4
  class JsonAttributeValidator < ActiveModel::EachValidator
5
5
  def validate_each(object, attribute, value)
6
- json_attribute = object.class.trax_attribute_fields[:json][attribute]
6
+ json_attribute = object.class.fields_module[attribute]
7
7
 
8
8
  unless value.is_a?(json_attribute) && value.valid?
9
9
  if value.is_a?(json_attribute)
10
10
  value.errors.messages.each_pair do |k,v|
11
- v = v.join(", ") if v.is_a?(Array)
12
- object.errors.add(:"#{attribute}.#{k}", v)
11
+ v.flatten.join(", ") if v.is_a?(Array)
12
+ object.errors.add("#{attribute}.#{k}", v)
13
13
  end
14
14
  else
15
15
  object.errors[attribute] << "can not be blank"
@@ -0,0 +1,17 @@
1
+ # validates each json property and bubbles up any errors
2
+ # also throws a generic can not be blank error, in the event
3
+ # that a hash is not provided
4
+ class StringAttributeValidator < ActiveModel::EachValidator
5
+ def validate_each(object, attribute, value)
6
+ string_attribute = value.class
7
+
8
+ unless value.valid?
9
+ if value.is_a?(string_attribute)
10
+ value.errors.messages.each_pair do |k,v|
11
+ v.flatten.join(", ") if v.is_a?(Array)
12
+ object.errors.add("#{attribute}.#{k}", v)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module TraxModel
2
- VERSION = '0.0.92'
2
+ VERSION = '0.0.93'
3
3
  end
@@ -0,0 +1,16 @@
1
+ default: &default
2
+ adapter: sqlite3
3
+ database: spec/test.db
4
+
5
+ sqllite:
6
+ <<: *default
7
+
8
+ postgres:
9
+ <<: *default
10
+ adapter: postgresql
11
+ encoding: unicode
12
+ database: trax_model_tests
13
+ pool: 5
14
+ host: localhost
15
+ username: postgres
16
+ password:
@@ -0,0 +1,68 @@
1
+ DEFAULT_TABLES = Proc.new do
2
+ create_table "products", :force => true do |t|
3
+ t.string "name"
4
+ t.integer "category_id"
5
+ t.integer "user_id"
6
+ t.decimal "price"
7
+ t.integer "in_stock_quantity"
8
+ t.integer "on_order_quantity"
9
+ t.boolean "active"
10
+ t.string "uuid"
11
+ t.integer "status"
12
+ t.integer "size"
13
+ t.datetime "created_at", :null => false
14
+ t.datetime "updated_at", :null => false
15
+ end
16
+
17
+ create_table "messages", :force => true do |t|
18
+ t.string "title"
19
+ t.text "body"
20
+ t.integer "status"
21
+ t.string "uuid"
22
+ t.boolean "deleted"
23
+ t.datetime "deleted_at"
24
+ t.datetime "deliver_at"
25
+ t.datetime "created_at", :null => false
26
+ t.datetime "updated_at", :null => false
27
+ end
28
+
29
+ create_table "widgets", :force => true do |t|
30
+ t.string "uuid"
31
+ t.string "email_address"
32
+ t.string "subdomain"
33
+ t.string "website"
34
+ t.integer "status"
35
+ t.datetime "created_at", :null => false
36
+ t.datetime "updated_at", :null => false
37
+ end
38
+
39
+ create_table "things", :force => true do |t|
40
+ t.string "name"
41
+ t.string "uuid"
42
+ t.datetime "created_at", :null => false
43
+ t.datetime "updated_at", :null => false
44
+ end
45
+
46
+ create_table "people", :force => true do |t|
47
+ t.string "name"
48
+ t.string "uuid"
49
+ t.datetime "created_at", :null => false
50
+ t.datetime "updated_at", :null => false
51
+ end
52
+
53
+ create_table "staplers", :force => true do |t|
54
+ t.string "name"
55
+ t.string "type"
56
+ t.integer "attribute_set_id"
57
+ t.datetime "created_at", :null => false
58
+ t.datetime "updated_at", :null => false
59
+ end
60
+
61
+ create_table "swingline_stapler_attribute_sets", :force => true do |t|
62
+ t.float "speed"
63
+ t.string "owner"
64
+
65
+ t.datetime "created_at", :null => false
66
+ t.datetime "updated_at", :null => false
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ PG_TABLES = Proc.new do
2
+ execute %q{CREATE EXTENSION IF NOT EXISTS "uuid-ossp";}
3
+ execute %q{CREATE EXTENSION IF NOT EXISTS "pg_trgm";}
4
+
5
+ create_table "ecommerce_products", :id => :uuid, :force => true do |t|
6
+ t.string "name"
7
+ t.string "type"
8
+ t.uuid "category_id"
9
+ t.uuid "user_id"
10
+ t.decimal "price"
11
+ t.boolean "active"
12
+ t.integer "status"
13
+ t.jsonb "custom_fields"
14
+ t.jsonb "stock"
15
+ t.datetime "created_at", :null => false
16
+ t.datetime "updated_at", :null => false
17
+ end
18
+
19
+ create_table "ecommerce_product_attribute_sets", :id => :uuid, :force => true do |t|
20
+ t.uuid "user_id"
21
+ t.uuid "product_id"
22
+ t.string "type"
23
+ t.jsonb "specifics"
24
+ t.datetime "created_at", :null => false
25
+ t.datetime "updated_at", :null => false
26
+ end
27
+ end
@@ -1,18 +1,35 @@
1
1
  require 'rubygems'
2
2
  require 'bundler'
3
3
  require 'simplecov'
4
- require 'pry'
4
+ # require 'pry'
5
5
  require 'trax_model'
6
+ require 'active_record'
6
7
 
7
8
  SimpleCov.start do
8
9
  add_filter '/spec/'
9
10
  end
10
11
 
12
+ ENV["DB"] ||= "sqllite"
13
+ ENV["DB"] = "postgres" if ENV["DB"] == "pg" || ENV["pg"] == "true"
14
+
11
15
  RSpec.configure do |config|
16
+ config.filter_run_excluding :postgres => true unless ENV["DB"] == "postgres"
17
+
12
18
  config.before(:suite) do
19
+ db_config = ::YAML::load(::File.open("#{File.dirname(__FILE__)}/db/database.yml"))
20
+
21
+ ::ActiveRecord::Base.establish_connection(db_config[ENV["DB"]])
22
+
23
+ ::ActiveRecord::Base.connection.tables.each do |table|
24
+ ::ActiveRecord::Base.connection.drop_table(table)
25
+ end
26
+
27
+ ::Trax::Model.configure do |config|
28
+ config.auto_include = false
29
+ end
30
+
31
+ ::Dir["#{::File.dirname(__FILE__)}/support/*.rb"].each { |f| require f }
13
32
  end
14
33
  end
15
34
 
16
35
  Bundler.require(:default, :development, :test)
17
-
18
- ::Dir["#{::File.dirname(__FILE__)}/support/*.rb"].each {|f| require f }
@@ -0,0 +1,123 @@
1
+ require 'active_record'
2
+
3
+ ::ActiveRecord::Schema.define(:version => 1) do
4
+ require_relative '../db/schema/default_tables'
5
+ instance_eval(&DEFAULT_TABLES)
6
+
7
+ if ENV["DB"] == "postgres"
8
+ require_relative '../db/schema/pg_tables'
9
+ instance_eval(&PG_TABLES)
10
+ end
11
+ end
12
+
13
+ if ENV["DB"] == "postgres"
14
+ require_relative 'pg/models'
15
+ end
16
+
17
+ class Product < ::ActiveRecord::Base
18
+ include ::Trax::Model
19
+ include ::Trax::Model::Attributes::Mixin
20
+
21
+ mixins :unique_id => {
22
+ :uuid_column => "uuid",
23
+ :uuid_prefix => "1a"
24
+ }
25
+
26
+ define_attributes do
27
+ enum :status, :default => :in_stock do
28
+ define :in_stock, 1
29
+ define :out_of_stock, 2
30
+ define :backordered, 3
31
+ end
32
+
33
+ boolean :active, :default => false
34
+ end
35
+ end
36
+
37
+ module Products
38
+ class Shoes < Product
39
+ include ::Trax::Model
40
+ include ::Trax::Model::Attributes::Mixin
41
+ end
42
+
43
+ class MensShoes < Shoes
44
+ include ::Trax::Model
45
+ include ::Trax::Model::Attributes::Mixin
46
+
47
+ define_attributes do
48
+ enum :size, :default => :mens_9 do
49
+ define :mens_6, 1
50
+ define :mens_7, 2
51
+ define :mens_8, 3
52
+ define :mens_9, 4
53
+ define :mens_10, 5
54
+ define :mens_11, 6
55
+ define :mens_12, 7
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ class Widget < ::ActiveRecord::Base
62
+ include ::Trax::Model
63
+
64
+ mixins :unique_id => {
65
+ :uuid_column => "uuid",
66
+ :uuid_prefix => "2a"
67
+ }
68
+
69
+ validates :subdomain, :subdomain => true, :allow_nil => true
70
+ validates :email_address, :email => true, :allow_nil => true
71
+ validates :website, :url => true, :allow_nil => true
72
+ end
73
+
74
+ class Message < ::ActiveRecord::Base
75
+ include ::Trax::Model
76
+
77
+ mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "3a" },
78
+ :freezable => true,
79
+ :restorable => { :field => :deleted }
80
+
81
+ enum :status => [ :queued, :scheduled, :delivered, :failed_delivery ]
82
+
83
+ default_value_for :status do
84
+ self.statuses[:queued]
85
+ end
86
+
87
+ validates :deliver_at, :future => true, :allow_nil => true
88
+
89
+ freezable_by_enum :status => [ :delivered, :failed_delivery ]
90
+ end
91
+
92
+ class Thing < ::ActiveRecord::Base
93
+ include ::Trax::Model
94
+
95
+ mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "4a" }
96
+ end
97
+
98
+ class Person < ::ActiveRecord::Base
99
+ include ::Trax::Model
100
+
101
+ mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "5a" }
102
+ end
103
+
104
+ class Stapler < ::ActiveRecord::Base
105
+ include ::Trax::Model
106
+ end
107
+
108
+ class SwinglineStapler < ::Stapler
109
+ include ::Trax::Model::STI::Attributes
110
+ end
111
+
112
+ class SwinglineStaplerAttributeSet < ::ActiveRecord::Base
113
+ end
114
+
115
+ require 'trax/model/struct'
116
+
117
+ class StoreCategory < ::Trax::Model::Struct
118
+ string :name
119
+ struct :meta_attributes do
120
+ string :description
121
+ string :keywords
122
+ end
123
+ end
@@ -0,0 +1,103 @@
1
+ module Ecommerce
2
+ class ProductAttributeSet < ::ActiveRecord::Base
3
+ self.table_name = "ecommerce_product_attribute_sets"
4
+
5
+ include ::Trax::Model
6
+ include ::Trax::Model::Attributes::Mixin
7
+
8
+ mixins :unique_id => { :uuid_prefix => "c2" }
9
+
10
+ belongs_to :user, :class_name => "Ecommerce::User"
11
+ belongs_to :product, :class_name => "Ecommerce::Product"
12
+ end
13
+
14
+ class ShippingAttributes < ::Ecommerce::ProductAttributeSet
15
+ include ::Trax::Model
16
+ include ::Trax::Model::Attributes::Mixin
17
+
18
+ define_attributes do
19
+ struct :specifics, :validate => true do
20
+ string :cost, :default => 0
21
+
22
+ validates(:cost, :length => {:minimum => 10})
23
+
24
+ enum :service do
25
+ define :usps, 1
26
+ define :fedex, 2
27
+ end
28
+
29
+ struct :dimensions, :validate => true do
30
+ string :length
31
+ string :width
32
+ string :height
33
+
34
+ validates(:length, :length => {:maximum => 10})
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ class Product < ::ActiveRecord::Base
41
+ self.table_name = "ecommerce_products"
42
+
43
+ include ::Trax::Model
44
+ include ::Trax::Model::Attributes::Mixin
45
+
46
+ mixins :unique_id => { :uuid_prefix => "9a" }
47
+
48
+ belongs_to :store, :class_name => "Ecommerce::Store"
49
+
50
+ define_attributes do
51
+ string :name, :default => "Whatever" do
52
+ validates(self, :length => { :minimum => 20 })
53
+ end
54
+ boolean :active, :default => true
55
+
56
+ enum :status, :default => :in_stock do
57
+ define :in_stock, 1
58
+ define :out_of_stock, 2
59
+ define :backordered, 3
60
+ end
61
+ end
62
+ end
63
+
64
+ module Products
65
+ class Shoes < ::Ecommerce::Product
66
+ include ::Trax::Model
67
+ include ::Trax::Model::Attributes::Mixin
68
+ end
69
+
70
+ class MensShoes < ::Ecommerce::Products::Shoes
71
+ include ::Trax::Model
72
+ include ::Trax::Model::Attributes::Mixin
73
+
74
+ define_attributes do
75
+ string :name, :default => "Some Shoe Name", :define_scopes => true
76
+ boolean :active, :default => true, :define_scopes => true
77
+
78
+ struct :custom_fields do
79
+ string :primary_utility, :default => "Skateboarding", :define_scopes => true
80
+ string :sole_material
81
+ boolean :has_shoelaces, :define_scopes => true
82
+
83
+ enum :color, :default => :blue, :define_scopes => false do
84
+ define :red, 1
85
+ define :blue, 2
86
+ define :green, 3
87
+ define :black, 4
88
+ end
89
+
90
+ enum :size, :default => :mens_9, :define_scopes => true do
91
+ define :mens_6, 1
92
+ define :mens_7, 2
93
+ define :mens_8, 3
94
+ define :mens_9, 4
95
+ define :mens_10, 5
96
+ define :mens_11, 6
97
+ define :mens_12, 7
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end