trax_model 0.0.97 → 0.0.98

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe39dac9ff74e51159a879a215dc98ccc0bd78c4
4
- data.tar.gz: 373428f4903798d45be49fa6d9b539a68c5ed890
3
+ metadata.gz: a56cf1df33c14218b82e1c7b26c649c52adb18df
4
+ data.tar.gz: e12109ca5ea6fb9abccb869c7c67d87431614f9c
5
5
  SHA512:
6
- metadata.gz: ddd740eab7478af4d5b6b3c02556d281191899d8f5ad6ae1882d937e38120f0c759a2970d32dd58fed65d806928e3fc53a02ccf89efff3f4d1afc8a93fb6dba8
7
- data.tar.gz: e71ce067fa72afa23bf4149849fd29d37a4e6eb823e2033d0a96835fbc1592ec3e537cf2f99fca3ba1ddd249ab3a8a9dc6675fe33fd64eb2e7e7252c4561e1c6
6
+ metadata.gz: dc8eb0602e23a03e13a87daf5420f3ea51f1dab49708cee5128c242e175609f795a852185f60c2b26f8a250d42c7a2873a75a8b9c1d8007111cd5afcb6fe338a
7
+ data.tar.gz: ced26e12c60dcff2b03ccb64bfb1c0bae10e526eb9cb2e9748be3a528e67db668634f47d1ac5f6f4e5b6feca75cf39e1390677c415ba7f8b3365ff3c13fe0708
data/Guardfile CHANGED
@@ -17,7 +17,7 @@ end
17
17
  # * 'just' rspec: 'rspec'
18
18
  guard :rspec, cmd: 'bundle exec rspec' do
19
19
  watch(%r{^spec/.+_spec\.rb$})
20
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
20
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
21
21
  watch('spec/spec_helper.rb') { "spec" }
22
22
 
23
23
  # Rails example
@@ -5,7 +5,6 @@ require 'hashie/mash'
5
5
  require 'hashie/trash'
6
6
  require 'hashie/extensions/dash/indifferent_access'
7
7
  require 'simple_enum'
8
- require_relative './string'
9
8
  require_relative './validators/boolean_validator'
10
9
  require_relative './validators/email_validator'
11
10
  require_relative './validators/frozen_validator'
@@ -39,6 +38,7 @@ module Trax
39
38
  autoload :Restorable
40
39
  autoload :Railtie
41
40
  autoload :STI
41
+ autoload :StringExtensions
42
42
  autoload :Struct
43
43
  autoload :StructExtensions
44
44
  autoload :Validators
@@ -85,6 +85,7 @@ module Trax
85
85
  ::Trax::Model::Mixins::FieldScopes
86
86
  ::Trax::Model::Mixins::IdScopes
87
87
  ::Trax::Model::Mixins::SortByScopes
88
+ ::Trax::Model::Mixins::StiEnum
88
89
  end
89
90
 
90
91
  eager_autoload_mixins!
@@ -156,6 +157,8 @@ module Trax
156
157
  end
157
158
  end
158
159
 
160
+ ::String.include(::Trax::Model::StringExtensions)
161
+
159
162
  ::ActiveSupport.run_load_hooks(:trax_model, self)
160
163
  end
161
164
  end
@@ -10,7 +10,7 @@ module Trax
10
10
  klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
11
11
 
12
12
  attribute_klass = if options.key?(:extend)
13
- _klass_prototype = options[:extend].constantize
13
+ _klass_prototype = options[:extend].is_a?(::String) ? options[:extend].safe_constantize : options[:extend]
14
14
  _klass = ::Trax::Core::NamedClass.new(klass_name, _klass_prototype, :parent_definition => klass, &block)
15
15
  _klass
16
16
  else
@@ -8,7 +8,7 @@ module Trax
8
8
  def self.define_attribute(klass, attribute_name, **options, &block)
9
9
  klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
10
10
  attribute_klass = if options.key?(:extend)
11
- _klass_prototype = options[:extend].constantize
11
+ _klass_prototype = options[:extend].is_a?(::String) ? options[:extend].safe_constantize : options[:extend]
12
12
  _klass = ::Trax::Core::NamedClass.new(klass_name, _klass_prototype, :parent_definition => klass, &block)
13
13
  _klass.include(::Trax::Model::StructExtensions)
14
14
  _klass
@@ -6,6 +6,7 @@ module Trax
6
6
  autoload :FieldScopes
7
7
  autoload :IdScopes
8
8
  autoload :SortByScopes
9
+ autoload :StiEnum
9
10
  end
10
11
  end
11
12
  end
@@ -14,13 +14,11 @@ module Trax
14
14
  case field_scope_options[:type]
15
15
  when :where
16
16
  define_where_scope_for_field(field_scope_name, **field_scope_options)
17
- when :match
17
+ when :where_lower
18
+ define_where_lower_scope_for_field(field_scope_name, **field_scope_options)
19
+ when :match, :matching
18
20
  define_matching_scope_for_field(field_scope_name, **field_scope_options)
19
- when :matching
20
- define_matching_scope_for_field(field_scope_name, **field_scope_options)
21
- when :not
22
- define_where_not_scope_for_field(field_scope_name, **field_scope_options)
23
- when :where_not
21
+ when :where_not, :not
24
22
  define_where_not_scope_for_field(field_scope_name, **field_scope_options)
25
23
  else
26
24
  define_where_scope_for_field(field_scope_name, **field_scope_options)
@@ -32,8 +30,32 @@ module Trax
32
30
  private
33
31
  def define_where_scope_for_field(field_scope_name, **options)
34
32
  scope field_scope_name, lambda{ |*_values|
35
- _values.flat_compact_uniq!
36
- where(options[:field] => _values)
33
+ _relation = if _values.first.is_a?(::ActiveRecord::Relation)
34
+ where(options[:field] => _values.first)
35
+ else
36
+ _values.flat_compact_uniq!
37
+ where(options[:field] => _values)
38
+ end
39
+
40
+ _relation
41
+ }
42
+
43
+ # Alias scope names with pluralized versions, i.e. by_id also => by_ids
44
+ singleton_class.__send__(:alias_method, :"#{field_scope_name.to_s.pluralize}", field_scope_name)
45
+ end
46
+
47
+ def define_where_lower_scope_for_field(field_scope_name, **options)
48
+ scope field_scope_name, lambda{ |*_values|
49
+ _query = "lower(#{options[:field]}) in (?)"
50
+
51
+ _relation = if _values.first.is_a?(::ActiveRecord::Relation)
52
+ where(_query, _values.first)
53
+ else
54
+ _values.map!(&:downcase)
55
+ where(_query, _values)
56
+ end
57
+
58
+ _relation
37
59
  }
38
60
 
39
61
  # Alias scope names with pluralized versions, i.e. by_id also => by_ids
@@ -42,15 +64,27 @@ module Trax
42
64
 
43
65
  def define_where_not_scope_for_field(field_scope_name, **options)
44
66
  scope field_scope_name, lambda{ |*_values|
45
- _values.flat_compact_uniq!
46
- where.not(options[:field] => _values)
67
+ _relation = if _values.first.is_a?(::ActiveRecord::Relation)
68
+ where.not(options[:field] => _values.first)
69
+ else
70
+ _values.flat_compact_uniq!
71
+ where.not(options[:field] => _values)
72
+ end
73
+
74
+ _relation
47
75
  }
48
76
  end
49
77
 
50
78
  def define_matching_scope_for_field(field_scope_name, **options)
51
79
  scope field_scope_name, lambda{ |*_values|
52
- _values.flat_compact_uniq!
53
- matching(options[:field] => _values)
80
+ _relation = if _values.first.is_a?(::ActiveRecord::Relation)
81
+ matching(options[:field] => _values.first)
82
+ else
83
+ _values.flat_compact_uniq!
84
+ matching(options[:field] => _values)
85
+ end
86
+
87
+ _relation
54
88
  }
55
89
  end
56
90
  end
@@ -0,0 +1,51 @@
1
+
2
+ module Trax
3
+ module Model
4
+ module Mixins
5
+ module StiEnum
6
+ extend ::Trax::Model::Mixin
7
+
8
+ mixed_in do |**options|
9
+ default_options = { :type_column => :type, :enum_column => :kind }
10
+
11
+ options = default_options.merge!(options)
12
+ end
13
+
14
+ included do
15
+ after_initialize :set_type_from_kind, :unless => :type?, :if => :kind?
16
+ after_initialize :set_kind_from_type, :unless => :kind?, :if => :type?
17
+ end
18
+
19
+ def set_type_from_kind
20
+ self[:type] = self.class.kind_to_type_mapping[self[:kind]] if !self[:type]
21
+ end
22
+
23
+ def set_kind_from_type
24
+ self[:kind] = self.class.type_to_kind_mapping[self[:type]] if !self[:kind]
25
+ end
26
+
27
+ module ClassMethods
28
+ def subclass_from_attributes?(attrs)
29
+ _attrs = attrs.with_indifferent_access if attrs
30
+ attrs[:type] = kind_to_type_mapping[_attrs["kind"]] if attrs && !_attrs.key?("type") && _attrs.key?("kind")
31
+ super(attrs)
32
+ end
33
+
34
+ def type_to_kind_mapping
35
+ @type_to_kind_mapping ||= fields[:kind].choices.each_with_object({}) do |choice, result|
36
+ result[choice.attributes[:type]] = choice.to_s
37
+ result
38
+ end.with_indifferent_access
39
+ end
40
+
41
+ def kind_to_type_mapping
42
+ @kind_to_type_mapping ||= fields[:kind].choices.each_with_object({}) do |choice, result|
43
+ result[choice.to_s] = choice.attributes[:type]
44
+ result
45
+ end.with_indifferent_access
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ module Trax
2
+ module Model
3
+ module StringExtensions
4
+ extend ::ActiveSupport::Concern
5
+
6
+ def uuid
7
+ ::Trax::Model::UUID === self ? ::Trax::Model::UUID.new(self) : nil
8
+ end
9
+ end
10
+ end
11
+ end
@@ -40,11 +40,70 @@ module Trax
40
40
  define_where_scopes_for_boolean_property(attribute_name, attribute_klass, **options)
41
41
  when :enum
42
42
  define_scopes_for_enum(attribute_name, attribute_klass, **options)
43
+ when :array
44
+ define_scopes_for_array(attribute_name, attribute_klass, **options)
45
+ when :integer
46
+ define_scopes_for_numeric(attribute_name, attribute_klass, **options)
47
+ when :time
48
+ define_scopes_for_time(attribute_name, attribute_klass, **options)
43
49
  else
44
50
  define_where_scopes_for_property(attribute_name, attribute_klass, **options)
45
51
  end
46
52
  end
47
53
 
54
+ def define_scopes_for_array(attribute_name, property_klass, as:nil)
55
+ return unless has_active_record_ancestry?(property_klass)
56
+
57
+ model_class = model_class_for_property(property_klass)
58
+ field_name = property_klass.parent_definition.name.demodulize.underscore
59
+ attribute_name = property_klass.name.demodulize.underscore
60
+ scope_name = as || :"by_#{field_name}_#{attribute_name}"
61
+
62
+ model_class.scope(scope_name, lambda{ |*_scope_values|
63
+ _scope_values.flat_compact_uniq!
64
+ model_class.where("#{field_name} -> '#{attribute_name}' ?| array[:values]", :values => _scope_values)
65
+ })
66
+ end
67
+
68
+ def define_scopes_for_numeric(attribute_name, property_klass, as:nil)
69
+ return unless has_active_record_ancestry?(property_klass)
70
+
71
+ model_class = model_class_for_property(property_klass)
72
+ field_name = property_klass.parent_definition.name.demodulize.underscore
73
+ attribute_name = property_klass.name.demodulize.underscore
74
+ cast_type = property_klass.type
75
+
76
+ { :gt => '>', :gte => '>=', :lt => '<', :lte => '<=', :eq => '='}.each_pair do |k, operator|
77
+ scope_name = as ? :"#{as}_#{k}" : :"by_#{field_name}_#{attribute_name}_#{k}"
78
+
79
+ model_class.scope(scope_name, lambda{ |*_scope_values|
80
+ _scope_values.flat_compact_uniq!
81
+ model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
82
+ })
83
+ end
84
+ end
85
+
86
+ def define_scopes_for_time(attribute_name, property_klass, as:nil)
87
+ return unless has_active_record_ancestry?(property_klass)
88
+
89
+ model_class = model_class_for_property(property_klass)
90
+ field_name = property_klass.parent_definition.name.demodulize.underscore
91
+ attribute_name = property_klass.name.demodulize.underscore
92
+ cast_type = 'timestamp'
93
+
94
+ { :gt => '>', :lt => '<'}.each_pair do |k, operator|
95
+ scope_prefix = as ? as : :"by_#{field_name}_#{attribute_name}"
96
+ scope_name = "#{scope_prefix}_#{k}"
97
+ scope_alias = "#{scope_prefix}_#{{:gt => 'after', :lt => 'before' }[k]}"
98
+
99
+ model_class.scope(scope_name, lambda{ |*_scope_values|
100
+ _scope_values.flat_compact_uniq!
101
+ model_class.where("(#{field_name} ->> '#{attribute_name}')::#{cast_type} #{operator} ?", _scope_values)
102
+ })
103
+ model_class.singleton_class.__send__("alias_method", scope_alias.to_sym, scope_name)
104
+ end
105
+ end
106
+
48
107
  #this only supports properties 1 level deep, but works beautifully
49
108
  #I.E. for this structure
50
109
  # define_attributes do
@@ -2,16 +2,27 @@ module Trax
2
2
  module Model
3
3
  class UUID < String
4
4
  class_attribute :prefix_map
5
-
6
5
  self.prefix_map = ::Hashie::Mash.new
7
6
 
7
+ def self.===(val)
8
+ return false unless (val.is_a?(::Trax::Model::UUID) || val.is_a?(::String)) && val.length == 36
9
+ return true if val.is_a?(::Trax::Model::UUID)
10
+
11
+ #i.e. if we have 2 and 3 digit lengths, for value 'ABCDE' return ['AB', 'ABC']
12
+ value_samples = prefix_lengths.map do |limit|
13
+ val[0..(limit-1)]
14
+ end
15
+
16
+ return value_samples.any?{|sample| prefixes.include?(sample) }
17
+ end
18
+
8
19
  def self.klass_prefix_map
9
20
  prefix_map.invert
10
21
  end
11
22
 
12
23
  def self.generate(prefix = nil)
13
24
  uuid = ::SecureRandom.uuid
14
- uuid[0..1] = prefix if prefix
25
+ uuid[0..(prefix.length-1)] = prefix if prefix
15
26
  uuid
16
27
  end
17
28
 
@@ -23,6 +34,14 @@ module Trax
23
34
  prefix_map[:"#{prefix_value}"] = klass
24
35
  end
25
36
 
37
+ def self.prefixes
38
+ @prefixes ||= ::Trax::Model::Registry.uuid_map.keys
39
+ end
40
+
41
+ def self.prefix_lengths
42
+ @prefix_lengths ||= prefixes.map(&:length).uniq
43
+ end
44
+
26
45
  def self.register(&block)
27
46
  instance_exec(&block)
28
47
  end
@@ -1,3 +1,3 @@
1
1
  module TraxModel
2
- VERSION = '0.0.97'
2
+ VERSION = '0.0.98'
3
3
  end
@@ -1,4 +1,13 @@
1
1
  DEFAULT_TABLES = Proc.new do
2
+ create_table "vehicles", :force => true do |t|
3
+ t.string "type"
4
+ t.integer "kind"
5
+ t.integer "make"
6
+ t.integer "model"
7
+ t.string "uuid"
8
+ t.datetime "created_at", :null => false
9
+ t.datetime "updated_at", :null => false
10
+ end
2
11
  create_table "products", :force => true do |t|
3
12
  t.string "name"
4
13
  t.integer "category_id"
@@ -14,7 +14,9 @@ ENV["DB"] ||= "sqllite"
14
14
  ENV["DB"] = "postgres" if ENV["DB"] == "pg" || ENV["pg"] == "true"
15
15
 
16
16
  RSpec.configure do |config|
17
+ config.filter_run :focus
17
18
  config.filter_run_excluding :postgres => true unless ENV["DB"] == "postgres"
19
+ config.run_all_when_everything_filtered = true
18
20
 
19
21
  config.before(:suite) do
20
22
  db_config = ::YAML::load(::File.open("#{File.dirname(__FILE__)}/db/database.yml"))
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require 'trax_core'
2
3
 
3
4
  ::ActiveRecord::Schema.define(:version => 1) do
4
5
  require_relative '../db/schema/default_tables'
@@ -13,6 +14,15 @@ end
13
14
  if ENV["DB"] == "postgres"
14
15
  require_relative 'pg/models'
15
16
  end
17
+ #
18
+ # class Blueprint < ::Trax::Core::Blueprint
19
+ # class Vehicle < ::Trax::Core::Blueprint
20
+ # enum :kind do
21
+ # define :car, 1, :type => "Vehicle::Car"
22
+ # define :truck, 2, :type => "Vehicle::Truck"
23
+ # end
24
+ # end
25
+ # end
16
26
 
17
27
  class Product < ::ActiveRecord::Base
18
28
  include ::Trax::Model
@@ -60,6 +70,27 @@ module Products
60
70
  end
61
71
  end
62
72
 
73
+ class Vehicle < ::ActiveRecord::Base
74
+ include ::Trax::Model
75
+ include ::Trax::Model::Attributes::Mixin
76
+
77
+ mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "9c" },
78
+ :sti_enum => true
79
+
80
+ define_attributes do
81
+ enum :kind do
82
+ define :car, 1, :type => "Vehicle::Car"
83
+ define :truck, 2, :type => "Vehicle::Truck"
84
+ end
85
+ end
86
+
87
+ class Car < ::Vehicle
88
+ end
89
+
90
+ class Truck < ::Vehicle
91
+ end
92
+ end
93
+
63
94
  class Widget < ::ActiveRecord::Base
64
95
  include ::Trax::Model
65
96
 
@@ -78,7 +109,11 @@ class Message < ::ActiveRecord::Base
78
109
 
79
110
  mixins :unique_id => { :uuid_column => "uuid", :uuid_prefix => "3a" },
80
111
  :freezable => true,
81
- :restorable => { :field => :deleted }
112
+ :restorable => { :field => :deleted },
113
+ :field_scopes => {
114
+ :by_title => true,
115
+ :by_title_case_insensitive => { :field => :title, :type => :where_lower}
116
+ }
82
117
 
83
118
  enum :status => [ :queued, :scheduled, :delivered, :failed_delivery ]
84
119
 
@@ -114,8 +149,6 @@ end
114
149
  class SwinglineStaplerAttributeSet < ::ActiveRecord::Base
115
150
  end
116
151
 
117
-
118
-
119
152
  class StoreCategory < ::Trax::Core::Types::Struct
120
153
  include ::Trax::Model::StructExtensions
121
154
 
@@ -6,9 +6,6 @@ module Ecommerce
6
6
  include ::Trax::Model::Attributes::Mixin
7
7
 
8
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
9
  end
13
10
 
14
11
  class ShippingAttributes < ::Ecommerce::ProductAttributeSet
@@ -39,6 +36,8 @@ module Ecommerce
39
36
 
40
37
  validates(:length, :numericality => {:greater_than => 0})
41
38
  end
39
+
40
+ define_model_scope_for :cost, :as => :by_cost
42
41
  end
43
42
  end
44
43
  end
@@ -64,6 +63,17 @@ module Ecommerce
64
63
  define :out_of_stock, 2
65
64
  define :backordered, 3
66
65
  end
66
+
67
+ struct :custom_fields do
68
+ integer :cost
69
+ integer :price
70
+ integer :in_stock_quantity, :default => 0
71
+ integer :number_of_sales, :default => 0
72
+ time :last_received_at
73
+
74
+ define_model_scope_for :in_stock_quantity, :as => :by_quantity_in_stock
75
+ define_model_scope_for :last_received_at, :as => :by_last_received_at
76
+ end
67
77
  end
68
78
  end
69
79
 
@@ -71,6 +81,9 @@ module Ecommerce
71
81
  class Shoes < ::Ecommerce::Product
72
82
  include ::Trax::Model
73
83
  include ::Trax::Model::Attributes::Mixin
84
+
85
+ define_attributes do
86
+ end
74
87
  end
75
88
 
76
89
  class MensShoes < ::Ecommerce::Products::Shoes
@@ -81,14 +94,12 @@ module Ecommerce
81
94
  string :name, :default => "Some Shoe Name"
82
95
  boolean :active, :default => true
83
96
 
84
- struct :custom_fields do
97
+ struct :custom_fields, :extend => ::Ecommerce::Product::Fields::CustomFields do
85
98
  string :primary_utility, :default => "Skateboarding"
86
99
  string :sole_material
87
100
  boolean :has_shoelaces
88
- integer :in_stock_quantity, :default => 0
89
- integer :number_of_sales, :default => 0
90
- integer :cost
91
- integer :price
101
+
102
+ array :tags
92
103
 
93
104
  enum :color, :default => :blue do
94
105
  define :red, 1
@@ -112,6 +123,7 @@ module Ecommerce
112
123
  end
113
124
 
114
125
  define_model_scopes_for(:primary_utility, :has_shoelaces, :size)
126
+ define_model_scope_for :tags, :as => :by_tags
115
127
  end
116
128
  end
117
129
  end
@@ -4,7 +4,7 @@ describe ::Trax::Model::Attributes::Types::Struct, :postgres => true do
4
4
  subject{ ::Ecommerce::Products::MensShoes::Fields::CustomFields }
5
5
 
6
6
  it { expect(subject.new.primary_utility).to eq "Skateboarding" }
7
- it { expect(subject.new.sole_material).to eq nil }
7
+ it { expect(subject.new.sole_material).to eq "" }
8
8
 
9
9
  context "attribute definition" do
10
10
  subject { ::Ecommerce::ShippingAttributes.new }
@@ -69,6 +69,86 @@ describe ::Trax::Model::Attributes::Types::Struct, :postgres => true do
69
69
  it { expect(subject.by_custom_fields_size(:mens_6, :mens_7)).to include(mens_6, mens_7) }
70
70
  it { expect(subject.by_custom_fields_size(:mens_6, :mens_7)).to_not include(mens_10) }
71
71
  end
72
+
73
+ context "array property" do
74
+ before(:all) do
75
+ @item_1 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :tags => ['skateboarding'] })
76
+ @item_2 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :tags => ['skateboarding', 'walking']})
77
+ @item_3 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :tags => ['running']})
78
+ end
79
+
80
+ subject { ::Ecommerce::Products::MensShoes.all }
81
+
82
+ it { expect(subject.by_tags('skateboarding')).to include(@item_1, @item_2) }
83
+ it { expect(subject.by_tags('skateboarding')).to_not include(@item_3) }
84
+ it { expect(subject.by_tags('running')).to include(@item_3) }
85
+ it { expect(subject.by_tags('running')).to_not include(@item_1, @item_2) }
86
+ end
87
+
88
+ context "time property" do
89
+ before(:all) do
90
+ @timestamp_1 = "2013-01-01 07:00:00"
91
+ @timestamp_2 = "2014-01-01 07:00:00"
92
+ @timestamp_3 = "2015-01-01 07:00:00"
93
+ @item_1 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :last_received_at => @timestamp_1 })
94
+ @item_2 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :last_received_at => @timestamp_2 })
95
+ @item_3 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :last_received_at => @timestamp_3 })
96
+ end
97
+
98
+ subject { ::Ecommerce::Products::MensShoes.all }
99
+
100
+ context "greater than" do
101
+ it { expect(subject.by_last_received_at_gt(@timestamp_2)).to include(@item_3) }
102
+ it { expect(subject.by_last_received_at_gt(@timestamp_2)).to_not include(@item_2, @item1) }
103
+ end
104
+
105
+ context "less than" do
106
+ it { expect(subject.by_last_received_at_lt(@timestamp_2)).to include(@item_1) }
107
+ it { expect(subject.by_last_received_at_lt(@timestamp_2)).to_not include(@item_3, @item_2) }
108
+ end
109
+ end
110
+
111
+ context "numeric property" do
112
+ before(:all) do
113
+ @item_1 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :in_stock_quantity => 1 })
114
+ @item_2 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :in_stock_quantity => 2 })
115
+ @item_3 = ::Ecommerce::Products::MensShoes.create(:custom_fields => { :in_stock_quantity => 3 })
116
+ end
117
+
118
+ subject { ::Ecommerce::Products::MensShoes.all }
119
+
120
+
121
+ context "less than" do
122
+ it { expect(subject.by_quantity_in_stock_lt(2)).to include(@item_1) }
123
+ it { expect(subject.by_quantity_in_stock_lt(2)).to_not include(@item_2, @item_3) }
124
+
125
+ context "or equal" do
126
+ it { expect(subject.by_quantity_in_stock_lte(2)).to include(@item_1, @item_2) }
127
+ it { expect(subject.by_quantity_in_stock_lt(2)).to_not include(@item_3) }
128
+ end
129
+ end
130
+
131
+ context "greater than" do
132
+ it { expect(subject.by_quantity_in_stock_gt(2)).to include(@item_3) }
133
+ it { expect(subject.by_quantity_in_stock_gt(2)).to_not include(@item_2, @item_1) }
134
+
135
+ context "or equal" do
136
+ it { expect(subject.by_quantity_in_stock_gte(2)).to include(@item_3, @item_2) }
137
+ it { expect(subject.by_quantity_in_stock_gte(2)).to_not include(@item_1) }
138
+ end
139
+ end
140
+
141
+ context "equal" do
142
+ it {
143
+ values = subject.by_quantity_in_stock_eq(2).map(&:custom_fields).map(&:in_stock_quantity)
144
+ expect(values).to include(2)
145
+ }
146
+ it {
147
+ values = subject.by_quantity_in_stock_eq(2).map(&:custom_fields).map(&:in_stock_quantity)
148
+ expect(values).to_not include(1, 3)
149
+ }
150
+ end
151
+ end
72
152
  end
73
153
 
74
154
  context "dirty attributes" do
@@ -1,7 +1,47 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ::Trax::Model::Mixins::FieldScopes do
4
- subject{ ::Message.create(:title => "Whatever") }
4
+ let(:known_title) { "Whatever's Clever: The Questioning" }
5
+ let(:other_known_title) { "Whatever's Clever: The Reckoning" }
6
+ let(:unknown_title) { "Most Known Unkown" }
7
+ let(:known_titles) { [known_title, other_known_title] }
8
+ let!(:known_message) { ::Message.create(:title => known_title) }
9
+ let!(:other_known_message) { ::Message.create(:title => other_known_title) }
10
+ let!(:known_titles_relation) { ::Message.where(id: known_message.id).select(:title) }
11
+ let!(:known_titles_downcased_relation) { ::Message.where(id: known_message.id).select('lower(title)') }
12
+ let!(:known_titles_upcased_relation) { ::Message.where(id: known_message.id).select('upper(title)') }
5
13
 
6
- its(:status) { should eq "queued" }
14
+ subject { ::Message }
15
+
16
+ context "type 'where'" do
17
+ it { expect(subject.by_title(known_title)).to be_present }
18
+ it { expect(subject.by_title(known_title.downcase)).to be_empty }
19
+ it { expect(subject.by_title(known_title.upcase)).to be_empty }
20
+
21
+ it { expect(subject.by_title(*known_titles)).to be_present }
22
+ it { expect(subject.by_title(*known_titles.map(&:downcase))).to be_empty }
23
+ it { expect(subject.by_title(*known_titles.map(&:upcase))).to be_empty }
24
+
25
+ it { expect(subject.by_title(known_titles_relation)).to be_present }
26
+ it { expect(subject.by_title(known_titles_downcased_relation)).to be_empty }
27
+ it { expect(subject.by_title(known_titles_upcased_relation)).to be_empty }
28
+
29
+ it { expect(subject.by_title(unknown_title)).to be_empty }
30
+ end
31
+
32
+ context "type 'where_lower'" do
33
+ it { expect(subject.by_title_case_insensitive(known_title)).to be_present }
34
+ it { expect(subject.by_title_case_insensitive(known_title.downcase)).to be_present }
35
+ it { expect(subject.by_title_case_insensitive(known_title.upcase)).to be_present }
36
+
37
+ it { expect(subject.by_title_case_insensitive(*known_titles)).to be_present }
38
+ it { expect(subject.by_title_case_insensitive(*known_titles.map(&:downcase))).to be_present }
39
+ it { expect(subject.by_title_case_insensitive(*known_titles.map(&:upcase))).to be_present }
40
+
41
+ it { expect(subject.by_title_case_insensitive(known_titles_relation)).to be_empty }
42
+ it { expect(subject.by_title_case_insensitive(known_titles_downcased_relation)).to be_present }
43
+ it { expect(subject.by_title_case_insensitive(known_titles_upcased_relation)).to be_empty }
44
+
45
+ it { expect(subject.by_title_case_insensitive(unknown_title)).to be_empty }
46
+ end
7
47
  end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ describe ::Trax::Model::Mixins::StiEnum do
3
+ subject{ ::Vehicle }
4
+
5
+ it { expect(::Vehicle.new(:kind => :car).type).to eq "Vehicle::Car"}
6
+ it { expect(::Vehicle.new(:kind => :truck).type).to eq "Vehicle::Truck"}
7
+ end
@@ -3,10 +3,12 @@ describe ::String do
3
3
  let(:product) { ::Product.create(:name => "iMac") }
4
4
  subject{ "#{product.uuid}" }
5
5
 
6
+ it{ expect(subject.uuid).to be_instance_of(::Trax::Model::UUID) }
7
+
6
8
  its(:uuid) { should be_instance_of(::Trax::Model::UUID) }
7
9
 
8
10
  context "when not a uuid length" do
9
11
  let(:truncated_uuid) { subject[0..8] }
10
- it { truncated_uuid.uuid.should be_nil }
12
+ it { expect(truncated_uuid.uuid).to be_nil }
11
13
  end
12
14
  end
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "trax_core", "~> 0.0.80"
21
+ spec.add_dependency "trax_core", "~> 0.0.83"
22
22
  spec.add_dependency "default_value_for", "~> 3.0.0"
23
23
  spec.add_dependency "simple_enum"
24
24
  spec.add_development_dependency "hashie", ">= 3.4.2"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trax_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.97
4
+ version: 0.0.98
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Ayre
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-16 00:00:00.000000000 Z
11
+ date: 2015-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trax_core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.80
19
+ version: 0.0.83
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.80
26
+ version: 0.0.83
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: default_value_for
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -347,6 +347,7 @@ files:
347
347
  - lib/trax/model/mixins/field_scopes.rb
348
348
  - lib/trax/model/mixins/id_scopes.rb
349
349
  - lib/trax/model/mixins/sort_by_scopes.rb
350
+ - lib/trax/model/mixins/sti_enum.rb
350
351
  - lib/trax/model/mti.rb
351
352
  - lib/trax/model/mti/abstract.rb
352
353
  - lib/trax/model/mti/entity.rb
@@ -357,12 +358,12 @@ files:
357
358
  - lib/trax/model/scopes.rb
358
359
  - lib/trax/model/sti.rb
359
360
  - lib/trax/model/sti/attributes.rb
361
+ - lib/trax/model/string_extensions.rb
360
362
  - lib/trax/model/struct_extensions.rb
361
363
  - lib/trax/model/unique_id.rb
362
364
  - lib/trax/model/uuid.rb
363
365
  - lib/trax/model/uuid_array.rb
364
366
  - lib/trax/model/uuid_prefix.rb
365
- - lib/trax/string.rb
366
367
  - lib/trax/validators/boolean_validator.rb
367
368
  - lib/trax/validators/email_validator.rb
368
369
  - lib/trax/validators/enum_attribute_validator.rb
@@ -389,6 +390,7 @@ files:
389
390
  - spec/trax/model/freezable_spec.rb
390
391
  - spec/trax/model/matchable_spec.rb
391
392
  - spec/trax/model/mixins/field_scopes_spec.rb
393
+ - spec/trax/model/mixins/sti_enum_spec.rb
392
394
  - spec/trax/model/registry_spec.rb
393
395
  - spec/trax/model/restorable_spec.rb
394
396
  - spec/trax/model/sti/attributes_spec.rb
@@ -443,6 +445,7 @@ test_files:
443
445
  - spec/trax/model/freezable_spec.rb
444
446
  - spec/trax/model/matchable_spec.rb
445
447
  - spec/trax/model/mixins/field_scopes_spec.rb
448
+ - spec/trax/model/mixins/sti_enum_spec.rb
446
449
  - spec/trax/model/registry_spec.rb
447
450
  - spec/trax/model/restorable_spec.rb
448
451
  - spec/trax/model/sti/attributes_spec.rb
@@ -1,5 +0,0 @@
1
- ::String.class_eval do
2
- def uuid
3
- self.length == 36 ? ::Trax::Model::UUID.new(self) : nil
4
- end
5
- end