trax_model 0.0.93 → 0.0.95

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: f90a589c224b12e3d814f1d0000f97ae92abf3a0
4
- data.tar.gz: 3805c04cfb881d04c38bbeddc52ff028250cd7b1
3
+ metadata.gz: 2e9c4e45cdc76295880d1e087727ec1414889bdc
4
+ data.tar.gz: eb26cfa1732eaa1e2ba69b3b5448e71e8c229837
5
5
  SHA512:
6
- metadata.gz: df15b25f69f3dd4ab7a5bf1ae9b67ff16a9868a64812e6ccfad1190758a0af053aa77ce5e0edf9c226fb5246315b675242202714ca08138a38a27863e2808972
7
- data.tar.gz: 483dace95c58f209f3474258263b4b18d5f6f4cf8ad11c00ca4290a061f0217856aeab45c992ca819447136bd8aa558e302229bc3aa127b755efb8c14a79e575
6
+ metadata.gz: fc5388aaf9d9a351bc4412357e4d7d2b01ccac35934ac02d9b193aa8eb24814f852e482efa6ee455318c8fe4129c65fda97e2d2e11b19fd8a4a943372eec0dab
7
+ data.tar.gz: 0847884f74518a4fe603b92d2e2366b08218ccf7a56a07d4ae057f5ebc783d952fefcfe532de86117fd7e02656e9b682bab9ee463aa94b5f40e8e30c15ecb696
@@ -22,6 +22,10 @@ module Trax
22
22
  attribute(*args, type: :enum, **options, &block)
23
23
  end
24
24
 
25
+ def integer(*args, **options, &block)
26
+ attribute(*args, type: :integer, **options, &block)
27
+ end
28
+
25
29
  def string(*args, **options, &block)
26
30
  attribute(*args, :type => :string, **options, &block)
27
31
  end
@@ -0,0 +1,33 @@
1
+ require 'hashie/extensions/ignore_undeclared'
2
+
3
+ module Trax
4
+ module Model
5
+ module Attributes
6
+ module Types
7
+ class Integer < ::Trax::Model::Attributes::Type
8
+ def self.define_attribute(klass, attribute_name, **options, &block)
9
+ klass_name = "#{klass.fields_module.name.underscore}/#{attribute_name}".camelize
10
+ attribute_klass = if options.key?(:class_name)
11
+ options[:class_name].constantize
12
+ else
13
+ ::Trax::Core::NamedClass.new(klass_name, Value, :parent_definition => klass, &block)
14
+ end
15
+
16
+ klass.attribute(attribute_name, typecaster_klass.new)
17
+ klass.default_value_for(attribute_name) { options[:default] } if options.key?(:default)
18
+ end
19
+
20
+ class Value < ::Trax::Model::Attributes::Value
21
+ def self.type; :integer end;
22
+ end
23
+
24
+ class TypeCaster < ::ActiveRecord::Type::Integer
25
+ end
26
+
27
+ self.value_klass = ::Trax::Model::Attributes::Types::Integer::Value
28
+ self.typecaster_klass = ::Trax::Model::Attributes::Types::Integer::TypeCaster
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -38,6 +38,7 @@ module Trax
38
38
  klass.attribute(attribute_name, typecaster_klass.new(target_klass: attribute_klass))
39
39
  klass.validates(attribute_name, :json_attribute => true) unless options.key?(:validate) && !options[:validate]
40
40
  klass.default_value_for(attribute_name) { {} }
41
+ define_model_accessors(klass, attribute_name, attribute_klass, options[:model_accessors]) if options.key?(:model_accessors) && options[:model_accessors]
41
42
  end
42
43
 
43
44
  class Value < ::Trax::Model::Struct
@@ -72,6 +73,36 @@ module Trax
72
73
 
73
74
  self.value_klass = ::Trax::Model::Attributes::Types::Json::Value
74
75
  self.typecaster_klass = ::Trax::Model::Attributes::Types::Json::TypeCaster
76
+
77
+ private
78
+
79
+ def self.define_model_accessors(model, attribute_name, struct_attribute, option_value)
80
+ properties_to_define = if [ true ].include?(option_value)
81
+ struct_attribute.properties.to_a
82
+ elsif option_value.is_a(Hash) && option_value.has_key?(:only)
83
+ struct_attribute.properties.to_a.only(*option_value[:only])
84
+ elsif option_value.is_a(Hash) && option_value.has_key?(:except)
85
+ struct_attribute.properties.to_a.except(*option_value[:except])
86
+ elsif option_value.is_a(Array)
87
+ struct_attribute.properties.to_a.only(*option_value[:only])
88
+ else
89
+ raise Trax::Model::Errors::InvalidOption.new(
90
+ :option => :model_accessors,
91
+ :valid_choices => ["true", "array of properties", "hash with :only or :except keys"]
92
+ )
93
+ end
94
+
95
+ properties_to_define.each do |_property|
96
+ getter_method, setter_method = _property.to_sym, :"#{_property}="
97
+
98
+ model.__send__(:define_method, setter_method) do |val|
99
+ self[attribute_name] = {} unless self[attribute_name]
100
+ self.__send__(attribute_name).__send__(setter_method, val)
101
+ end
102
+
103
+ model.delegate(getter_method, :to => attribute_name)
104
+ end
105
+ end
75
106
  end
76
107
  end
77
108
  end
@@ -3,6 +3,15 @@ require 'trax/core/errors'
3
3
  module Trax
4
4
  module Model
5
5
  module Errors
6
+ class InvalidOption < ::Trax::Core::Errors::Base
7
+ argument :option
8
+ argument :valid_options
9
+
10
+ message {
11
+ "Invalid option #{option}: must instead be one of #{valid_options.join(", ")}"
12
+ }
13
+ end
14
+
6
15
  class MixinNotRegistered < ::Trax::Core::Errors::Base
7
16
  argument :mixin
8
17
  argument :model
@@ -11,6 +11,8 @@ module Trax
11
11
  include ::Hashie::Extensions::Dash::PropertyTranslation
12
12
  include ::ActiveModel::Validations
13
13
 
14
+ attr_reader :record
15
+
14
16
  # note that we must explicitly set default or blank values for all properties.
15
17
  # It defeats the whole purpose of being a 'struct'
16
18
  # if we fail to do so, and it makes our data far more error prone
@@ -19,6 +21,7 @@ module Trax
19
21
  :string_property => "",
20
22
  :struct_property => {},
21
23
  :enum_property => nil,
24
+ :integer_property => nil
22
25
  }.with_indifferent_access.freeze
23
26
 
24
27
  def self.fields_module
@@ -72,6 +75,16 @@ module Trax
72
75
  coerce_key(name.to_sym, enum_klass)
73
76
  end
74
77
 
78
+ def self.integer_property(name, *args, **options, &block)
79
+ name = name.is_a?(Symbol) ? name.to_s : name
80
+ klass_name = "#{fields_module.name.underscore}/#{name}".camelize
81
+ integer_klass = ::Trax::Core::NamedClass.new(klass_name, ::Trax::Model::Attributes::Types::Integer::Value, :parent_definition => self, &block)
82
+ options[:default] = options.key?(:default) ? options[:default] : DEFAULT_VALUES_FOR_PROPERTY_TYPES[__method__]
83
+ define_where_scopes_for_property(name, integer_klass) if options.key?(:define_scopes) && options[:define_scopes]
84
+ property(name.to_sym, *args, **options)
85
+ coerce_key(name.to_sym, Integer)
86
+ end
87
+
75
88
  def self.to_schema
76
89
  ::Trax::Core::Definition.new(
77
90
  :source => self.name,
@@ -98,6 +111,7 @@ module Trax
98
111
  alias :enum :enum_property
99
112
  alias :struct :struct_property
100
113
  alias :string :string_property
114
+ alias :integer :integer_property
101
115
  end
102
116
 
103
117
  def value
@@ -5,6 +5,8 @@ class JsonAttributeValidator < ActiveModel::EachValidator
5
5
  def validate_each(object, attribute, value)
6
6
  json_attribute = object.class.fields_module[attribute]
7
7
 
8
+ value.instance_variable_set("@record", object)
9
+
8
10
  unless value.is_a?(json_attribute) && value.valid?
9
11
  if value.is_a?(json_attribute)
10
12
  value.errors.messages.each_pair do |k,v|
@@ -1,3 +1,3 @@
1
1
  module TraxModel
2
- VERSION = '0.0.93'
2
+ VERSION = '0.0.95'
3
3
  end
@@ -54,6 +54,8 @@ module Products
54
54
  define :mens_11, 6
55
55
  define :mens_12, 7
56
56
  end
57
+
58
+ integer :in_stock_quantity, :default => 0
57
59
  end
58
60
  end
59
61
  end
@@ -7,8 +7,8 @@ module Ecommerce
7
7
 
8
8
  mixins :unique_id => { :uuid_prefix => "c2" }
9
9
 
10
- belongs_to :user, :class_name => "Ecommerce::User"
11
- belongs_to :product, :class_name => "Ecommerce::Product"
10
+ # belongs_to :user, :class_name => "Ecommerce::User"
11
+ # belongs_to :product, :class_name => "Ecommerce::Product"
12
12
  end
13
13
 
14
14
  class ShippingAttributes < ::Ecommerce::ProductAttributeSet
@@ -16,10 +16,11 @@ module Ecommerce
16
16
  include ::Trax::Model::Attributes::Mixin
17
17
 
18
18
  define_attributes do
19
- struct :specifics, :validate => true do
20
- string :cost, :default => 0
21
-
22
- validates(:cost, :length => {:minimum => 10})
19
+ struct :specifics, :model_accessors => true, :validate => true do
20
+ integer :cost, :default => 0
21
+ validates(:cost, :numericality => {:greater_than => 0})
22
+ integer :tax
23
+ string :delivery_time
23
24
 
24
25
  enum :service do
25
26
  define :usps, 1
@@ -27,11 +28,12 @@ module Ecommerce
27
28
  end
28
29
 
29
30
  struct :dimensions, :validate => true do
30
- string :length
31
- string :width
32
- string :height
31
+ integer :length
32
+ integer :width
33
+ integer :height
34
+ string :packaging_box
33
35
 
34
- validates(:length, :length => {:maximum => 10})
36
+ validates(:length, :numericality => {:greater_than => 0})
35
37
  end
36
38
  end
37
39
  end
@@ -79,6 +81,10 @@ module Ecommerce
79
81
  string :primary_utility, :default => "Skateboarding", :define_scopes => true
80
82
  string :sole_material
81
83
  boolean :has_shoelaces, :define_scopes => true
84
+ integer :in_stock_quantity, :default => 0
85
+ integer :number_of_sales, :default => 0
86
+ integer :cost
87
+ integer :price
82
88
 
83
89
  enum :color, :default => :blue, :define_scopes => false do
84
90
  define :red, 1
@@ -96,6 +102,10 @@ module Ecommerce
96
102
  define :mens_11, 6
97
103
  define :mens_12, 7
98
104
  end
105
+
106
+ def total_profit
107
+ number_of_sales * (price - cost)
108
+ end
99
109
  end
100
110
  end
101
111
  end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::Trax::Model::Attributes::Types::Integer do
4
+ subject{ ::Products::MensShoes::Fields::Quantity }
5
+
6
+ context "model" do
7
+ subject { ::Products::MensShoes.new(:in_stock_quantity => 1) }
8
+
9
+ it { expect(subject.in_stock_quantity).to eq 1 }
10
+
11
+ context "non integer value" do
12
+ let(:test_subject) { ::Products::MensShoes.new(:in_stock_quantity => "blah") }
13
+ it { expect(test_subject.in_stock_quantity).to eq 0 }
14
+ end
15
+ end
16
+
17
+ context "struct attribute", :postgres => true do
18
+ subject { ::Ecommerce::Products::MensShoes.new(:in_stock_quantity => 1) }
19
+ end
20
+ end
@@ -6,6 +6,30 @@ describe ::Trax::Model::Attributes::Types::Json, :postgres => true do
6
6
  it { expect(subject.new.primary_utility).to eq "Skateboarding" }
7
7
  it { expect(subject.new.sole_material).to eq "" }
8
8
 
9
+ context "attribute definition" do
10
+ subject { ::Ecommerce::ShippingAttributes.new }
11
+
12
+ context "model_accessors (delegators)" do
13
+ it {
14
+ ::Ecommerce::ShippingAttributes.fields[:specifics].properties.each do |_property|
15
+ expect(subject.__send__(_property)).to eq subject.specifics.__send__(_property)
16
+ end
17
+ }
18
+
19
+ context "initializing model and setting delegated attributes directly", :delegated_attributes => { :cost => 5, :tax => 5, :delivery_time => "5 days" } do
20
+ self::DELEGATED_ATTRIBUTES = { :cost => 5, :tax => 5, :delivery_time => "5 days" }
21
+
22
+ subject{ |example| ::Ecommerce::ShippingAttributes.new(example.example_group::DELEGATED_ATTRIBUTES) }
23
+
24
+ self::DELEGATED_ATTRIBUTES.each_pair do |k,v|
25
+ it "#{k} should be set" do
26
+ expect(subject.specifics.__send__(k)).to eq v
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
9
33
  context "model" do
10
34
  subject { ::Ecommerce::Products::MensShoes.new }
11
35
 
@@ -13,6 +37,13 @@ describe ::Trax::Model::Attributes::Types::Json, :postgres => true do
13
37
  it { subject.custom_fields.size.should eq :mens_9 }
14
38
  end
15
39
 
40
+ context "instance method defined within struct" do
41
+ subject { ::Ecommerce::Products::MensShoes.new(:custom_fields => {:cost => 10, :price => 20, :number_of_sales => 5}) }
42
+ it {
43
+ expect(subject.custom_fields.total_profit).to eq 50
44
+ }
45
+ end
46
+
16
47
  context "search scopes" do
17
48
  context "enum property" do
18
49
  [ :mens_6, :mens_7, :mens_10 ].each_with_index do |enum_name|
@@ -68,7 +99,7 @@ describe ::Trax::Model::Attributes::Types::Json, :postgres => true do
68
99
  end
69
100
 
70
101
  context "valid struct attribute" do
71
- let(:subject_attributes) { { :specifics => { :cost => "asdasdasdasdasd", :dimensions => {}}} }
102
+ let(:subject_attributes) { { :specifics => { :cost => 1, :dimensions => { :length => 10 }}} }
72
103
 
73
104
  it { expect(subject.valid?).to eq true }
74
105
  end
@@ -4,7 +4,7 @@ describe ::Trax::Model::Attributes do
4
4
  subject{ described_class }
5
5
 
6
6
  describe ".register_attribute_type" do
7
- [:array, :boolean, :enum, :json, :string, :uuid_array].each do |type|
7
+ [:array, :boolean, :enum, :integer, :json, :string, :uuid_array].each do |type|
8
8
  it "registers attribute type #{type}" do
9
9
  expect(subject.config.attribute_types).to have_key(type)
10
10
  end
@@ -4,6 +4,6 @@ describe ::Trax::Model::Errors do
4
4
  subject{ Trax::Model::Errors::InvalidPrefix }
5
5
 
6
6
  it do
7
- expect{subject.new(:blah => "blah")}.to raise_error
7
+ expect{subject.new(:blah => "blah")}.to raise_error(NoMethodError)
8
8
  end
9
9
  end
@@ -12,7 +12,7 @@ describe ::Trax::Model::Freezable do
12
12
  subject.title = "somethingelse"
13
13
  subject.save
14
14
 
15
- subject.errors.messages[:title].should include("Cannot be modified")
15
+ expect(subject.errors.messages[:title]).to include("Cannot be modified")
16
16
  end
17
17
  end
18
18
  end
@@ -8,13 +8,13 @@ describe ::Trax::Model::Restorable do
8
8
  context "when destroyed" do
9
9
  it "should soft delete" do
10
10
  subject.destroy
11
- subject.deleted.should be true
11
+ expect(subject.deleted).to be true
12
12
  end
13
13
 
14
14
  it "should be restorable" do
15
15
  subject.destroy
16
16
  subject.restore
17
- subject.deleted.should be false
17
+ expect(subject.deleted).to be false
18
18
  end
19
19
  end
20
20
 
@@ -26,18 +26,18 @@ describe ::Trax::Model::Restorable do
26
26
 
27
27
  it do
28
28
  subject
29
- Message.all.pluck(:id).should include(subject.id)
29
+ expect(Message.all.pluck(:id)).to include(subject.id)
30
30
  end
31
31
 
32
32
  it do
33
33
  subject.destroy
34
- Message.all.pluck(:id).should_not include(subject.id)
34
+ expect(Message.all.pluck(:id)).to_not include(subject.id)
35
35
  end
36
36
  end
37
37
 
38
38
  it ".by_is_deleted" do
39
39
  subject.destroy
40
- Message.by_is_deleted.pluck(:id).should include(subject.id)
40
+ expect(Message.by_is_deleted.pluck(:id)).to include(subject.id)
41
41
  end
42
42
  end
43
43
  end
@@ -7,8 +7,10 @@ describe ::Trax::Model::UniqueId do
7
7
  describe "uuid_prefix" do
8
8
  context "bad prefixes" do
9
9
  ["a", "1p", "a1a", "bl", "1", "111"].each do |prefix|
10
+ let(:test_subject) { ::Trax::Core::NamedClass.new("Product::Asdfg#{prefix}", subject)}
11
+
10
12
  it "raises error when passed hex incompatible prefix #{prefix}" do
11
- expect{ subject.trax_defaults.uuid_prefix=(prefix) }.to raise_error
13
+ expect{ test_subject.mixins(:unique_id => {:uuid_prefix => prefix})}.to raise_error(::Trax::Core::Errors::ConfigurationError)
12
14
  end
13
15
  end
14
16
  end
@@ -6,7 +6,7 @@ describe ::EmailValidator do
6
6
  ["bill@somewhere", "bill", "@initech.com", "!!!@!!!.com"].each do |bad_email|
7
7
  it "should fail validation for #{bad_email}" do
8
8
  widget = ::Widget.create(:email_address => bad_email)
9
- widget.valid?.should eq false
9
+ expect(widget.valid?).to eq false
10
10
  end
11
11
  end
12
12
  end
@@ -7,7 +7,7 @@ describe ::SubdomainValidator do
7
7
  ["bad!", "-asdasd", "www", "ac"].each do |bad_subdomain|
8
8
  it "should fail validation for #{bad_subdomain}" do
9
9
  widget = ::Widget.create(:subdomain => bad_subdomain)
10
- widget.valid?.should eq false
10
+ expect(widget.valid?).to eq false
11
11
  end
12
12
  end
13
13
  end
data/trax_model.gemspec CHANGED
@@ -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.74"
21
+ spec.add_dependency "trax_core", "~> 0.0.76"
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.93
4
+ version: 0.0.95
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-09-02 00:00:00.000000000 Z
11
+ date: 2015-09-09 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.74
19
+ version: 0.0.76
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.74
26
+ version: 0.0.76
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: default_value_for
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -318,6 +318,7 @@ files:
318
318
  - lib/trax/model/attributes/types/array.rb
319
319
  - lib/trax/model/attributes/types/boolean.rb
320
320
  - lib/trax/model/attributes/types/enum.rb
321
+ - lib/trax/model/attributes/types/integer.rb
321
322
  - lib/trax/model/attributes/types/json.rb
322
323
  - lib/trax/model/attributes/types/string.rb
323
324
  - lib/trax/model/attributes/types/uuid_array.rb
@@ -366,6 +367,7 @@ files:
366
367
  - spec/support/pg/models.rb
367
368
  - spec/trax/model/attributes/fields_spec.rb
368
369
  - spec/trax/model/attributes/types/enum_spec.rb
370
+ - spec/trax/model/attributes/types/integer_spec.rb
369
371
  - spec/trax/model/attributes/types/json_spec.rb
370
372
  - spec/trax/model/attributes_spec.rb
371
373
  - spec/trax/model/errors_spec.rb
@@ -419,6 +421,7 @@ test_files:
419
421
  - spec/support/pg/models.rb
420
422
  - spec/trax/model/attributes/fields_spec.rb
421
423
  - spec/trax/model/attributes/types/enum_spec.rb
424
+ - spec/trax/model/attributes/types/integer_spec.rb
422
425
  - spec/trax/model/attributes/types/json_spec.rb
423
426
  - spec/trax/model/attributes_spec.rb
424
427
  - spec/trax/model/errors_spec.rb