traitr 0.0.6 → 0.0.7

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