lazy_record 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +134 -7
- data/bin/console +6 -5
- data/config/environment.rb +10 -0
- data/example/cat.rb +3 -0
- data/example/dog.rb +10 -0
- data/example/friend.rb +3 -0
- data/example/person.rb +78 -0
- data/lazy_record.gemspec +1 -1
- data/lib/lazy_record.rb +3 -1
- data/lib/lazy_record/associations.rb +1 -1
- data/lib/lazy_record/attributes.rb +1 -1
- data/lib/lazy_record/base.rb +4 -3
- data/lib/lazy_record/callbacks.rb +1 -1
- data/lib/lazy_record/dynamic_modules.rb +1 -1
- data/lib/lazy_record/methods.rb +1 -1
- data/lib/lazy_record/relation.rb +22 -7
- data/lib/lazy_record/scopes.rb +1 -1
- data/lib/lazy_record/validations.rb +1 -1
- data/lib/lazy_record/version.rb +2 -2
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f5ad82781d6334b11179ab0981b622e4d47494a
|
4
|
+
data.tar.gz: d82b3d440be2c90ff4354d557dc3f1ac8fa53ab5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 419b37034fd69cc6c115ede5ee8b663f589bd432ea7f25f6942378d7ced2b319e53eabf4ab056a9b063792804a6d5929934a3e23b448246302df275e6b68726b
|
7
|
+
data.tar.gz: 5750864f0f361b69d54e3c063b2c8b48444d903c933c957224592fb8261d363bc12f1ec175ec3fab87850d6312db525ba82bbe000907785db3ff8f8dec317785
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# LazyRecord
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
LazyRecord writes a bunch of boilerplate code for your POROs, similarly to what you'd expect ActiveRecord to do for your database-backed objects. This project is an attempt to understand and explore dynamic programming techniques in Ruby, and demystify some of the Rails magic. Maybe someone will find it useful.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -22,20 +20,149 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
|
23
|
+
All objects that inherit from `LazyRecord::Base` get block syntax added to their `#initialize` method.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class Thing < LazyRecord::Base
|
27
|
+
end
|
28
|
+
|
29
|
+
thing = Thing.new do |t|
|
30
|
+
t.inspect
|
31
|
+
end
|
32
|
+
# => #<Thing id: 1>
|
33
|
+
```
|
34
|
+
Every LazyRecord object is assigned an auto-incrementing ID after initialization. IDs reset when the program is terminated.
|
35
|
+
|
36
|
+
Use `lr_attr_accessor` like you would use `attr_accessor`. You'll get hash syntax in your `#intialize` method for attribute setting.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
class Thing < LazyRecord::Base
|
40
|
+
lr_attr_accessor :stuff, :junk
|
41
|
+
end
|
42
|
+
|
43
|
+
thing = Thing.new stuff: 'stuff' do |t|
|
44
|
+
t.junk = 'junk'
|
45
|
+
end
|
46
|
+
# => #<Thing id: 1, stuff: "stuff", junk: "junk">
|
47
|
+
```
|
48
|
+
|
49
|
+
Validate presence of attributes with `lr_validates` like you would with ActiveRecord. Failed validations will return false and the ID will not be incremented. More validation options coming in the future.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
class Thing < LazyRecord::Base
|
53
|
+
lr_attr_accessor :stuff, :junk
|
54
|
+
lr_validates :stuff, presence: true
|
55
|
+
end
|
56
|
+
|
57
|
+
thing = Thing.new junk: 'junk'
|
58
|
+
ArgumentError
|
59
|
+
stuff must be given
|
60
|
+
#<Thing id: nil, stuff: nil, junk: "junk">
|
61
|
+
# => false
|
62
|
+
```
|
63
|
+
Use `lr_has_many` to set up associated collections of another class. `lr_belongs_to` will be added in a future update.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class Whatever < LazyRecord::Base
|
67
|
+
end
|
68
|
+
|
69
|
+
class Thing < LazyRecord::Base
|
70
|
+
lr_attr_accessor :stuff, :junk
|
71
|
+
lr_validates :stuff, presence: true
|
72
|
+
lr_has_many :whatevers
|
73
|
+
end
|
74
|
+
|
75
|
+
whatever = Whatever.new
|
76
|
+
# => #<Whatever id: 1>
|
77
|
+
|
78
|
+
thing = Thing.new do |t|
|
79
|
+
t.stuff = 'stuff'
|
80
|
+
t.whatevers << whatever
|
81
|
+
end
|
82
|
+
# => #<Thing id: 1, stuff: "stuff", junk: nil>
|
83
|
+
|
84
|
+
thing.whatevers
|
85
|
+
# => #<WhateverRelation [#<Whatever id: 1>]>
|
86
|
+
```
|
87
|
+
|
88
|
+
Use `lr_scope` and `#where` to create class scope methods and query objects. Works just like ActiveRecord scopes, including scope chaining. Only since it is all Ruby and no SQL, use `==` as the comparison operator.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class Whatever < LazyRecord::Base
|
92
|
+
lr_attr_accessor :party_value, :sleepy_value
|
93
|
+
lr_scope :big_party, -> { where('party_value > 10') }
|
94
|
+
lr_scope :low_sleepy, -> { where('sleepy_value < 10') }
|
95
|
+
end
|
96
|
+
|
97
|
+
class Thing < LazyRecord::Base
|
98
|
+
lr_attr_accessor :stuff, :junk
|
99
|
+
lr_validates :stuff, presence: true
|
100
|
+
lr_has_many :whatevers
|
101
|
+
end
|
102
|
+
|
103
|
+
Whatever.new party_value: 12, sleepy_value: 12
|
104
|
+
Whatever.new party_value: 13, sleepy_value: 3
|
105
|
+
Whatever.new party_value: 4, sleepy_value: 11
|
106
|
+
Whatever.new party_value: 3, sleepy_value: 5
|
107
|
+
thing = Thing.new do |t|
|
108
|
+
t.stuff = 'stuff'
|
109
|
+
t.whatevers = Whatever.all
|
110
|
+
end
|
111
|
+
# => #<Thing id: 1, stuff: "stuff", junk: nil>
|
112
|
+
|
113
|
+
thing.whatevers.big_party
|
114
|
+
# => #<WhateverRelation [#<Whatever id: 1, party_value: 12, sleepy_value: 12>, #<Whatever id: 2, party_value: 13, sleepy_value: 3>]>
|
115
|
+
|
116
|
+
thing.whatevers.low_sleepy
|
117
|
+
# => #<WhateverRelation [#<Whatever id: 2, party_value: 13, sleepy_value: 3>, #<Whatever id: 4, party_value: 3, sleepy_value: 5>]>
|
118
|
+
|
119
|
+
thing.whatevers.big_party.low_sleepy
|
120
|
+
# => #<WhateverRelation [#<Whatever id: 2, party_value: 13, sleepy_value: 3>]>
|
121
|
+
|
122
|
+
Whatever.low_sleepy
|
123
|
+
# => #<WhateverRelation [#<Whatever id: 2, party_value: 13, sleepy_value: 3>, #<Whatever id: 4, party_value: 3, sleepy_value: 5>]>
|
124
|
+
```
|
125
|
+
|
126
|
+
Use `lr_method` for an alternative API for defining short instance methods. Can use lambda syntax or string syntax. Only good for quick one-liners. If the method references `self` of the instance, either explicitly or implicitly, it needs to use the string syntax, since any variables not passed into the lambda will be evaluated in the context of the Class level scope.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class Whatever < LazyRecord::Base
|
130
|
+
lr_attr_accessor :party_value, :sleepy_value, :right
|
131
|
+
lr_scope :big_party, -> { where('party_value > 10') }
|
132
|
+
lr_scope :low_sleepy, -> { where('sleepy_value < 10') }
|
133
|
+
end
|
134
|
+
|
135
|
+
class Thing < LazyRecord::Base
|
136
|
+
lr_attr_accessor :stuff, :junk
|
137
|
+
lr_validates :stuff, presence: true
|
138
|
+
lr_has_many :whatevers
|
139
|
+
lr_method :speak, -> (string) { puts string }
|
140
|
+
lr_method :add_whatever, 'hmm', 'whatevers << Whatever.new(right: hmm)'
|
141
|
+
end
|
142
|
+
|
143
|
+
thing = Thing.new stuff: 'stuff'
|
144
|
+
thing.speak "I'm a thing"
|
145
|
+
# I'm a thing
|
146
|
+
# => nil
|
147
|
+
|
148
|
+
thing.add_whatever(true)
|
149
|
+
# => [#<Whatever id: 1, party_value: nil, sleepy_value: nil, right: true>]
|
150
|
+
|
151
|
+
thing.whatevers
|
152
|
+
# => #<WhateverRelation [#<Whatever id: 1, party_value: nil, sleepy_value: nil, right: true>]>
|
153
|
+
```
|
26
154
|
|
27
155
|
## Development
|
28
156
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
157
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. There is an `example` directory with some LazyRecord classes defined.
|
30
158
|
|
31
159
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
160
|
|
33
161
|
## Contributing
|
34
162
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
163
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/msimonborg/lazy_record.
|
36
164
|
|
37
165
|
|
38
166
|
## License
|
39
167
|
|
40
168
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
41
|
-
|
data/bin/console
CHANGED
@@ -2,14 +2,15 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'bundler/setup'
|
5
|
-
|
5
|
+
require_relative '../config/environment'
|
6
|
+
|
6
7
|
|
7
8
|
# You can add fixtures and/or initialization code here to make experimenting
|
8
9
|
# with your gem easier. You can also use a different console, if you like.
|
9
10
|
|
10
11
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
|
12
|
-
|
12
|
+
require "pry"
|
13
|
+
Pry.start
|
13
14
|
|
14
|
-
require 'irb'
|
15
|
-
IRB.start(__FILE__)
|
15
|
+
# require 'irb'
|
16
|
+
# IRB.start(__FILE__)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
example = File.expand_path('../../example', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(example) unless $LOAD_PATH.include?(example)
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'lazy_record'
|
7
|
+
autoload :Cat, 'cat'
|
8
|
+
autoload :Dog, 'dog'
|
9
|
+
autoload :Friend, 'friend'
|
10
|
+
autoload :Person, 'person'
|
data/example/cat.rb
ADDED
data/example/dog.rb
ADDED
data/example/friend.rb
ADDED
data/example/person.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
class Person < LazyRecord::Base
|
2
|
+
lr_attr_accessor :name, :age, :haircut
|
3
|
+
lr_has_many :dogs, :cats
|
4
|
+
lr_accepts_nested_attributes_for :dogs, :cats
|
5
|
+
|
6
|
+
can_multiply
|
7
|
+
|
8
|
+
lr_scope :new_with_dog, ->(opts = {}) {
|
9
|
+
opts[:dog] = {} unless opts[:dog]
|
10
|
+
self.new(opts) { |p| p.adopt_a_dog(opts[:dog]) }
|
11
|
+
}
|
12
|
+
lr_scope :young, -> { where('age < 30') }
|
13
|
+
lr_scope :short_hair, -> { where('haircut == "short"') }
|
14
|
+
|
15
|
+
lr_method :speak, -> (string) { puts string }
|
16
|
+
lr_method :add_dog, :name, 'dogs << Dog.new(name: name)'
|
17
|
+
lr_method :introduce_yourself, 'puts "Hello, my name is #{name}"'
|
18
|
+
|
19
|
+
lr_validates :name, :age, presence: true
|
20
|
+
|
21
|
+
def self.make_people(*args, &block)
|
22
|
+
opts = args.extract_options!
|
23
|
+
|
24
|
+
people = args.map do |arg|
|
25
|
+
Person.new { |p| p.name = arg }
|
26
|
+
end
|
27
|
+
|
28
|
+
if opts[:count] == true
|
29
|
+
puts "There are #{people.size} people!"
|
30
|
+
end
|
31
|
+
|
32
|
+
if opts[:dog]
|
33
|
+
people.each do |person|
|
34
|
+
person.adopt_a_dog(opts[:dog]) do |d|
|
35
|
+
d.name = "#{person.name}'s best friend"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
people.each { |person| block.call(person) } if block
|
41
|
+
|
42
|
+
people
|
43
|
+
end
|
44
|
+
|
45
|
+
def times(num, &block)
|
46
|
+
if block
|
47
|
+
i = 0
|
48
|
+
while i < num
|
49
|
+
block.call
|
50
|
+
i += 1
|
51
|
+
end
|
52
|
+
i
|
53
|
+
else
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def adopt_a_dog(opts = {}, &block)
|
59
|
+
dog = Dog.new(opts)
|
60
|
+
block.call(dog) if block
|
61
|
+
self.dogs << dog
|
62
|
+
dog
|
63
|
+
end
|
64
|
+
|
65
|
+
JOE = new_with_dog(
|
66
|
+
name: 'Joe',
|
67
|
+
age: 35,
|
68
|
+
haircut: 'short',
|
69
|
+
dog: {
|
70
|
+
cute: true,
|
71
|
+
name: 'Frank',
|
72
|
+
breed: 'Schnauzer',
|
73
|
+
weight: 45,
|
74
|
+
friend: true,
|
75
|
+
years_known: 6
|
76
|
+
}
|
77
|
+
).freeze
|
78
|
+
end
|
data/lazy_record.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['msimonborg@gmail.com']
|
12
12
|
|
13
13
|
spec.summary = 'Some ActiveRecord magic for your table-less POROs. WIP.'
|
14
|
-
spec.description = 'Add some convenience macros for your POROs that cut down on boilerplate code. Method definition macros, more powerful attr_accessors, and easy associations between in-memory objects. Somewhat mocks the ActiveRecord API to make it feel comfortable and intuitive for Rails developers.'
|
14
|
+
spec.description = 'Add some convenience macros for your POROs that cut down on boilerplate code. Method definition macros, more powerful attr_accessors, and easy associations between in-memory objects. Somewhat mocks the ActiveRecord API to make it feel comfortable and intuitive for Rails developers. The main intent of this project is to explore dynamic programming in Ruby. Maybe someone will find it useful. WIP.'
|
15
15
|
spec.homepage = 'https://www.github.com/msimonborg/lazy_record'
|
16
16
|
spec.license = 'MIT'
|
17
17
|
spec.add_dependency 'activesupport'
|
data/lib/lazy_record.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/inflector'
|
2
4
|
require 'lazy_record/version'
|
3
5
|
require 'lazy_record/associations'
|
4
6
|
require 'lazy_record/attributes'
|
@@ -11,4 +13,4 @@ require 'lazy_record/relation'
|
|
11
13
|
require 'lazy_record/base'
|
12
14
|
|
13
15
|
# Namespace
|
14
|
-
|
16
|
+
module LazyRecord; end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
module LazyRecord
|
3
3
|
# Add special attr_accessors that automatically add initialization options
|
4
4
|
# to your initialize method. Using lr_attr_accessor, you automatically get
|
5
5
|
# an #initialize method that takes setter options for each attribute and
|
data/lib/lazy_record/base.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
module LazyRecord
|
3
3
|
# Inherit from LazyRecord::Base to achieve lazier development.
|
4
4
|
#
|
5
5
|
# LazyRecord gives you some ActiveRecord-style conveniences for your in-memory
|
@@ -37,7 +37,7 @@ class LazyRecord
|
|
37
37
|
@all.where(condition)
|
38
38
|
end
|
39
39
|
|
40
|
-
def initialize
|
40
|
+
def initialize(_opts = {})
|
41
41
|
yield self if block_given?
|
42
42
|
end
|
43
43
|
|
@@ -56,7 +56,8 @@ class LazyRecord
|
|
56
56
|
private :id=
|
57
57
|
|
58
58
|
def inspect
|
59
|
-
"#<#{self.class} id: #{id ? id : 'nil'}
|
59
|
+
"#<#{self.class} id: #{id ? id : 'nil'}"\
|
60
|
+
"#{instance_attrs_to_s.unshift('').join(', ')}>"
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|
data/lib/lazy_record/methods.rb
CHANGED
data/lib/lazy_record/relation.rb
CHANGED
@@ -1,32 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Relation
|
4
|
-
|
2
|
+
module LazyRecord
|
3
|
+
class Relation
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :model, :all
|
5
7
|
|
6
8
|
def initialize(model:, array: nil)
|
7
9
|
raise ArgumentError, "model must be a class" unless model.is_a?(Class)
|
8
10
|
@model = model
|
11
|
+
@all = []
|
9
12
|
self_extend_scopes_module
|
10
|
-
array.each { |object|
|
13
|
+
array.each { |object| @all << object } if array
|
11
14
|
end
|
12
15
|
|
13
16
|
def <<(other)
|
14
17
|
unless other.is_a?(model)
|
15
18
|
raise ArgumentError, "object must be of type #{model}"
|
16
19
|
else
|
17
|
-
|
20
|
+
all << other
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
24
|
def inspect
|
22
|
-
"\#<#{model}Relation [#{
|
25
|
+
"\#<#{model}Relation [#{all.map(&:inspect).join(', ')}]>"
|
23
26
|
end
|
24
27
|
|
25
28
|
def where(condition)
|
26
|
-
result = select { |x| eval "x.#{condition}" }
|
29
|
+
result = all.select { |x| eval "x.#{condition}" }
|
27
30
|
self.class.new(model: model, array: result)
|
28
31
|
end
|
29
32
|
|
33
|
+
def each(&block)
|
34
|
+
all.each(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](index)
|
38
|
+
all[index]
|
39
|
+
end
|
40
|
+
|
41
|
+
def last
|
42
|
+
self[-1]
|
43
|
+
end
|
44
|
+
|
30
45
|
def self_extend_scopes_module
|
31
46
|
if model.const_defined?(:ScopeMethods, _search_ancestors = false)
|
32
47
|
mod = eval("#{model}::ScopeMethods")
|
data/lib/lazy_record/scopes.rb
CHANGED
data/lib/lazy_record/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lazy_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- M. Simon Borg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -83,7 +83,8 @@ dependencies:
|
|
83
83
|
description: Add some convenience macros for your POROs that cut down on boilerplate
|
84
84
|
code. Method definition macros, more powerful attr_accessors, and easy associations
|
85
85
|
between in-memory objects. Somewhat mocks the ActiveRecord API to make it feel comfortable
|
86
|
-
and intuitive for Rails developers.
|
86
|
+
and intuitive for Rails developers. The main intent of this project is to explore
|
87
|
+
dynamic programming in Ruby. Maybe someone will find it useful. WIP.
|
87
88
|
email:
|
88
89
|
- msimonborg@gmail.com
|
89
90
|
executables: []
|
@@ -101,6 +102,11 @@ files:
|
|
101
102
|
- Rakefile
|
102
103
|
- bin/console
|
103
104
|
- bin/setup
|
105
|
+
- config/environment.rb
|
106
|
+
- example/cat.rb
|
107
|
+
- example/dog.rb
|
108
|
+
- example/friend.rb
|
109
|
+
- example/person.rb
|
104
110
|
- lazy_record.gemspec
|
105
111
|
- lib/lazy_record.rb
|
106
112
|
- lib/lazy_record/associations.rb
|