trax_model 0.0.97 → 0.0.98

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