poros 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb33cb3edeb46a15e44bb915e71420a9c92ed99234ee52ab3d083b2e963b9807
4
- data.tar.gz: 503c54369c8579a4a0d7fa41a2ae5bdb3cbdd59e8b4a4c999ad531d104858825
3
+ metadata.gz: 6dc1a45e91216abe73208b6c54926ad645e00217ac26c1ee829afc4b75bd279a
4
+ data.tar.gz: b0eae29db2cba9dfe71dc4d830bbba43c41ff2ea8cb6a6d4c89328f4ce2d450b
5
5
  SHA512:
6
- metadata.gz: 02f0e7d5fbb462f9e4e29595c8d2e22fc775410b4b7782e1db43c787f078c4b8b07c61ebb2b57c4ea35215a4f1074faadc1bdbd297be29dd409f493c5a5bf31b
7
- data.tar.gz: 95c434c7a70309d7bf2181dfbee83c9e0856969bff04051353b8b06c1c0391768e7802e3fe37457b712893b7950af959f75e7f0a75b1cfb866ccfc92fb256752
6
+ metadata.gz: c26f6f28c9324df4cd0be12b514ee967486e3a80a6fa051b0682099b77fdaa50ae77720aa698af07dcd4c789593ddddd32e0d9cb0d926e8cd1314fc1a3a57834
7
+ data.tar.gz: 3c04c87297694078d97ec8b29c98d440ce0e5c0274791899bab06ac859d5638b671b1b3b94d33b0177471367a2ec76c74fa234fb7cc2fbac3327fcd6df38c293
data/README.md CHANGED
@@ -52,9 +52,9 @@ many_widgets = Widget.where(order: 1, name: /regex/)
52
52
  attributes defined in `poro_attr`. In the future I may define this function for
53
53
  you but for right now I'm not sure I want to go that direction.
54
54
 
55
- `poro_attrs :blah` will define a getter and setter for `:blah` and also be persisted when you run `#save`
55
+ `poro_attr :blah` will define a getter and setter for `:blah` and also be persisted when you run `#save`
56
56
 
57
- `poro_indexes :blah` will create indexes on these columns, this will greatly
57
+ `poro_index :blah` will create indexes on these columns, this will greatly
58
58
  speed up searching but if your index gets really big the initial load of a
59
59
  model will take quite a long time.
60
60
 
@@ -62,8 +62,7 @@ model will take quite a long time.
62
62
  save to the default of `./db/#{self}`. If you do re-define it you _must_
63
63
  seperate each model into it's own folder or it will have _lots_ of issues.
64
64
 
65
- `.where` acts a bit like ActiveRecord but it's not currently chainable (I plan
66
- to do that in the future though) and takes exact matches, regexs, arrays, or
65
+ `.where` acts a bit like ActiveRecord and it takes exact matches, regexs, arrays, or
67
66
  lambdas in any combination.
68
67
 
69
68
  ```ruby
@@ -72,15 +71,52 @@ Widget.where(name: 'sprocket')
72
71
  Widget.where(name: /spr.*cket/)
73
72
  Widget.where(order: -> value { value > 3 })
74
73
  Widget.where(order: [1, 5, 8], active: true)
74
+ Widget.where(order: 1).where(name: 'cog')
75
75
 
76
76
  # Invalid examples
77
77
  Widget.where(name: 'sprocket', name: 'cog') # can't check same value twice, use arrays
78
- Widget.where(order: 1).where(name: 'cog') # can't chain wheres
79
78
  ```
80
79
 
81
-
82
80
  `.find` takes the `#uuid` which is generated by Poros and returns the single record.
83
81
 
82
+ A basic usage of relations would be something like:
83
+
84
+ ```ruby
85
+ class Author
86
+ include Poros
87
+
88
+ poro_attr :name
89
+
90
+ has_many :books
91
+
92
+ def initialize(name: '')
93
+ @name = name
94
+ end
95
+ end
96
+
97
+ class Book
98
+ include Poros
99
+
100
+ poro_attr :title, :author_uuid
101
+
102
+ belongs_to :author
103
+
104
+ def initialize(title: '', author: nil, author_uuid: nil)
105
+ @title = title
106
+ @author_uuid = author_uuid if author_uuid
107
+ self.author = author if author
108
+ end
109
+ end
110
+
111
+ audrey_niffenegger = Author.new(name: 'Audrey Niffenegger').save
112
+ john_steinbeck = Author.new(name: 'John Steinbeck').save
113
+ book_1 = Book.new(title: 'The Grapes of Wrath', author_uuid: john_steinbeck.uuid).save
114
+ book_2 = Book.new(title: 'The Time Traveler\'s Wife', author_uuid: audrey_niffenegger.uuid).save
115
+ book_3 = Book.new(title: 'Of Mice and Men', author_uuid: john_steinbeck.uuid).save
116
+ ```
117
+
118
+ And then you'll be able to do things like `author_2.books.where(name: /wrath/)` or `book_2.author`
119
+
84
120
  ## Development
85
121
 
86
122
  After checking out the repo, run `bundle install` to install dependencies. Then, run
@@ -57,54 +57,7 @@ module Poros
57
57
  end
58
58
 
59
59
  def where(query)
60
- indexed, table_scan = query.partition { |index, key| poro_indexes.include?(index) }
61
-
62
- indexed_results = indexed.map { |key, value|
63
- case value
64
- when Regexp
65
- index_data[key].keys.flat_map { |value_name|
66
- index_data[key][value_name] if value =~ value_name
67
- }.compact
68
- when Array
69
- value.flat_map { |value_name| index_data[key][value_name] }
70
- when Proc
71
- index_data[key].keys.flat_map { |value_name|
72
- index_data[key][value_name] if value.call(value_name)
73
- }.compact
74
- else
75
- index_data[key].has_key?(value) ?
76
- index_data[key][value] : []
77
- end
78
- }.inject(:&)
79
-
80
- if table_scan.size > 0
81
- scanned_results = Dir.glob(File.join(data_directory, '*.yml')).map { |file|
82
- next if file == index_file
83
- data = YAML.load(File.read(file))
84
- data[:uuid] if table_scan.all? { |key, value|
85
- case value
86
- when Regexp
87
- value =~ data[key]
88
- when Array
89
- value.include?(data[key])
90
- when Proc
91
- value.call(data[key])
92
- else
93
- data[key] == value
94
- end
95
- }
96
- }.compact
97
- end
98
-
99
- if indexed.size > 0 && table_scan.size > 0
100
- results = indexed_results & scanned_results
101
- elsif indexed.size > 0
102
- results = indexed_results
103
- else
104
- results = scanned_results
105
- end
106
-
107
- results.map { |uuid| find(uuid) }
60
+ Poros::Query.new(self).where(query)
108
61
  end
109
62
 
110
63
  def index_data
@@ -23,5 +23,9 @@ module Poros
23
23
  self.class.update_index(self)
24
24
  self
25
25
  end
26
+
27
+ def ==(other)
28
+ uuid == other.uuid
29
+ end
26
30
  end
27
31
  end
@@ -0,0 +1,71 @@
1
+ module Poros
2
+ class Query
3
+ include Enumerable
4
+ attr_accessor :object, :queries
5
+
6
+ def initialize(object)
7
+ @object = object
8
+ @queries = {}
9
+ end
10
+
11
+ def where(query)
12
+ @queries.merge!(query)
13
+ self
14
+ end
15
+
16
+ def each(&block)
17
+ results.each { |result| block.call(result) }
18
+ end
19
+
20
+ def results
21
+ indexed, table_scan = @queries.partition { |index, key| @object.poro_indexes.include?(index) }
22
+
23
+ indexed_results = indexed.map { |key, value|
24
+ case value
25
+ when Regexp
26
+ @object.index_data[key].keys.flat_map { |value_name|
27
+ @object.index_data[key][value_name] if value =~ value_name
28
+ }.compact
29
+ when Array
30
+ value.flat_map { |value_name| @object.index_data[key][value_name] }
31
+ when Proc
32
+ @object.index_data[key].keys.flat_map { |value_name|
33
+ @object.index_data[key][value_name] if value.call(value_name)
34
+ }.compact
35
+ else
36
+ @object.index_data[key].has_key?(value) ?
37
+ @object.index_data[key][value] : []
38
+ end
39
+ }.inject(:&)
40
+
41
+ if table_scan.size > 0
42
+ scanned_results = Dir.glob(File.join(@object.data_directory, '*.yml')).map { |file|
43
+ next if file == @object.index_file
44
+ data = YAML.load(File.read(file))
45
+ data[:uuid] if table_scan.all? { |key, value|
46
+ case value
47
+ when Regexp
48
+ value =~ data[key]
49
+ when Array
50
+ value.include?(data[key])
51
+ when Proc
52
+ value.call(data[key])
53
+ else
54
+ data[key] == value
55
+ end
56
+ }
57
+ }.compact
58
+ end
59
+
60
+ if indexed.size > 0 && table_scan.size > 0
61
+ results = indexed_results & scanned_results
62
+ elsif indexed.size > 0
63
+ results = indexed_results
64
+ else
65
+ results = scanned_results
66
+ end
67
+
68
+ results.map { |uuid| @object.find(uuid) }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,55 @@
1
+ module Poros
2
+ module Relations
3
+ def has_many(key, class_name: nil, foreign_key: nil, primary_key: :uuid)
4
+
5
+ define_method key do
6
+ foreign_key = (foreign_key || self.class.to_s.downcase + '_uuid').to_sym
7
+ object = constantize(singularize(class_name || key))
8
+ primary_value = self.send(primary_key)
9
+
10
+ object.where(foreign_key => primary_value)
11
+ end
12
+ end
13
+
14
+ def belongs_to(key, class_name: nil, foreign_key: nil, primary_key: :uuid)
15
+ define_method key do
16
+ object = constantize(class_name || key)
17
+ foreign_method = (foreign_key || (key.to_s + '_uuid')).to_sym
18
+ primary_attr = primary_key || (key.to_s + '_uuid')
19
+
20
+ object.where(primary_attr => self.send(foreign_method)).first
21
+ end
22
+
23
+ define_method key.to_s + '=' do |value|
24
+ foreign_attr = foreign_key || (key.to_s + '_uuid=')
25
+ primary_method = primary_key || (key.to_s + '_uuid')
26
+
27
+ self.send(foreign_attr, value.send(primary_method))
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def singularize(word)
34
+ # I don't want to depend on rails or another gem so I'm doing something
35
+ # incredibly basic that'll work 90+% of the time (for English anyways).
36
+ case word.to_s
37
+ when /ies$/
38
+ word.to_s.chomp('ies') + 'y'
39
+ when /s$/
40
+ word.to_s.chomp('s')
41
+ else
42
+ word.to_s
43
+ end
44
+ end
45
+
46
+ def constantize(word)
47
+ object_name = word
48
+ if word.downcase == word
49
+ object_name = word.to_s.split('_').map(&:capitalize).join
50
+ end
51
+
52
+ Object.const_get(object_name)
53
+ end
54
+ end
55
+ end
data/lib/poros/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Poros
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/poros.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  require 'yaml'
2
2
  require 'securerandom'
3
3
 
4
+ require 'poros/query'
4
5
  require 'poros/instance_methods'
5
6
  require 'poros/class_methods'
7
+ require 'poros/relations'
6
8
 
7
9
  module Poros
8
10
  include Poros::InstanceMethods
9
11
  include Poros::ClassMethods
12
+ include Poros::Relations
10
13
 
11
14
  def self.included(base)
12
15
  base.class_eval do
13
16
  extend ClassMethods
17
+ extend Relations
14
18
  end
15
19
  end
16
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poros
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Ross Earle
@@ -69,6 +69,8 @@ files:
69
69
  - lib/poros/class_methods.rb
70
70
  - lib/poros/info.rb
71
71
  - lib/poros/instance_methods.rb
72
+ - lib/poros/query.rb
73
+ - lib/poros/relations.rb
72
74
  - lib/poros/version.rb
73
75
  - poros.gemspec
74
76
  homepage: https://github.com/HellRok/YAMLCache