trax_model 0.0.93 → 0.0.95

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