lazy_lazer 0.2.0 → 0.3.0
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 +4 -4
- data/.rubocop.yml +2 -0
- data/README.md +17 -26
- data/lib/lazy_lazer/version.rb +1 -1
- data/lib/lazy_lazer.rb +96 -43
- metadata +2 -3
- data/lib/lazy_lazer/utilities.rb +0 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a24047c46cd370e8fb1d092129dacb1816f50ebc
|
|
4
|
+
data.tar.gz: 072b5a1b15857682e4317eb8f275685462199e3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de1fb716edf702586d593c085256d984ee3bd8cf0be8811381ac1d6eec10451146fa16f0e88f89a9561ad54d2242a255fc5351c1c6f0d961a2b01dc0e1c62f2d
|
|
7
|
+
data.tar.gz: ada536d3a04d7ce62beff22a4e2afb1d1cf38bf1aa51cae3f08e1e2bed9f2848cecb42b5af8d047c4cea6a2260864749ee9ac5975748f525bbc51c72f29749c8
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
**lazy lazer**
|
|
2
2
|
|
|
3
3
|
**features**:
|
|
4
|
-
- simple codebase (~
|
|
4
|
+
- simple codebase (~110 lines of code, ~200 lines of tests)
|
|
5
5
|
- doesn't inherit all of the Hash and Enumerable cruft
|
|
6
6
|
- super lazy, doesn't even parse attributes until it's necessary
|
|
7
7
|
|
|
@@ -9,35 +9,26 @@
|
|
|
9
9
|
class User
|
|
10
10
|
include LazyLazer
|
|
11
11
|
|
|
12
|
-
# User.new(first_name: 'John') #=> Error: missing `id`
|
|
13
|
-
# User.new(id: 1).id? #=> true
|
|
14
12
|
property :id, required: true
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# user.email #=> nil
|
|
18
|
-
property :email, default: nil
|
|
19
|
-
|
|
20
|
-
# user = User.new(id: 1)
|
|
21
|
-
# user.language #=> :en_US
|
|
22
|
-
property :language, default: :en_US
|
|
23
|
-
|
|
24
|
-
# user = User.new(id: 1, first_name: 'John')
|
|
25
|
-
# user.name #=> 'John'
|
|
26
|
-
# user.first_name #=> NoMethodError: ...
|
|
27
|
-
property :last_name, default: -> { %w[Doe Bloggs Hansen].sample }
|
|
28
|
-
|
|
29
|
-
# user = User.new(id: 1, created_at: 1502834161)
|
|
30
|
-
# user.created_at #=> 2017-08-15 22:56:13 +0100
|
|
31
|
-
property :created_at, with: ->(time) { Time.at(time) }
|
|
32
|
-
|
|
33
|
-
# user = User.new(id: 1, age: '45')
|
|
34
|
-
# user.age #=> 45
|
|
13
|
+
property :email, default: 'unknown@example.com'
|
|
14
|
+
property :created_at, from: 'creation_time_utc', with: ->(time) { Time.at(time) }
|
|
35
15
|
property :age, with: :to_i
|
|
36
16
|
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
property :favorite_ice_cream
|
|
18
|
+
|
|
19
|
+
def lazer_reload
|
|
39
20
|
self.fully_loaded = true # mark model as fully updated
|
|
40
|
-
|
|
21
|
+
{ favorite_ice_cream: %w[vanilla strawberry chocolate].sample }
|
|
41
22
|
end
|
|
42
23
|
end
|
|
24
|
+
|
|
25
|
+
user = User.new(id: 152, creation_time_utc: 1500000000, age: '21')
|
|
26
|
+
|
|
27
|
+
user.id #=> 152
|
|
28
|
+
user.email #=> "unknown@example.com"
|
|
29
|
+
user.created_at #=> 2017-07-14 03:40:00 +0100
|
|
30
|
+
user.age #=> 21
|
|
31
|
+
|
|
32
|
+
user.favorite_ice_cream #=> "chocolate"
|
|
33
|
+
user.reload.favorite_ice_cream #=> "vanilla"
|
|
43
34
|
```
|
data/lib/lazy_lazer/version.rb
CHANGED
data/lib/lazy_lazer.rb
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'lazy_lazer/version'
|
|
4
4
|
require_relative 'lazy_lazer/errors'
|
|
5
|
-
require_relative 'lazy_lazer/utilities'
|
|
6
5
|
|
|
7
|
-
# LazyLazer
|
|
6
|
+
# LazyLazer
|
|
8
7
|
module LazyLazer
|
|
9
8
|
# Hook into `include LazyLazer`.
|
|
10
9
|
# @param [Module] base the object to include the methods in
|
|
@@ -12,8 +11,22 @@ module LazyLazer
|
|
|
12
11
|
def self.included(base)
|
|
13
12
|
base.extend(ClassMethods)
|
|
14
13
|
base.include(InstanceMethods)
|
|
15
|
-
base.instance_variable_set(
|
|
16
|
-
|
|
14
|
+
base.instance_variable_set(
|
|
15
|
+
:@lazer_metadata,
|
|
16
|
+
properties: [],
|
|
17
|
+
required: {},
|
|
18
|
+
default: {},
|
|
19
|
+
from: {},
|
|
20
|
+
with: {}
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Get the source key from an instance
|
|
25
|
+
# @param [Object] instance the instance
|
|
26
|
+
# @param [Symbol] key the property key
|
|
27
|
+
# @return [Symbol] the source key if found or the passed key if not found
|
|
28
|
+
def self.source_key(instance, key)
|
|
29
|
+
instance.class.lazer_metadata[:from].fetch(key, key)
|
|
17
30
|
end
|
|
18
31
|
|
|
19
32
|
# The methods to extend the class with.
|
|
@@ -22,78 +35,118 @@ module LazyLazer
|
|
|
22
35
|
# @param [Class] klass the subclass
|
|
23
36
|
# @return [void]
|
|
24
37
|
def inherited(klass)
|
|
25
|
-
klass.instance_variable_set(:@
|
|
26
|
-
klass.instance_variable_set(:@_lazer_required_properties, @_lazer_required_properties)
|
|
38
|
+
klass.instance_variable_set(:@lazer_metadata, @lazer_metadata)
|
|
27
39
|
end
|
|
28
40
|
|
|
29
|
-
# @return [Hash<Symbol, Hash>]
|
|
30
|
-
def
|
|
31
|
-
@
|
|
41
|
+
# @return [Hash<Symbol, Hash>] the lazer configuration for this model
|
|
42
|
+
def lazer_metadata
|
|
43
|
+
@lazer_metadata
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
# Define a property.
|
|
35
47
|
# @param [Symbol] name the name of the property method
|
|
36
48
|
# @param [Hash] options the options to create the property with
|
|
37
|
-
# @option options [Boolean] :required (false) whether existence of this
|
|
38
|
-
#
|
|
39
|
-
# @option options [
|
|
40
|
-
#
|
|
41
|
-
# @option options [
|
|
42
|
-
#
|
|
43
|
-
# @
|
|
44
|
-
# to the value of the key
|
|
49
|
+
# @option options [Boolean] :required (false) whether existence of this property should be
|
|
50
|
+
# checked on model creation
|
|
51
|
+
# @option options [Object, Proc] :default the default value to return if not provided
|
|
52
|
+
# @option options [Symbol] :from the key in the source object to get the property from
|
|
53
|
+
# @option options [Proc, Symbol] :with an optional transformation to apply to the value
|
|
54
|
+
# @note both :required and :default can't be provided
|
|
55
|
+
# @return [Symbol] the name of the created property
|
|
45
56
|
def property(name, **options)
|
|
57
|
+
raise 'both :required and :default cannot be given' if options[:required] && options[:default]
|
|
46
58
|
sym_name = name.to_sym
|
|
47
|
-
properties
|
|
48
|
-
|
|
49
|
-
define_method(
|
|
59
|
+
@lazer_metadata[:properties] << sym_name
|
|
60
|
+
options.each_pair { |option, value| @lazer_metadata[option][sym_name] = value }
|
|
61
|
+
define_method(sym_name) { read_attribute(sym_name) }
|
|
50
62
|
end
|
|
51
63
|
end
|
|
52
64
|
|
|
53
|
-
# The
|
|
65
|
+
# The methods to extend the instance with.
|
|
54
66
|
module InstanceMethods
|
|
55
|
-
#
|
|
56
|
-
#
|
|
67
|
+
# Create a new instance of the class from a set of source attributes.
|
|
57
68
|
# @param [Hash] attributes the model attributes
|
|
58
69
|
# @return [void]
|
|
59
70
|
def initialize(attributes = {})
|
|
60
|
-
# Check all required attributes.
|
|
61
|
-
self.class.
|
|
62
|
-
|
|
71
|
+
# Check that all required attributes exist.
|
|
72
|
+
self.class.lazer_metadata[:required].keys.each do |property|
|
|
73
|
+
key = LazyLazer.source_key(self, property)
|
|
74
|
+
raise RequiredAttribute, "#{self} requires `#{key}`" unless attributes.key?(key)
|
|
63
75
|
end
|
|
64
76
|
|
|
65
|
-
@
|
|
66
|
-
@
|
|
77
|
+
@_lazer_source = attributes.dup
|
|
78
|
+
@_lazer_cache = {}
|
|
67
79
|
end
|
|
68
80
|
|
|
81
|
+
# Converts all the attributes that haven't been converted yet and returns the final hash.
|
|
69
82
|
# @param [Boolean] strict whether to fully load all attributes
|
|
70
83
|
# @return [Hash] a hash representation of the model
|
|
71
84
|
def to_h(strict = true)
|
|
72
85
|
if strict
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@_lazer_attribute_cache[key] = read_attribute(key)
|
|
76
|
-
end
|
|
86
|
+
todo = self.class.lazer_metadata[:properties] - @_lazer_cache.keys
|
|
87
|
+
todo.each { |k| read_attribute(k) }
|
|
77
88
|
end
|
|
78
|
-
@
|
|
89
|
+
@_lazer_cache
|
|
79
90
|
end
|
|
80
91
|
|
|
81
|
-
#
|
|
82
|
-
|
|
92
|
+
# @abstract Provides reloading behaviour for lazy loading.
|
|
93
|
+
# @return [Hash] the result of reloading the hash
|
|
94
|
+
# @note if necessary, subclasses can make this method private, so this isn't tested.
|
|
95
|
+
def lazer_reload
|
|
96
|
+
self.fully_loaded = true
|
|
97
|
+
{}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Reload the object. Calls {#lazer_reload}, then merges the results into the internal store.
|
|
101
|
+
# Also evicts the internal cache of the new keys.
|
|
102
|
+
# @return [self] the updated object
|
|
103
|
+
def reload
|
|
104
|
+
new_attributes = lazer_reload
|
|
105
|
+
@_lazer_source.merge!(new_attributes)
|
|
106
|
+
@_lazer_cache = {}
|
|
107
|
+
self
|
|
108
|
+
end
|
|
83
109
|
|
|
84
110
|
# Return the value of the attribute.
|
|
85
111
|
# @param [Symbol] name the attribute name
|
|
86
112
|
# @raise MissingAttribute if the key was not found
|
|
87
113
|
def read_attribute(name)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
114
|
+
# Returns the cached attribute.
|
|
115
|
+
return @_lazer_cache[name] if @_lazer_cache.key?(name)
|
|
116
|
+
|
|
117
|
+
# Converts the property into the lookup key.
|
|
118
|
+
source_key = LazyLazer.source_key(self, name)
|
|
91
119
|
|
|
92
|
-
if
|
|
93
|
-
|
|
120
|
+
# Reloads if a key that should be there isn't.
|
|
121
|
+
reload if !@_lazer_source.key?(source_key) &&
|
|
122
|
+
self.class.lazer_metadata[:required].include?(name) &&
|
|
123
|
+
!fully_loaded?
|
|
124
|
+
|
|
125
|
+
# Complains if even after reloading, the key is missing (and there's no default).
|
|
126
|
+
if !@_lazer_source.key?(source_key) && !self.class.lazer_metadata[:default].key?(name)
|
|
127
|
+
raise MissingAttribute, "`#{source_key}` missing for #{self}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Gets the value or gets the default.
|
|
131
|
+
value_or_default = @_lazer_source.fetch(source_key) do
|
|
132
|
+
default = self.class.lazer_metadata[:default][name]
|
|
133
|
+
default.is_a?(Proc) ? instance_exec(&default) : default
|
|
94
134
|
end
|
|
95
|
-
|
|
96
|
-
|
|
135
|
+
|
|
136
|
+
# Transforms the result using :with, if found.
|
|
137
|
+
transformer = self.class.lazer_metadata[:with][name]
|
|
138
|
+
coerced =
|
|
139
|
+
case transformer
|
|
140
|
+
when Symbol
|
|
141
|
+
value_or_default.public_send(transformer)
|
|
142
|
+
when Proc
|
|
143
|
+
instance_exec(value_or_default, &transformer)
|
|
144
|
+
else
|
|
145
|
+
value_or_default
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Add to cache and return the result.
|
|
149
|
+
@_lazer_cache[name] = coerced
|
|
97
150
|
end
|
|
98
151
|
|
|
99
152
|
# Return the value of the attribute, returning nil if not found
|
|
@@ -108,7 +161,7 @@ module LazyLazer
|
|
|
108
161
|
# @param [Symbol] attribute the attribute to update
|
|
109
162
|
# @param [Object] value the new value
|
|
110
163
|
def write_attribute(attribute, value)
|
|
111
|
-
@
|
|
164
|
+
@_lazer_cache[attribute] = value
|
|
112
165
|
end
|
|
113
166
|
|
|
114
167
|
# Update multiple attributes at once.
|
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.
|
|
4
|
+
version: 0.3.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-08-
|
|
11
|
+
date: 2017-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -130,7 +130,6 @@ files:
|
|
|
130
130
|
- lazy_lazer.gemspec
|
|
131
131
|
- lib/lazy_lazer.rb
|
|
132
132
|
- lib/lazy_lazer/errors.rb
|
|
133
|
-
- lib/lazy_lazer/utilities.rb
|
|
134
133
|
- lib/lazy_lazer/version.rb
|
|
135
134
|
homepage: https://github.com/avinashbot/lazy_lazer
|
|
136
135
|
licenses:
|
data/lib/lazy_lazer/utilities.rb
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module LazyLazer
|
|
4
|
-
# Utility methods.
|
|
5
|
-
# @api private
|
|
6
|
-
module Utilities
|
|
7
|
-
# Get a value from a hash, calling the default if needed.
|
|
8
|
-
# @param [Hash] source the hash to lookup
|
|
9
|
-
# @param [Symbol] key the key to lookup
|
|
10
|
-
# @param [Proc, Object] default the default value or Proc to call
|
|
11
|
-
# @return the object or the default value
|
|
12
|
-
def self.lookup_default(source, key, default)
|
|
13
|
-
return source[key] if source.key?(key)
|
|
14
|
-
return default.call if default.is_a?(Proc)
|
|
15
|
-
default
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Transforms a value using a "transformer" supplied using :with.
|
|
19
|
-
# @param [Object] value the value to transform
|
|
20
|
-
# @param [nil, Symbol, Proc] transformer the transformation method
|
|
21
|
-
# @param [Object] context the context to run the proc in
|
|
22
|
-
# @return [Object] the result of applying the transformation to the value
|
|
23
|
-
def self.transform_value(value, transformer)
|
|
24
|
-
case transformer
|
|
25
|
-
when nil
|
|
26
|
-
value
|
|
27
|
-
when Symbol
|
|
28
|
-
value.public_send(transformer)
|
|
29
|
-
when Proc
|
|
30
|
-
transformer.call(value)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|