traitr 0.0.6 → 0.0.7

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: 344b8ed96166aea681f7b1bc1f5d824caf0ae95c
4
- data.tar.gz: 837d71f3b9012e0e8851f70547019e03c95f8196
3
+ metadata.gz: 989c4e5ac888058f57192ebbbc0e21729f574032
4
+ data.tar.gz: 21deba747d0819395e847d96bc73d976e62d2c40
5
5
  SHA512:
6
- metadata.gz: 7b9577c91836a307873d3037ae981990657cb0d141627e8eefa87b125b4f58f7a3aea3d6621ba07b9612eb487fde4489aa3d800aacc90aba70d3b6c6f4418626
7
- data.tar.gz: 70125caef20138cd3c21995a6c47910bb2b8ab7b4ddb5d72528a48140188df6e72679e899181395bc93cae1288d4d6e532f251260c64739e8331e92c345a6006
6
+ metadata.gz: e29befd886a056da95c916aed20ec0bb2933382acaf070d0e3a79341a5434cd023cc72964712ea10cf07b2c196faeb2ebb058f0e35aea1689a09ac58fa79bace
7
+ data.tar.gz: 18165a4221d8f9e44973920a786c4f196240af8cc245f30a0cd4efa30d316eb3ae1517d06fc2e5f3bbe9c31f7522bf40cd4baa5ef6b5d71ddfdb308d8d31cf6e
@@ -12,6 +12,7 @@ module Traitor
12
12
  @trait_cache = {}
13
13
 
14
14
  class << self
15
+ # Reset Traitor entirely by clearing its attributes.
15
16
  def reset!
16
17
  @trait_library = {}
17
18
  @alternate_create_methods = {}
@@ -20,6 +21,10 @@ module Traitor
20
21
  @trait_cache = {}
21
22
  end
22
23
 
24
+ # Hook an object into Traitor. Read GETTING_STARTED.md for assistance.
25
+ #
26
+ # @param [String|Symbol] klass - the class reference.
27
+ # @param [Hash] traits - the hash of traits defined on the Traitor.s
23
28
  def define(klass, **traits)
24
29
  @trait_library[klass] ||= {}
25
30
 
@@ -47,11 +52,20 @@ module Traitor
47
52
  @trait_library[klass].merge!(traits)
48
53
  end
49
54
 
50
- ##
51
55
  # build an instance of an object using the defined traits and attributes.
52
- ##
56
+ #
57
+ # @param [String|Symbol] klass - the class reference symbol to get the library from
58
+ # @param [Array] traits - the list of traits to refer attributes from
59
+ # @param [Hash] attributes - the list of attributes to apply to the specific object.
53
60
  def build(klass, *traits, **attributes)
54
- attributes = get_attributes_from_traits(klass, traits).merge(attributes)
61
+ attrs, concat_attrs = split_attributes(attributes)
62
+ attributes = get_attributes_from_traits(klass, traits).merge(attrs)
63
+ # but add all the separate concatenated attributes into a list.
64
+ concat_attrs.each do |k, v|
65
+ attributes[k] ||= []
66
+ attributes[k] << v
67
+ end
68
+
55
69
  build_kwargs = Traitor::Config.build_kwargs || {}
56
70
 
57
71
  record = if Traitor::Config.build_with_list
@@ -66,10 +80,12 @@ module Traitor
66
80
  record
67
81
  end
68
82
 
69
- ##
70
83
  # build an instance of an object using the defined traits and attributes,
71
- # and then save it.
72
- ##
84
+ # and then save it using the appropriate create method.
85
+ #
86
+ # @param [String|Symbol] klass - the class reference symbol to get the library from
87
+ # @param [Array] traits - the list of traits to refer attributes from
88
+ # @param [Hash] attributes - the list of attributes to apply to the specific object.
73
89
  def create(klass, *traits, **attributes)
74
90
  create_method, create_kwargs = @alternate_create_methods[klass] ||
75
91
  [Traitor::Config.create_method, Traitor::Config.create_kwargs || {}]
@@ -88,6 +104,12 @@ module Traitor
88
104
  record
89
105
  end
90
106
 
107
+ # build an instance of an object using the defined traits and attributes,
108
+ # and then save it using the explicitly referenced create method.
109
+ #
110
+ # @param [String|Symbol] klass - the class reference symbol to get the library from
111
+ # @param [Array] traits - the list of traits to refer attributes from
112
+ # @param [Hash] attributes - the list of attributes to apply to the specific object.
91
113
  def create_using(klass, create_method, *traits, **attributes)
92
114
  old_create_method_kwargs = @alternate_create_methods[klass]
93
115
  @alternate_create_methods[klass] = [create_method, attributes.delete(:create_kwargs) || {}]
@@ -98,6 +120,14 @@ module Traitor
98
120
 
99
121
  private
100
122
 
123
+ # Helper method to call after blocks that are defined on a Traitor.
124
+ #
125
+ # @param [String|Symbol] klass - the class reference for the library.
126
+ # @param [Symbol] trigger - one of the values in BLOCK_KEYS, to check for in
127
+ # the block library.
128
+ # @param [Object] record - the object the trigger will yield.
129
+ # @param [Array] traits - A list of traits that the object was built with,
130
+ # to find any triggers specific to the referenced traits.
101
131
  def call_blocks(klass, trigger, record, *traits)
102
132
  return unless @block_library[klass]
103
133
  [].tap do |blocks|
@@ -110,12 +140,26 @@ module Traitor
110
140
  end.compact.each { |block| block.call(record) }
111
141
  end
112
142
 
143
+ # given a string/symbol, return the class based on that symbol.
144
+ # e.g. :some_class -> SomeClass
145
+ #
146
+ # @param [String|Symbol] klass - the class symbol/reference
147
+ # @return [Object] the class
113
148
  def convert_to_class(klass)
114
149
  @class_cache[klass] ||= Object.const_get(camelize(klass))
115
150
  rescue NameError
116
151
  raise Traitor::Error.new("Tried to create a #{camelize(klass)}, but it does not exist!")
117
152
  end
118
153
 
154
+ # Given a list of traits, return the compiled hash of attributes with calculated values.
155
+ # Will properly call lambdas/procs to get their value and will concatenate attributes
156
+ # prefixed with a '+'.
157
+ #
158
+ # Will always start from the :default_traits trait, if it exists.
159
+ #
160
+ # @param [String|Symbol] klass - the class reference to get the list of traits from the library.
161
+ # @param [Array[String|Symbol]] traits - a list of traits to refer to in the library.
162
+ # @return [Hash] The calculated attributes hash.
119
163
  def get_attributes_from_traits(klass, traits)
120
164
  # we only call this method when the klass has been converted to a key inside create
121
165
  return {} unless library = @trait_library[klass]
@@ -124,23 +168,66 @@ module Traitor
124
168
 
125
169
  cache_key = klass.to_s + ':' + traits.join(':')
126
170
  @trait_cache[cache_key] ||= {}.tap do |attributes|
127
- traits.each { |trait| attributes.merge!(library[trait] || {}) }
171
+ traits.each do |trait|
172
+ # pull out concatenating attributes into their own list
173
+ attrs, concat_attrs = split_attributes(library[trait] || {})
174
+
175
+ # raw merge in the standard attributes...
176
+ attributes.merge!(attrs)
177
+
178
+ # but add all the separate concatenated attributes into a list.
179
+ concat_attrs.each do |k, v|
180
+ attributes[k] ||= []
181
+ attributes[k] << v
182
+ end
183
+ end
128
184
  end
129
185
 
130
186
  # use late resolution on lambda values by calling them here as part of constructing a new hash
131
187
  Hash[
132
188
  @trait_cache[cache_key].map do |attribute, value|
133
- [attribute, value.is_a?(Proc) && ![:after_build, :after_create].include?(attribute) ? value.call : value]
189
+ [attribute, calculate_value(value)]
134
190
  end
135
191
  ]
136
192
  end
137
193
 
194
+ # Split a hash into two hashes, the first being a list of all values that do
195
+ # not begin with '+', the latter being a list of all values that do.
196
+ #
197
+ # @param [Hash] attributes
198
+ # @return [Array[Hash, Hash]] The split hash.
199
+ def split_attributes(attributes)
200
+ attributes.reduce([{}, {}]) do |memo, attr_val|
201
+ attribute, value = attr_val
202
+ if attribute.to_s.start_with?('+')
203
+ memo[1][attribute[1..-1].to_sym] = value
204
+ else
205
+ memo[0][attribute] = value
206
+ end
207
+ memo
208
+ end
209
+ end
210
+
211
+ # Given the value reference from an attribute, call it if callable, or map it
212
+ # if appropriate, or return it as-is.
213
+ #
214
+ # @param [Mixed] v - the value from an attribute. can be Proc, Array, or "value"
215
+ # @return the calculated value.
216
+ def calculate_value(v)
217
+ return v.call if v.is_a?(Proc)
218
+ return v.map { |sv| calculate_value(sv) } if v.is_a?(Array)
219
+ v
220
+ end
221
+
222
+ # simplifed version of ActiveRecord camelize.
223
+ # used to convert a generic class name (in)
224
+ #
225
+ # @param [String|Symbol] term - the term to symbolize
226
+ # @return [String] the camel-cased version of the term, to be retrieved as a const.
138
227
  def camelize(term)
139
- string = term.to_s
140
- string[0] = string[0].upcase
141
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
142
- string.gsub!('/'.freeze, '::'.freeze)
143
- string
228
+ term.to_s.capitalize
229
+ .gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
230
+ .gsub('/'.freeze, '::'.freeze)
144
231
  end
145
232
  end
146
233
  end
@@ -12,29 +12,33 @@ module Traitor
12
12
  def configure_for_rails!
13
13
  @create_method = :save
14
14
  @create_kwargs = { validate: false }
15
- @build_kwargs = { without_protection: true }
15
+ @build_kwargs = { without_protection: true }
16
16
  end
17
17
 
18
18
  def configure_safe_for_rails!
19
19
  @create_method = :save
20
20
  @create_kwargs = {}
21
- @build_kwargs = {}
21
+ @build_kwargs = {}
22
22
  end
23
23
 
24
+ # Undefine all configuration values.
24
25
  def reset!
25
- @create_method = nil
26
- @create_kwargs = {}
27
- @build_kwargs = {}
26
+ @create_method = nil
27
+ @create_kwargs = {}
28
+ @build_kwargs = {}
28
29
  @build_with_list = false
29
- @no_callbacks = false
30
+ @no_callbacks = false
30
31
  end
31
32
 
33
+ # Temporarily store the old configuration, so the config can be modified and
34
+ # then later restored.
32
35
  def stash!
33
36
  @old_config = Hash[
34
37
  self.instance_variables.map { |att| [att, self.instance_variable_get(att)] }
35
38
  ]
36
39
  end
37
40
 
41
+ # After calling #stash!, call this to restore the stashed config.
38
42
  def restore!
39
43
  return unless @old_config
40
44
  @old_config.each { |att, val| self.instance_variable_set(att, val) }
@@ -29,6 +29,7 @@ module Traitor
29
29
  self.class.connection.execute(insert_sql)
30
30
  id = self.maximum(pk)
31
31
  self.send(:"#{pk}=", id)
32
+ self.instance_variable_set(:@new_record, false)
32
33
  self.clear_changes_information
33
34
  end
34
35
 
@@ -43,6 +44,7 @@ module Traitor
43
44
  pk = self.class.primary_key
44
45
  id = conn.execute("SELECT last_insert_rowid() AS id")[0]['id']
45
46
  self.send(:"#{pk}=", id)
47
+ self.instance_variable_set(:@new_record, false)
46
48
  self.clear_changes_information
47
49
  end
48
50
 
@@ -58,7 +60,16 @@ module Traitor
58
60
  end.to_sql
59
61
 
60
62
  # return and assign everything to gather values created/modified by db triggers
61
- self.attributes = self.class.connection.execute(insert_sql + " RETURNING *")[0]
63
+ result = self.class.connection.execute(insert_sql + " RETURNING *")
64
+
65
+ # reassign the write values back to the object, in case there are DB triggers.
66
+ result.to_a.first.each do |column_name, serialized_value|
67
+ column = self.column_for_attribute(column_name)
68
+ deserialized_value = column.type_cast_from_database(serialized_value)
69
+ self["#{column_name}"] = deserialized_value
70
+ end
71
+
72
+ # mark the instance as having been saved.
62
73
  self.instance_variable_set(:@new_record, false)
63
74
  self.clear_changes_information
64
75
  end
@@ -3,6 +3,7 @@ require 'rspec'
3
3
  module Traitor
4
4
  module Helpers
5
5
  class RSpec
6
+ # define the metadata keys to override traitor configs on a per-example basis.
6
7
  def self.configure!
7
8
  ::RSpec.configure do |config|
8
9
  config.around(:example) do |example|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traitr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Lome
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-29 00:00:00.000000000 Z
11
+ date: 2016-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler