hugg-orm 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fcefba25435c17f1d48c113fb95cd455999188bd
4
+ data.tar.gz: 9fa82007c4e17a0565d2e38098ea312eebfceefa
5
+ SHA512:
6
+ metadata.gz: 98439bedf84eafe456465061514d3dd00d8e8d879cc82f81fceb8fb1236df73d4971ff409e8a8c16449d88301cf370a86ff9136e81fdef276aad3e7a2af3fa1d
7
+ data.tar.gz: 167741b1ed8bdd9970ab6cda82957a3b2e7f2466c4c1b5088e6126e9dfe22d801cf4beb4da1b403e35f6d80352462b3469dccc3626722b4831990309d7be4cd1
@@ -0,0 +1,9 @@
1
+ require 'active_support/all'
2
+
3
+ require "hugg_orm/version"
4
+ require "hugg_orm/connection"
5
+ require "hugg_orm/persistence"
6
+ require 'hugg_orm/query'
7
+
8
+ module HuggORM
9
+ end
@@ -0,0 +1,71 @@
1
+ require 'forwardable'
2
+ require 'pg'
3
+
4
+ module HuggORM
5
+ class Connection
6
+ extend Forwardable
7
+
8
+ RETRIES = 1
9
+
10
+ attr_reader :options
11
+
12
+ def self.db
13
+ @_db ||= self.new
14
+ end
15
+
16
+ def connection
17
+ return @connection unless @connection.nil?
18
+ connect(@options)
19
+ @connection
20
+ end
21
+
22
+ def options(opts = {})
23
+ @options = opts
24
+ end
25
+
26
+ def connect(opts = {})
27
+ @options = opts
28
+ @connection = PG.connect(opts)
29
+ self
30
+ end
31
+
32
+ def disconnect
33
+ connection.finish if @connection
34
+ @connection = nil
35
+ self
36
+ end
37
+
38
+ def reconnect
39
+ disconnect
40
+ connect(@options)
41
+ self
42
+ end
43
+
44
+ def method_missing(meth, *args, &block)
45
+ if connection.respond_to?(meth)
46
+ method_retry(meth, *args, &block)
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ def method_retry(method, *args, &block)
53
+ tries ||= RETRIES + 1
54
+ connection.send(method, *args, &block)
55
+ rescue PG::ConnectionBad, PG::AdminShutdown
56
+ if (tries -= 1) > 0
57
+ reconnect
58
+ retry
59
+ end
60
+ raise
61
+ end
62
+
63
+ def exec(*args)
64
+ method_retry(:exec, *args)
65
+ end
66
+
67
+ def exec_params(*args)
68
+ method_retry(:exec_params, *args)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,204 @@
1
+ module HuggORM
2
+ module Persistence
3
+ include Comparable
4
+
5
+ def self.included(base)
6
+ base.send(:include, InstanceMethods)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ attr_accessor :_table_name, :_primary_key, :_key_field, :_exclude_fields, :_include_fields
12
+
13
+ def table_name(table_name = nil)
14
+ self._table_name = table_name unless table_name.nil?
15
+ self._table_name
16
+ end
17
+
18
+ def primary_key(primary_key = nil)
19
+ self._primary_key = primary_key unless primary_key.nil?
20
+ self._primary_key
21
+ end
22
+
23
+ def key_field(key_field = nil)
24
+ self._key_field = key_field.to_sym unless key_field.nil?
25
+ self._key_field ||= :id
26
+ end
27
+
28
+ def exclude_fields(*fields)
29
+ self._exclude_fields = fields unless fields.empty?
30
+ self._exclude_fields ||= []
31
+ end
32
+
33
+ def include_fields(*fields)
34
+ self._include_fields = fields unless fields.empty?
35
+ self._include_fields ||= []
36
+ end
37
+
38
+ def find(id)
39
+ where("#{primary_key} = ?", [id]).select.first
40
+ end
41
+
42
+ def all
43
+ results = new_query.select
44
+ new_collection(results)
45
+ end
46
+
47
+ def select(query = nil, where: nil, order: nil, limit: nil, offset: nil)
48
+ return new_collection(query.select) if query
49
+
50
+ query = new_query
51
+ query.where(where[:conditions], where[:values]) if where
52
+ query.order(order) if order
53
+ query.limit(limit) if limit
54
+ query.offset(offset) if offset
55
+
56
+ new_collection(query.select)
57
+ end
58
+
59
+ def create(data)
60
+ new(data) if new_query.insert(data) > 0
61
+ end
62
+
63
+ def update(id, data)
64
+ results = where("#{primary_key} = ?", [id]).update(data, true)
65
+ new(results.first) unless results.empty?
66
+ end
67
+
68
+ def destroy(id)
69
+ where("#{primary_key} = ?", [id]).delete > 0
70
+ end
71
+
72
+ def count
73
+ new_query.count
74
+ end
75
+
76
+ def where(conditions, values = [])
77
+ new_query.where(conditions, values)
78
+ end
79
+
80
+ def limit(limit, offset = nil)
81
+ new_query.limit(limit, offset)
82
+ end
83
+
84
+ def order(*args)
85
+ new_query.order(*args)
86
+ end
87
+
88
+ def new_query
89
+ Query.new(table_name)
90
+ end
91
+
92
+ def new_collection(data_ary)
93
+ data_ary.map { |data| new(data) }
94
+ end
95
+
96
+ def to_pgtimestamp(field)
97
+ "to_timestamp(#{field}, 'YYYY-MM-DD HH24:MI:SS')"
98
+ end
99
+
100
+ def string_to_pgtimestamp(string)
101
+ to_pgtimestamp("'#{string}'")
102
+ end
103
+
104
+ def escape_like(str = '')
105
+ pat = Regexp.union("\\", "%", "_")
106
+ str.gsub(pat) { |mat| ["\\", mat].join }
107
+ end
108
+
109
+ end
110
+
111
+ module InstanceMethods
112
+ attr_accessor :uuid, :data
113
+
114
+ def initialize(data = {})
115
+ @data = {}
116
+ update_attributes(data) unless data.empty?
117
+ end
118
+
119
+ def key
120
+ @data[self.class.key_field]
121
+ end
122
+
123
+ def set_id
124
+ # Set id based on key field, but only if id isn't already set and
125
+ # the key method has been overridden and returns data.
126
+ @data[self.class.key_field] = key if @data[self.class.key_field].nil? && key
127
+ end
128
+
129
+ def update_attributes(update_data)
130
+ # Remove fields not set to be included
131
+ unless self.class.include_fields.empty?
132
+ update_data = self.class.include_fields.each_with_object({}) do |key, hash|
133
+ hash[key] = update_data[key] if update_data.has_key? key
134
+ end
135
+ end
136
+
137
+ # Update all fields unless they have been excluded
138
+ update_data.each do |field, value|
139
+ @data[field] = value unless self.class.exclude_fields.include?(field)
140
+ end
141
+
142
+ # Set id based on key method.
143
+ set_id
144
+
145
+ self
146
+ end
147
+
148
+ def destroy
149
+ if self.class.destroy(key)
150
+ @data = {}
151
+ @state = :destroyed
152
+ end
153
+
154
+ self
155
+ end
156
+
157
+ def save
158
+ updated_data = self.class.where("#{self.class.primary_key} = ?", [key]).update(@data, true)
159
+
160
+ if updated_data != 0
161
+ @data = updated_data
162
+ @state = :updated
163
+ else
164
+ self.class.new_query.insert(@data)
165
+ @state = :created
166
+ end
167
+
168
+ self
169
+ end
170
+
171
+ def destroyed?
172
+ @state == :destroyed
173
+ end
174
+
175
+ def updated?
176
+ @state == :updated
177
+ end
178
+
179
+ def created?
180
+ @state == :created
181
+ end
182
+
183
+ def dump
184
+ str = "#{self.class.name}:\n"
185
+ @data.sort.each do |field, value|
186
+ str << " #{field}: #{value}\n"
187
+ end
188
+ str
189
+ end
190
+
191
+ def fetch(*args)
192
+ @data.send(:fetch, *args)
193
+ end
194
+
195
+ def has_field?(field)
196
+ @data.has_key?(field)
197
+ end
198
+
199
+ def <=>(o)
200
+ o.kind_of?(self.class) && @data == o.data ? 0 : nil
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,98 @@
1
+ module HuggORM
2
+ class Query
3
+ attr_reader :table, :column_name, :values,
4
+ :_command, :_where, :_order, :_limit, :_offset, :_returning
5
+
6
+ def initialize(table, column_name = 'data')
7
+ @table = table
8
+ @column_name = column_name
9
+ @values = []
10
+ end
11
+
12
+ def run
13
+ HuggORM::Connection.db.exec_params(to_sql, @values)
14
+ end
15
+
16
+ def to_sql
17
+ sql = "#{@_command} #{@_where} #{@_order} #{@_limit} #{@_offset} #{@_returning}".strip
18
+ sql.gsub!(/\s{2,}/, ' ')
19
+ sql
20
+ end
21
+
22
+ def insert(data)
23
+ @_command = "INSERT INTO #{@table} (#{@column_name}) VALUES (#{next_placeholder})"
24
+ @values << data.to_json
25
+ pg_result = run
26
+ pg_result.cmd_tuples
27
+ end
28
+
29
+ def update(data, return_data = false)
30
+ @_command = "UPDATE #{@table} SET #{@column_name} = #{@column_name} || #{next_placeholder}::jsonb"
31
+ @values << data.to_json
32
+
33
+ if return_data
34
+ @_returning = "RETURNING #{@column_name}"
35
+ return run.map { |tuple| deserialize(tuple["#{@column_name}"]) }
36
+ end
37
+
38
+ run.cmd_tuples
39
+ end
40
+
41
+ def select
42
+ @_command = "SELECT #{@column_name} FROM #{@table}"
43
+ pg_result = run
44
+ pg_result.map { |tuple| deserialize(tuple["#{@column_name}"]) }
45
+ end
46
+
47
+ def delete
48
+ @_command = "DELETE FROM #{@table}"
49
+ run.cmd_tuples
50
+ end
51
+
52
+ def count
53
+ @_command = "SELECT COUNT(*) FROM #{@table}"
54
+ run.getvalue(0, 0).to_i
55
+ end
56
+
57
+ def where(conditions, values = [])
58
+ values.size.times { |i| conditions.sub!(/\?/, next_placeholder(i + 1)) }
59
+ @_where = "WHERE #{conditions}"
60
+ @values += values
61
+ self
62
+ end
63
+
64
+ def limit(limit, offset = nil)
65
+ @_limit = "LIMIT #{next_placeholder}"
66
+ @values << limit
67
+ self.offset(offset) if offset
68
+ self
69
+ end
70
+
71
+ def offset(offset)
72
+ @_offset = "OFFSET #{next_placeholder}"
73
+ @values << offset
74
+ self
75
+ end
76
+
77
+ def order(*args)
78
+ fields = []
79
+ args.first.each_pair { |field, order| fields << "#{wrap_json(field)} #{order}" }
80
+ @_order = "ORDER BY #{fields.join(', ')}"
81
+ self
82
+ end
83
+
84
+ private
85
+
86
+ def deserialize(data)
87
+ JSON.parse(data, symbolize_names: true)
88
+ end
89
+
90
+ def next_placeholder(offset = 1)
91
+ "$#{@values.size + offset}"
92
+ end
93
+
94
+ def wrap_json(field)
95
+ "#{@column_name}->>'#{field}'"
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,3 @@
1
+ module HuggORM
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hugg-orm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Olle Johansson
8
+ - Lúcio Rosa
9
+ - Avidity
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2016-10-31 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: pg
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '0.8'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '0.8'
29
+ - !ruby/object:Gem::Dependency
30
+ name: activesupport
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '4.2'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '4.2'
43
+ - !ruby/object:Gem::Dependency
44
+ name: bundler
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.11'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.11'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '10.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '10.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.0'
85
+ description: Ruby Object Relational Mapper for JSON-based models with PostgreSQL
86
+ email:
87
+ - code@avidity.se
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - lib/hugg_orm.rb
93
+ - lib/hugg_orm/connection.rb
94
+ - lib/hugg_orm/persistence.rb
95
+ - lib/hugg_orm/query.rb
96
+ - lib/hugg_orm/version.rb
97
+ homepage: https://github.com/avidity/hugg-orm
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.5.1
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Ruby ORM for Postgresql
121
+ test_files: []