lazy_lazer 0.7.2 → 0.8.0

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: 77feb46739d312dbc726ab9291c19b7d1d585a44
4
- data.tar.gz: a9a5ffda84ee0ed882b7944d7a7767e59d8efeb9
3
+ metadata.gz: 55bf334bce0c909389956b65961ebb47509dc3bf
4
+ data.tar.gz: d13f32962f1218f44213c5221488396c2a3de4a5
5
5
  SHA512:
6
- metadata.gz: 8c4ec41ef8145e740c889200494831016cbb543f3fe1231bc93105a477bedcb765c242abc78f3307fabf8e791692da9a071b1dd991056c552c84ea2a216bffbf
7
- data.tar.gz: e067e5cd6cde812fd9731b02ba4a4e5ee98a6a3bd630ae2241ef5d1f35f4d933ab526143b5acb77801deae4e5e5ae16067b9f7f062e12f41e875d3f56085e8ec
6
+ metadata.gz: 48988bc8b686edf6080fc1468d09ac7385cd77eefec05a7d370657d7248e02ce125c4546cae6940f201dc5349e6235783f4020c8a2c35de800ee616e3428de80
7
+ data.tar.gz: e212429d02a6b1de380196d92fab5a79097c32032b9e9fa86edef7e3c4d4fe5a94b5e168ef46c512f92dc86575dc393ea02dc7f66e20db4059c764f9da996ede
data/README.md CHANGED
@@ -15,13 +15,17 @@ class User
15
15
  property :twitter_handle, :nil
16
16
  property :favorite_ice_cream
17
17
 
18
- def try_another_flavor!
18
+ def say_flavor!
19
19
  if exists_locally?(:favorite_ice_cream)
20
20
  puts "#{name} currently likes #{favorite_ice_cream}."
21
21
  else
22
22
  puts "#{name} doesn't have a favorite ice cream flavor yet."
23
23
  end
24
- invalidate(:favorite_ice_cream)
24
+ end
25
+
26
+ def try_another_flavor!
27
+ delete_attribute(:favorite_ice_cream)
28
+ not_fully_loaded!
25
29
  puts "#{name} just tried #{favorite_ice_cream}. They love it!"
26
30
  end
27
31
 
@@ -35,7 +39,7 @@ end
35
39
 
36
40
  user = User.new(name: 'Blinky', creation_time_utc: 1500000000, age: '21')
37
41
 
38
- user.name #=> 'Blinky'
42
+ user.name #=> "Blinky"
39
43
  user.email #=> "unknown@example.com"
40
44
  user.created_at #=> 2017-07-14 03:40:00 +0100
41
45
  user.age #=> 21
@@ -45,9 +49,8 @@ user.favorite_ice_cream #=> "chocolate"
45
49
  user.favorite_ice_cream #=> "chocolate"
46
50
  user.reload.favorite_ice_cream #=> "vanilla"
47
51
 
48
- user.try_another_flavor!
49
- #=> Blinky currently likes vanilla.
50
- #=> Blinky just tried strawberry. They love it!
52
+ user.say_flavor! #=> Blinky currently likes vanilla.
53
+ user.try_another_flavor! #=> Blinky just tried strawberry. They love it!
51
54
  ```
52
55
 
53
56
  <p align="center">
data/lib/lazy_lazer.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  require_relative 'lazy_lazer/errors'
4
6
  require_relative 'lazy_lazer/internal_model'
5
7
  require_relative 'lazy_lazer/key_metadata_store'
@@ -62,8 +64,10 @@ module LazyLazer
62
64
  module InstanceMethods
63
65
  extend Forwardable
64
66
 
65
- def_delegators :@_lazer_model, :to_h, :inspect, :read_attribute, :write_attribute, :fully_loaded?, :fully_loaded!, :not_fully_loaded!, :invalidate, :exists_locally?
66
- private :fully_loaded!, :not_fully_loaded!, :invalidate, :exists_locally?
67
+ def_delegators :@_lazer_model,
68
+ :to_h, :inspect, :read_attribute, :write_attribute, :delete_attribute,
69
+ :fully_loaded?, :fully_loaded!, :not_fully_loaded!, :exists_locally?
70
+ private :fully_loaded!, :not_fully_loaded!, :exists_locally?
67
71
 
68
72
  # Create a new instance of the class from a set of source attributes.
69
73
  # @param attributes [Hash] the model attributes
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module LazyLazer
4
6
  # A delegator for internal operations.
5
7
  class InternalModel
@@ -9,39 +11,35 @@ module LazyLazer
9
11
  def initialize(key_metadata, parent)
10
12
  @key_metadata = key_metadata
11
13
  @parent = parent
12
- @invalidated = Set.new
13
- @source_hash = {}
14
- @cache_hash = {}
14
+ @cache = {}
15
+ @source = {}
16
+ @writethrough = {}
15
17
  @fully_loaded = false
16
18
  end
17
19
 
18
20
  # Converts all unconverted keys and packages them as a hash.
19
21
  # @return [Hash] the converted hash
20
- def to_h
21
- return @cache_hash if fully_loaded?
22
- todo = @key_metadata.keys - @cache_hash.keys
23
- todo.each_with_object(@cache_hash) { |key, cache| cache[key] = load_key_from_source(key) }.dup
22
+ def to_h(strict: false)
23
+ todo = @key_metadata.keys - @cache.keys
24
+ todo.each do |key|
25
+ strict ? load_key_strict(key) : load_key_lenient(key)
26
+ end
27
+ @cache.dup
24
28
  end
25
29
 
26
30
  # @return [String] the string representation of the parent
27
31
  def inspect
28
32
  "#<#{@parent.class.name} (#{@fully_loaded ? 'loaded' : 'unloaded'}): [" + \
29
- @cache_hash.keys.join(', ') + ']>'
33
+ @cache.keys.join(', ') + ']>'
30
34
  end
31
35
 
32
36
  # Whether the key doesn't need to be lazily loaded.
33
37
  # @param key_name [Symbol] the key to check
34
38
  # @return [Boolean] whether the key exists locally.
35
39
  def exists_locally?(key_name)
40
+ return true if @cache.key?(key_name) || @writethrough.key?(key_name)
36
41
  meta = ensure_metadata_exists(key_name)
37
- @source_hash.key?(meta.source_key)
38
- end
39
-
40
- # Mark a key as tainted, forcing a reload on the next lookup.
41
- # @param key_name [Symbol] the key to invalidate
42
- # @return [void]
43
- def invalidate(key_name)
44
- @invalidated.add(key_name)
42
+ @source.key?(meta.source_key)
45
43
  end
46
44
 
47
45
  # Get the value of a key (fetching it from the cache if possible)
@@ -49,12 +47,7 @@ module LazyLazer
49
47
  # @return [Object] the returned value
50
48
  # @raise MissingAttribute if the attribute wasn't found and there isn't a default
51
49
  def read_attribute(key_name)
52
- key_name_sym = key_name.to_sym
53
- if @invalidated.include?(key_name_sym)
54
- @parent.reload
55
- @invalidated.delete(key_name_sym)
56
- end
57
- @cache_hash[key_name_sym] ||= load_key_from_source(key_name_sym)
50
+ @cache.fetch(key_name) { load_key_strict(key_name) }
58
51
  end
59
52
 
60
53
  # Update an attribute.
@@ -62,10 +55,21 @@ module LazyLazer
62
55
  # @param new_value [Object] the new value
63
56
  # @return [Object] the written value
64
57
  def write_attribute(key_name, new_value)
65
- unless @key_metadata.contains?(key_name)
66
- raise ArgumentError, "#{key_name} is not a valid attribute for #{parent}"
67
- end
68
- @cache_hash[key_name] = new_value
58
+ meta = ensure_metadata_exists(key_name)
59
+ @source.delete(meta.source_key)
60
+ @writethrough[key_name] = @cache[key_name] = new_value
61
+ end
62
+
63
+ # Delete an attribute
64
+ # @param key_name [Symbol] the name of the key
65
+ # @return [void]
66
+ def delete_attribute(key_name)
67
+ key_name_sym = key_name.to_sym
68
+ meta = ensure_metadata_exists(key_name_sym)
69
+ @cache.delete(key_name)
70
+ @writethrough.delete(key_name)
71
+ @source.delete(meta.source_key)
72
+ nil
69
73
  end
70
74
 
71
75
  # Mark the model as fully loaded.
@@ -91,7 +95,7 @@ module LazyLazer
91
95
  # @return [void]
92
96
  def verify_required!
93
97
  @key_metadata.required_properties.each do |key_name|
94
- next if @source_hash.key?(@key_metadata.get(key_name).source_key)
98
+ next if @source.key?(@key_metadata.get(key_name).source_key)
95
99
  raise RequiredAttribute, "#{@parent} requires `#{key_name}`"
96
100
  end
97
101
  end
@@ -106,8 +110,8 @@ module LazyLazer
106
110
  # @api private
107
111
  # @param attributes [Hash<Symbol, Object>] the attributes to merge
108
112
  def merge!(attributes)
109
- @cache_hash.clear
110
- @source_hash.merge!(attributes)
113
+ @cache.clear
114
+ @source.merge!(attributes)
111
115
  end
112
116
 
113
117
  private
@@ -116,11 +120,36 @@ module LazyLazer
116
120
  # @param key_name [Symbol] the key name
117
121
  # @return [Object] the returned value
118
122
  # @raise MissingAttribute if the attribute wasn't found and there isn't a default
119
- def load_key_from_source(key_name)
123
+ def load_key_strict(key_name)
120
124
  meta = ensure_metadata_exists(key_name)
121
- ensure_key_is_loaded(meta.source_key, meta.runtime_required?)
122
- return fetch_default(meta.default) unless @source_hash.key?(meta.source_key)
123
- transform_value(@source_hash[meta.source_key], meta.transform)
125
+ reload_if_missing(key_name, meta.source_key)
126
+ if !exists_locally?(key_name) && !meta.default_provided?
127
+ raise MissingAttribute, "`#{meta.source_key} is missing for #{@parent}`"
128
+ end
129
+ parse_key(meta, key_name)
130
+ end
131
+
132
+ # Load the key and apply transformations to it, skipping the cache.
133
+ # @param key_name [Symbol] the key name
134
+ # @return [Object] the returned value
135
+ def load_key_lenient(key_name)
136
+ meta = ensure_metadata_exists(key_name)
137
+ reload_if_missing(key_name, meta.source_key)
138
+ parse_key(meta, key_name)
139
+ end
140
+
141
+ def reload_if_missing(key_name, source_key)
142
+ @parent.reload if !@writethrough.key?(key_name) && !@source.key?(source_key) && !@fully_loaded
143
+ end
144
+
145
+ def parse_key(meta, key_name)
146
+ if @source.key?(meta.source_key)
147
+ @cache[key_name] = transform_value(@source[meta.source_key], meta.transform)
148
+ elsif @writethrough.key?(key_name)
149
+ @cache[key_name] = @writethrough[key_name]
150
+ elsif meta.default_provided?
151
+ @cache[key_name] = fetch_default(meta.default)
152
+ end
124
153
  end
125
154
 
126
155
  # Ensure the metadata is found.
@@ -132,17 +161,6 @@ module LazyLazer
132
161
  raise MissingAttribute, "`#{key_name}` isn't defined for #{@parent}"
133
162
  end
134
163
 
135
- # Reloads the model if a key isn't loaded and possibly errors if the key still isn't there.
136
- # @param source_key [Symbol] the key that should be loaded
137
- # @param runtime_required [Boolean] whether to raise an error if the key is not loaded
138
- # @return [void]
139
- # @raise MissingAttribute if runtime_required is true and the key can't be loaded.
140
- def ensure_key_is_loaded(source_key, runtime_required)
141
- @parent.reload if !@source_hash.key?(source_key) && !@fully_loaded
142
- return if @source_hash.key?(source_key) || !runtime_required
143
- raise MissingAttribute, "`#{source_key} is missing for #{@parent}`"
144
- end
145
-
146
164
  # Apply a transformation to a value.
147
165
  # @param value [Object] a value
148
166
  # @param transform [nil, Proc, Symbol] a transform type
@@ -11,8 +11,8 @@ module LazyLazer
11
11
  # @return [Boolean] whether the key must exist when creating the model
12
12
  attr_writer :required
13
13
 
14
- # @return [Boolean] whether the key must exist when loaded
15
- attr_writer :runtime_required
14
+ # @return [Boolean] whether a default was provided
15
+ attr_writer :default_provided
16
16
 
17
17
  # @return [Proc, Object] the default value or generator
18
18
  attr_accessor :default
@@ -26,7 +26,7 @@ module LazyLazer
26
26
  boolean_options.each_with_object(options) { |sym, hsh| hsh[sym] = true }
27
27
  self.source_key = options[:from] || key_name
28
28
  self.required = !!options[:required]
29
- self.runtime_required = !options.key?(:default) && !options[:nil]
29
+ self.default_provided = options.key?(:default) || options[:nil]
30
30
  self.transform = options[:with]
31
31
  self.default = options[:default]
32
32
  end
@@ -36,9 +36,9 @@ module LazyLazer
36
36
  @required
37
37
  end
38
38
 
39
- # @return [Boolean] whether the key must exist when loaded
40
- def runtime_required?
41
- @runtime_required
39
+ # @return [Boolean] whether no default was provided
40
+ def default_provided?
41
+ @default_provided
42
42
  end
43
43
  end
44
44
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module LazyLazer
4
4
  # The gem's semantic version number.
5
- VERSION = '0.7.2'
5
+ VERSION = '0.8.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazy_lazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avinash Dwarapu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-02 00:00:00.000000000 Z
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler