lazy_lazer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b97391a59b7f022f99a5211abffff9d9baeb907
4
+ data.tar.gz: 54639abaacb86f5be191f3d0d98fcc0172486587
5
+ SHA512:
6
+ metadata.gz: 44492444116d6d9fee06516379daa59801977ffe9dbdf5578534711030a43e06ae019d1ec38ec6a913300089fc5189b8b2c56eec356ee8ee58976f2a3f5ad2c6
7
+ data.tar.gz: 192948daba02e2c91815856085bf115fc1d072c6615f45dcd34934ca52f3ac95fd4cc2d670c4b561df43d1102450669d9a0420c0b2d3314e363de5c1b4c520ca
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+
4
+ Metrics/LineLength:
5
+ Max: 100
6
+
7
+ # Dumb, but also brand new.
8
+ Style/NumericPredicate:
9
+ Enabled: false
10
+
11
+ # Stop rubocop from complaining about large `describe` blocks.
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - spec/**/*_spec.rb
15
+
16
+ # Lower severity of complexity metrics.
17
+ Metrics/AbcSize:
18
+ Severity: refactor
19
+ Metrics/CyclomaticComplexity:
20
+ Severity: refactor
21
+ Metrics/PerceivedComplexity:
22
+ Severity: refactor
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in lazy_lazer.gemspec
8
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, cmd: 'bundle exec rspec -fp' do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { 'spec' }
6
+ watch('spec/spec_helper.rb') { 'spec' }
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Avinash Dwarapu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ **lazy lazer**
2
+
3
+ **features**:
4
+ - simple codebase (~100 lines of code)
5
+ - doesn't inherit all of the Hash and Enumerable cruft
6
+ - super lazy, doesn't even parse attributes until it's necessary
7
+
8
+ ```ruby
9
+ class User
10
+ include LazyLazer
11
+
12
+ # User.new(first_name: 'John') #=> Error: missing `id`
13
+ # User.new(id: 1).id? #=> true
14
+ property :id, required: true
15
+
16
+ # user = User.new(id: 1)
17
+ # user.email #=> nil
18
+ property :email, default: nil
19
+
20
+ # user = User.new(id: 1)
21
+ # user.preferred_language #=> :en_US
22
+ property :preferred_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: -> { '<last name>' }
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
35
+ property :age, with: :to_i
36
+
37
+ def reload
38
+ update_attributes!(...) # update your attributes somehow
39
+ fully_loaded = true # model is fully updated, no more lazy loading necessary
40
+ self # a rails convention, totally optional
41
+ end
42
+ end
43
+ ```
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+ require 'rspec/core/rake_task'
6
+
7
+ RuboCop::RakeTask.new do |task|
8
+ task.options = ['--fail-level', 'convention']
9
+ end
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task default: %i[rubocop spec]
data/bin/_guard-core ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application '_guard-core' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ require 'rubygems'
16
+ require 'bundler/setup'
17
+
18
+ load Gem.bin_path('guard', '_guard-core')
data/bin/console ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require_relative '../lib/lazy_lazer'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require 'pry'
12
+ Pry.start
data/bin/guard ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'guard' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ require 'rubygems'
16
+ require 'bundler/setup'
17
+
18
+ load Gem.bin_path('guard', 'guard')
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+ require 'lazy_lazer/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'lazy_lazer'
10
+ spec.version = LazyLazer::VERSION
11
+ spec.authors = ['Avinash Dwarapu']
12
+ spec.email = ['avinash@dwarapu.me']
13
+
14
+ spec.summary = 'Create lazily loadable models.'
15
+ spec.homepage = 'https://github.com/avinashbot/lazy_lazer'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.15'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'guard', '~> 2.14'
28
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
29
+
30
+ spec.add_development_dependency 'rubocop', '~> 0.47'
31
+ spec.add_development_dependency 'pry', '~> 0.10'
32
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LazyLazer
4
+ # This is raised when a required attribute isn't included.
5
+ class RequiredAttribute < StandardError; end
6
+
7
+ # Raised when a missing attribute is called but a default isn't present.
8
+ class MissingAttribute < StandardError; end
9
+ end
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LazyLazer
4
+ VERSION = '0.1.0'
5
+ end
data/lib/lazy_lazer.rb ADDED
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lazy_lazer/version'
4
+ require_relative 'lazy_lazer/errors'
5
+ require_relative 'lazy_lazer/utilities'
6
+
7
+ # LazyLazer is a lazy loading model.
8
+ module LazyLazer
9
+ # Hook into `include LazyLazer`.
10
+ # @param [Module] base the object to include the methods in
11
+ # @return [void]
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ base.include(InstanceMethods)
15
+ base.instance_variable_set(:@_lazer_properties, {})
16
+ base.instance_variable_set(:@_lazer_required_properties, [])
17
+ end
18
+
19
+ # The methods to extend the class with.
20
+ module ClassMethods
21
+ # Copies parent properties into subclasses.
22
+ # @param [Class] klass the subclass
23
+ # @return [void]
24
+ def inherited(klass)
25
+ klass.instance_variable_set(:@_lazer_properties, @_lazer_properties)
26
+ klass.instance_variable_set(:@_lazer_required_properties, @_lazer_required_properties)
27
+ end
28
+
29
+ # @return [Hash<Symbol, Hash>] defined properties and their options
30
+ def properties
31
+ @_lazer_properties
32
+ end
33
+
34
+ # Define a property.
35
+ # @param [Symbol] name the name of the property method
36
+ # @param [Hash] options the options to create the property with
37
+ # @option options [Boolean] :required (false) whether existence of this
38
+ # property should be checked on model creation
39
+ # @option options [Symbol, Array<Symbol>] :from (name) the key(s) to get
40
+ # the value of the property from
41
+ # @option options [Object, Proc] :default the default value to return if
42
+ # not provided
43
+ # @option options [Proc, Symbol] :with an optional transformation to apply
44
+ # to the value of the key
45
+ def property(name, **options)
46
+ sym_name = name.to_sym
47
+ properties[sym_name] = options
48
+ @_lazer_required_properties << sym_name if options[:required]
49
+ define_method(name) { read_attribute(name) }
50
+ end
51
+ end
52
+
53
+ # The base model class. This could be included directly.
54
+ module InstanceMethods
55
+ # Initializer.
56
+ #
57
+ # @param [Hash] attributes the model attributes
58
+ # @return [void]
59
+ def initialize(attributes = {})
60
+ # Check all required attributes.
61
+ self.class.instance_variable_get(:@_lazer_required_properties).each do |prop|
62
+ raise RequiredAttribute, "#{self} requires `#{prop}`" unless attributes.key?(prop)
63
+ end
64
+
65
+ @_lazer_attribute_source = attributes.dup
66
+ @_lazer_attribute_cache = {}
67
+ end
68
+
69
+ # @param [Boolean] strict whether to fully load all attributes
70
+ # @return [Hash] a hash representation of the model
71
+ def to_h(strict = true)
72
+ if strict
73
+ remaining = @_lazer_attribute_source.keys - @_lazer_attribute_cache.keys
74
+ remaining.each do |key|
75
+ @_lazer_attribute_cache[key] = read_attribute(key)
76
+ end
77
+ end
78
+ @_lazer_attribute_cache
79
+ end
80
+
81
+ # Reload the object.
82
+ def reload; end
83
+
84
+ # Return the value of the attribute.
85
+ # @param [Symbol] name the attribute name
86
+ # @raise MissingAttribute if the key was not found
87
+ # @note this differs from the Rails implementation and raises {MissingAttribute} if the
88
+ # attribute wasn't found.
89
+ def read_attribute(name)
90
+ return @_lazer_attribute_cache[name] if @_lazer_attribute_cache.key?(name)
91
+ reload if self.class.properties.key?(name) && !fully_loaded?
92
+ options = self.class.properties.fetch(name, {})
93
+
94
+ if !@_lazer_attribute_source.key?(name) && !options.key?(:default)
95
+ raise MissingAttribute, "#{name} is missing for #{self}"
96
+ end
97
+ uncoerced = Utilities.lookup_default(@_lazer_attribute_source, name, options[:default])
98
+ Utilities.transform_value(uncoerced, options[:with])
99
+ end
100
+ alias [] read_attribute
101
+
102
+ # Update an attribute.
103
+ # @param [Symbol] attribute the attribute to update
104
+ # @param [Object] value the new value
105
+ def write_attribute(attribute, value)
106
+ @_lazer_attribute_cache[attribute] = value
107
+ end
108
+
109
+ # Update multiple attributes at once.
110
+ # @param [Hash<Symbol, Object>] new_attributes the new attributes
111
+ def assign_attributes(new_attributes)
112
+ new_attributes.each { |key, value| write_attribute(key, value) }
113
+ end
114
+ alias attributes= assign_attributes
115
+
116
+ # @return [Boolean] whether the object is done with lazy loading
117
+ def fully_loaded?
118
+ @_lazer_fully_loaded ||= false
119
+ end
120
+
121
+ private
122
+
123
+ # @param [Boolean] state the new state
124
+ def fully_loaded=(state)
125
+ @_lazer_fully_loaded = state
126
+ end
127
+ end
128
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lazy_lazer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Avinash Dwarapu
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.47'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.47'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.10'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.10'
111
+ description:
112
+ email:
113
+ - avinash@dwarapu.me
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".rubocop.yml"
121
+ - Gemfile
122
+ - Guardfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - bin/_guard-core
127
+ - bin/console
128
+ - bin/guard
129
+ - bin/setup
130
+ - lazy_lazer.gemspec
131
+ - lib/lazy_lazer.rb
132
+ - lib/lazy_lazer/errors.rb
133
+ - lib/lazy_lazer/utilities.rb
134
+ - lib/lazy_lazer/version.rb
135
+ homepage: https://github.com/avinashbot/lazy_lazer
136
+ licenses: []
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.6.8
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Create lazily loadable models.
158
+ test_files: []