opal-activerecord 0.0.1
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 +7 -0
- data/.gitignore +5 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +77 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +7 -0
- data/lib/opal-activerecord.rb +1 -0
- data/lib/opal/activerecord.rb +5 -0
- data/lib/opal/activerecord/version.rb +5 -0
- data/opal-activerecord.gemspec +32 -0
- data/opal/active_record.rb +1 -0
- data/opal/active_record/core.rb +545 -0
- data/opal/opal-activerecord.rb +1 -0
- data/spec/activerecord_spec.rb +297 -0
- data/spec/spec_helper.rb +28 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 04e6c4caafb2f1e23a844f1d9719cd2a1f47af68
|
4
|
+
data.tar.gz: 306d390550152f3ade7714f97844dc0679e74267
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d2dd02a27d5982fc90bd3ba984c7184b02f9b9d064f5b2aa3cd6b6c3803857979a01a8e8cc59156aa12a3ac612f6e1c39b72b2be1577220bcc6765be1b07d571
|
7
|
+
data.tar.gz: 44b465aed34c997a4657e7252b9f9c9b201e8e56532fcbb3f946dc813f49c33eee23ca5d5c14ad92d60cf808f71ad4f9f3522b76beb2629b8ea552ee975662c2
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'opal', :github => 'opal/opal'
|
6
|
+
gem 'opal-rspec', '0.3.0.beta2'
|
7
|
+
|
8
|
+
gem 'rspec' # for testing in MRI
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
# for testing compatibility with non-opal activerecord
|
12
|
+
gem 'activerecord'
|
13
|
+
gem 'sqlite3'
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/opal/opal.git
|
3
|
+
revision: 139b534a8e14719f1406318d432b305ba76db4b1
|
4
|
+
specs:
|
5
|
+
opal (0.6.0)
|
6
|
+
source_map
|
7
|
+
sprockets
|
8
|
+
|
9
|
+
PATH
|
10
|
+
remote: .
|
11
|
+
specs:
|
12
|
+
opal-activerecord (0.0.1)
|
13
|
+
opal (>= 0.5.0, < 1.0.0)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: https://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activemodel (4.0.0)
|
19
|
+
activesupport (= 4.0.0)
|
20
|
+
builder (~> 3.1.0)
|
21
|
+
activerecord (4.0.0)
|
22
|
+
activemodel (= 4.0.0)
|
23
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
24
|
+
activesupport (= 4.0.0)
|
25
|
+
arel (~> 4.0.0)
|
26
|
+
activerecord-deprecated_finders (1.0.3)
|
27
|
+
activesupport (4.0.0)
|
28
|
+
i18n (~> 0.6, >= 0.6.4)
|
29
|
+
minitest (~> 4.2)
|
30
|
+
multi_json (~> 1.3)
|
31
|
+
thread_safe (~> 0.1)
|
32
|
+
tzinfo (~> 0.3.37)
|
33
|
+
arel (4.0.1)
|
34
|
+
atomic (1.1.14)
|
35
|
+
builder (3.1.4)
|
36
|
+
diff-lcs (1.2.4)
|
37
|
+
hike (1.2.3)
|
38
|
+
i18n (0.6.5)
|
39
|
+
json (1.8.1)
|
40
|
+
minitest (4.7.5)
|
41
|
+
multi_json (1.8.4)
|
42
|
+
opal-rspec (0.3.0.beta2)
|
43
|
+
opal (>= 0.6.0, < 1.0.0)
|
44
|
+
rack (1.5.2)
|
45
|
+
rake (10.1.0)
|
46
|
+
rspec (2.14.1)
|
47
|
+
rspec-core (~> 2.14.0)
|
48
|
+
rspec-expectations (~> 2.14.0)
|
49
|
+
rspec-mocks (~> 2.14.0)
|
50
|
+
rspec-core (2.14.7)
|
51
|
+
rspec-expectations (2.14.3)
|
52
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
53
|
+
rspec-mocks (2.14.4)
|
54
|
+
source_map (3.0.1)
|
55
|
+
json
|
56
|
+
sprockets (2.10.1)
|
57
|
+
hike (~> 1.2)
|
58
|
+
multi_json (~> 1.0)
|
59
|
+
rack (~> 1.0)
|
60
|
+
tilt (~> 1.1, != 1.3.0)
|
61
|
+
sqlite3 (1.3.8)
|
62
|
+
thread_safe (0.1.3)
|
63
|
+
atomic
|
64
|
+
tilt (1.4.1)
|
65
|
+
tzinfo (0.3.38)
|
66
|
+
|
67
|
+
PLATFORMS
|
68
|
+
ruby
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
activerecord
|
72
|
+
opal!
|
73
|
+
opal-activerecord!
|
74
|
+
opal-rspec (= 0.3.0.beta2)
|
75
|
+
rake
|
76
|
+
rspec
|
77
|
+
sqlite3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) <year> <copyright holders>
|
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/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Elia Schito
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Opal: ActiveRecord
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'opal-activerecord'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
Or install it yourself as:
|
14
|
+
|
15
|
+
$ gem install opal-activerecord
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Inside your `application.js.rb`:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'active_record' # to require the whole active record lib
|
24
|
+
```
|
25
|
+
|
26
|
+
## Testing
|
27
|
+
|
28
|
+
There are two ways to run tests. You can run them inside of MRI
|
29
|
+
for ease of testing and better debuggability or you can run them
|
30
|
+
using Opal (as this is how it will actually be used).
|
31
|
+
|
32
|
+
* To run in Opal do - rake
|
33
|
+
* To run in MRI do - rspec spec
|
34
|
+
|
35
|
+
In addition to this, you can run the spec against the real active
|
36
|
+
record to make sure the tests duplicate the functionality there. To
|
37
|
+
run that:
|
38
|
+
|
39
|
+
* run_with_real_active_record=true rspec spec
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
48
|
+
|
49
|
+
## License
|
50
|
+
|
51
|
+
opal-activerecord is Copyright © 2014 Steve Tuckner. It is free software, and may be redistributed under the terms specified in the LICENSE file (an MIT License).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'opal/activerecord'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'opal/activerecord/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'opal-activerecord'
|
8
|
+
gem.version = Opal::Activerecord::VERSION
|
9
|
+
gem.authors = ['Steve Tuckner']
|
10
|
+
gem.email = ['stevetuckner@stewdle.com']
|
11
|
+
gem.summary = %q{A small port of the glorious ActiveRecord for Opal}
|
12
|
+
gem.description = %q{
|
13
|
+
This implements a subset of the rails/activerecord.
|
14
|
+
It currently handles has_many and belongs_to
|
15
|
+
associations, saving, finding and simple where
|
16
|
+
queries.
|
17
|
+
}
|
18
|
+
gem.licenses = ['MIT']
|
19
|
+
gem.homepage = 'https://github.com/boberetezeke/opal-activerecord'
|
20
|
+
gem.rdoc_options << '--main' << 'README' <<
|
21
|
+
'--line-numbers' <<
|
22
|
+
'--include' << 'opal'
|
23
|
+
|
24
|
+
gem.files = `git ls-files`.split($/)
|
25
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
26
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
27
|
+
gem.require_paths = ['lib']
|
28
|
+
|
29
|
+
gem.add_dependency 'opal', ['>= 0.5.0', '< 1.0.0']
|
30
|
+
gem.add_development_dependency 'rake'
|
31
|
+
gem.add_development_dependency 'opal-rspec'
|
32
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record/core'
|
@@ -0,0 +1,545 @@
|
|
1
|
+
class String
|
2
|
+
def singularize
|
3
|
+
/^(.*)s$/.match(self)[1]
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
def debug(str)
|
8
|
+
#puts(str) #if $debug_on
|
9
|
+
end
|
10
|
+
|
11
|
+
module Arel
|
12
|
+
class SelectManager
|
13
|
+
attr_accessor :ordering, :limit, :offset
|
14
|
+
attr_accessor :table_name, :node
|
15
|
+
|
16
|
+
def initialize(connection, table_name)
|
17
|
+
@connection = connection
|
18
|
+
@table_name = table_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def where(node)
|
22
|
+
@node = node
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
@connection.execute(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Nodes
|
31
|
+
class BinaryOp
|
32
|
+
attr_reader :left_node, :right_node
|
33
|
+
def initialize(left_node, right_node)
|
34
|
+
@left_node = left_node
|
35
|
+
@right_node = right_node
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
"BinaryNode: #{self.class}: left:#{@left_node}, right:#{@right_node}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class And < BinaryOp
|
44
|
+
def value(record)
|
45
|
+
@left_node.value(record) && @right_node.value(record)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Or < BinaryOp
|
50
|
+
def value(record)
|
51
|
+
@left_node.value(record) || @right_node.value(record)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Equality < BinaryOp
|
56
|
+
def value(record)
|
57
|
+
@left_node.value(record) == @right_node.value(record)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class NotEqual < BinaryOp
|
62
|
+
def value(record)
|
63
|
+
@left_node.value(record) != @right_node.value(record)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Literal
|
68
|
+
def initialize(value)
|
69
|
+
@value = value
|
70
|
+
end
|
71
|
+
|
72
|
+
def value(record)
|
73
|
+
@value
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
"Literal: #{@value}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Symbol
|
82
|
+
def initialize(symbol)
|
83
|
+
@symbol = symbol
|
84
|
+
end
|
85
|
+
|
86
|
+
def value(record)
|
87
|
+
record.send(@symbol)
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
"Symbol: #{@symbol}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class Ordering
|
96
|
+
attr_reader :order_str
|
97
|
+
def initialize(order_str)
|
98
|
+
@order_str = order_str
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Limit
|
103
|
+
attr_reader :limit
|
104
|
+
def initialize(limit)
|
105
|
+
@limit = limit
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
module ActiveRecord
|
112
|
+
class Association
|
113
|
+
attr_reader :foreign_key, :association_type
|
114
|
+
|
115
|
+
def initialize(klass, association_type, name, options, connection)
|
116
|
+
@association_type = association_type
|
117
|
+
@klass = klass
|
118
|
+
@name = name
|
119
|
+
@options = options
|
120
|
+
@connection = connection
|
121
|
+
if @association_type == :belongs_to
|
122
|
+
@foreign_key = "#{name}_id"
|
123
|
+
elsif @association_type == :has_many
|
124
|
+
@foreign_key = "#{@klass.table_name.singularize}_id"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def table_name
|
129
|
+
(@association_type == :belongs_to) ? @name.to_s + "s" : @name.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
def all
|
133
|
+
where(1 => 1)
|
134
|
+
end
|
135
|
+
alias load all
|
136
|
+
|
137
|
+
def where(query={})
|
138
|
+
Relation.new(query, @connection)
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
"#Association: #{@name}: #{@association_type}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class CollectionProxy
|
147
|
+
def initialize(connection, owner, association)
|
148
|
+
@connection = connection
|
149
|
+
@owner = owner
|
150
|
+
@association = association
|
151
|
+
end
|
152
|
+
|
153
|
+
def <<(collection)
|
154
|
+
debug "CollectionProxy(owner: #{@owner})#<<(#{collection})"
|
155
|
+
collection = [collection] unless collection.is_a?(Array)
|
156
|
+
collection.each do |obj|
|
157
|
+
obj.write_attribute(@association.foreign_key, @owner.id)
|
158
|
+
obj.save
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def method_missing(sym, *args, &block)
|
163
|
+
if [:first, :last, :all, :load, :reverse].include?(sym)
|
164
|
+
where_clause = "#{@owner.table_name.singularize}_id"
|
165
|
+
debug "#{sym}: for table: #{@association.table_name}, where: #{where_clause} == #{@owner.id}"
|
166
|
+
Relation.new(@connection, @association.table_name).where(where_clause => @owner.id).send(sym)
|
167
|
+
else
|
168
|
+
super
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Relation
|
174
|
+
def initialize(connection, table_name)
|
175
|
+
@select_manager = Arel::SelectManager.new(connection, table_name)
|
176
|
+
end
|
177
|
+
|
178
|
+
def execute
|
179
|
+
@records = @select_manager.execute
|
180
|
+
end
|
181
|
+
|
182
|
+
def where(query)
|
183
|
+
key, value = query.first
|
184
|
+
node = eq_node(key, value)
|
185
|
+
if query.keys.size > 1
|
186
|
+
query.to_a[1..-1].each do |key, value|
|
187
|
+
node = Arel::Nodes::And.new(node, eq_node(key, value))
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
@select_manager.where(node)
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
def order(order_str)
|
196
|
+
@select_manager.ordering = Arel::Nodes::Ordering.new(order_str)
|
197
|
+
self
|
198
|
+
end
|
199
|
+
|
200
|
+
def limit(num)
|
201
|
+
@select_manager.limit = Arel::Nodes::Limit.new(num)
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
def offset(index)
|
206
|
+
@select_manager.offset = Arel::Nodes::Offset.new(index)
|
207
|
+
self
|
208
|
+
end
|
209
|
+
|
210
|
+
def first
|
211
|
+
execute.first
|
212
|
+
end
|
213
|
+
|
214
|
+
def last
|
215
|
+
execute.last
|
216
|
+
end
|
217
|
+
|
218
|
+
def reverse
|
219
|
+
execute.reverse
|
220
|
+
end
|
221
|
+
|
222
|
+
def [](index)
|
223
|
+
execute[index]
|
224
|
+
end
|
225
|
+
|
226
|
+
def all
|
227
|
+
execute
|
228
|
+
end
|
229
|
+
alias load all
|
230
|
+
|
231
|
+
def each
|
232
|
+
execute.each { |record| yield record }
|
233
|
+
end
|
234
|
+
|
235
|
+
def eq_node(key, value)
|
236
|
+
Arel::Nodes::Equality.new(Arel::Nodes::Symbol.new(key), Arel::Nodes::Literal.new(value))
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class MemoryStore
|
241
|
+
attr_reader :tables
|
242
|
+
|
243
|
+
def initialize
|
244
|
+
@tables = {}
|
245
|
+
@next_ids = {}
|
246
|
+
end
|
247
|
+
|
248
|
+
def execute(select_manager)
|
249
|
+
debug "MemoryStore#execute: table name = #{select_manager.table_name}"
|
250
|
+
debug "MemoryStore#execute: tables = #{@tables.keys}"
|
251
|
+
debug "MemoryStore#node = #{select_manager.node}"
|
252
|
+
records = @tables[select_manager.table_name.to_s].values.select do |record|
|
253
|
+
if select_manager.node
|
254
|
+
debug "MemoryStore#execute: checking record: #{record}"
|
255
|
+
select_manager.node.value(record)
|
256
|
+
else
|
257
|
+
true
|
258
|
+
end
|
259
|
+
end
|
260
|
+
debug "MemoryStore#execute: result = #{records.inspect}"
|
261
|
+
records
|
262
|
+
end
|
263
|
+
|
264
|
+
def push(table_name, record)
|
265
|
+
init_new_table(table_name)
|
266
|
+
@tables[table_name][record.id] = record
|
267
|
+
end
|
268
|
+
|
269
|
+
def find(table_name, id)
|
270
|
+
if @tables[table_name]
|
271
|
+
record = @tables[table_name][id]
|
272
|
+
else
|
273
|
+
record = nil
|
274
|
+
end
|
275
|
+
|
276
|
+
raise "record not found" unless record
|
277
|
+
record
|
278
|
+
end
|
279
|
+
|
280
|
+
def on_change(&call_back)
|
281
|
+
@change_callback = call_back
|
282
|
+
end
|
283
|
+
|
284
|
+
def create(table_name, record)
|
285
|
+
debug "MemoryStore#Create(#{record})"
|
286
|
+
init_new_table(table_name)
|
287
|
+
next_id = gen_next_id(table_name)
|
288
|
+
@tables[table_name][next_id] = record
|
289
|
+
@change_callback.call(:insert, record) if @change_callback
|
290
|
+
return next_id
|
291
|
+
end
|
292
|
+
|
293
|
+
def update(table_name, record)
|
294
|
+
init_new_table(table_name)
|
295
|
+
table = @tables[table_name]
|
296
|
+
@change_callback.call(:update, record) if @change_callback && record.attributes != table[record.id]
|
297
|
+
table[record.id] = record
|
298
|
+
end
|
299
|
+
|
300
|
+
def destroy(table_name, record)
|
301
|
+
@change_callback.call(:delete, record) if @change_callback
|
302
|
+
@tables[table_name].delete(record.id)
|
303
|
+
end
|
304
|
+
|
305
|
+
def gen_next_id(table_name)
|
306
|
+
next_id = @next_ids[table_name]
|
307
|
+
@next_ids[table_name] += 1
|
308
|
+
return "T-#{next_id}"
|
309
|
+
end
|
310
|
+
|
311
|
+
def init_new_table(table_name)
|
312
|
+
@tables[table_name] ||= {}
|
313
|
+
@next_ids[table_name] ||= 1
|
314
|
+
end
|
315
|
+
|
316
|
+
def to_s
|
317
|
+
"tables: #{@tables.inspect}, next_ids: #{@next_ids.inspect}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
class Base
|
322
|
+
attr_accessor :attributes, :observers
|
323
|
+
|
324
|
+
def self.new_from_json(json)
|
325
|
+
object = self.new
|
326
|
+
object.attributes = json
|
327
|
+
object
|
328
|
+
end
|
329
|
+
|
330
|
+
def self.accepts_nested_attributes_for(*args)
|
331
|
+
end
|
332
|
+
|
333
|
+
def self.default_scope(*args)
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.scope(*args)
|
337
|
+
end
|
338
|
+
|
339
|
+
def self.has_many(name, options={})
|
340
|
+
@associations ||= {}
|
341
|
+
@associations[name.to_s] = Association.new(self, :has_many, name, options, @connection)
|
342
|
+
end
|
343
|
+
|
344
|
+
def self.belongs_to(name, options={})
|
345
|
+
@associations ||= {}
|
346
|
+
@associations[name.to_s] = Association.new(self, :belongs_to, name, options, @connection)
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.table_name
|
350
|
+
self.to_s.downcase + "s"
|
351
|
+
end
|
352
|
+
|
353
|
+
def self.find(id)
|
354
|
+
connection.find(table_name, id)
|
355
|
+
end
|
356
|
+
|
357
|
+
def self.associations
|
358
|
+
@associations || {}
|
359
|
+
end
|
360
|
+
|
361
|
+
def self.after_initialize(sym)
|
362
|
+
@after_initialize_callback = sym
|
363
|
+
end
|
364
|
+
|
365
|
+
def self.connection
|
366
|
+
# FIXME: Base.connection seems very hacky, ideally I would like
|
367
|
+
# to do something like super.respond_to?(:connection)
|
368
|
+
#
|
369
|
+
@connection || Base.connection
|
370
|
+
end
|
371
|
+
|
372
|
+
def self.connection=(connection)
|
373
|
+
@connection = connection
|
374
|
+
end
|
375
|
+
|
376
|
+
def self.create(*args)
|
377
|
+
obj = self.new(*args)
|
378
|
+
#obj.save
|
379
|
+
obj
|
380
|
+
end
|
381
|
+
|
382
|
+
def self.method_missing(sym, *args)
|
383
|
+
if [:first, :last, :all, :where].include?(sym)
|
384
|
+
Relation.new(connection, table_name).send(sym, *args)
|
385
|
+
else
|
386
|
+
super
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def self.new(*args)
|
391
|
+
super(*args)
|
392
|
+
end
|
393
|
+
|
394
|
+
def initialize(initializers={})
|
395
|
+
@attributes = {}
|
396
|
+
@associations = {}
|
397
|
+
@observers = {}
|
398
|
+
initializers.each do |initializer, value|
|
399
|
+
@attributes[initializer.to_s] = value
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def method_missing(sym, *args)
|
404
|
+
method_name = sym.to_s
|
405
|
+
debug "Base#method_missing: #{method_name}, #{attributes}"
|
406
|
+
if m = /(.*)=$/.match(method_name)
|
407
|
+
val = write_value(m[1], args.shift)
|
408
|
+
else
|
409
|
+
val = read_value(method_name)
|
410
|
+
end
|
411
|
+
debug "Base#method_missing (at end), val = #{val}"
|
412
|
+
val
|
413
|
+
end
|
414
|
+
|
415
|
+
# stolen from ActiveRecord:core.rb
|
416
|
+
def ==(comparison_object)
|
417
|
+
super ||
|
418
|
+
comparison_object.instance_of?(self.class) &&
|
419
|
+
!id.nil? &&
|
420
|
+
comparison_object.id == id
|
421
|
+
end
|
422
|
+
alias :eql? :==
|
423
|
+
|
424
|
+
def on_change(sym, &block)
|
425
|
+
str = sym.to_s
|
426
|
+
self.observers ||= {}
|
427
|
+
self.observers[str] ||= []
|
428
|
+
self.observers[str].push(block)
|
429
|
+
debug "Base#on_change: self.observers = #{self.observers.inspect}"
|
430
|
+
end
|
431
|
+
|
432
|
+
def write_attribute(attribute_name, new_value)
|
433
|
+
attribute_name = attribute_name.to_s
|
434
|
+
old_value = self.attributes[attribute_name]
|
435
|
+
self.attributes[attribute_name] = new_value
|
436
|
+
|
437
|
+
if self.observers[attribute_name] then
|
438
|
+
self.observers[attribute_name].each do |observer|
|
439
|
+
observer.call(old_value, new_value)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def read_attribute(attribute_name)
|
445
|
+
self.attributes[attribute_name.to_s]
|
446
|
+
end
|
447
|
+
|
448
|
+
def write_value(name, new_value)
|
449
|
+
assoc = self.class.associations[name]
|
450
|
+
if assoc
|
451
|
+
if self.id
|
452
|
+
if assoc.association_type == :has_many
|
453
|
+
new_value.each do |value|
|
454
|
+
value.write_attribute("#{table_name.singularize}_id", self.id)
|
455
|
+
value.save
|
456
|
+
end
|
457
|
+
elsif assoc.association_type == :belongs_to
|
458
|
+
if new_value.id
|
459
|
+
write_attribute("#{new_value.table_name}_id", new_value.id)
|
460
|
+
|
461
|
+
else
|
462
|
+
write_attribute(name, new_value)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
else
|
466
|
+
write_attribute(name, new_value)
|
467
|
+
end
|
468
|
+
else
|
469
|
+
write_attribute(name, new_value)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def read_value(name)
|
474
|
+
debug "Base#read_value, name = #{name}, self.class.associations = #{self.class.associations.inspect}"
|
475
|
+
if assoc = self.class.associations[name]
|
476
|
+
val = (assoc.association_type == :has_many)
|
477
|
+
if assoc.association_type == :has_many
|
478
|
+
if self.id
|
479
|
+
CollectionProxy.new(connection, self, assoc)
|
480
|
+
else
|
481
|
+
read_attribute(name) || []
|
482
|
+
end
|
483
|
+
elsif assoc.association_type == :belongs_to
|
484
|
+
if self.id
|
485
|
+
Relation.new(connection, assoc.table_name).where("id" => read_attribute("#{assoc.table_name}_id")).first
|
486
|
+
else
|
487
|
+
read_attribute(name)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
else
|
491
|
+
read_attribute(name)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
def save
|
496
|
+
debug "save: memory(before) = #{connection}"
|
497
|
+
debug "save: self(before): #{self}"
|
498
|
+
self.class.associations.to_a.select do |name_and_assoc|
|
499
|
+
name = name_and_assoc[0]
|
500
|
+
assoc = name_and_assoc[1]
|
501
|
+
assoc.association_type == :belongs_to
|
502
|
+
end.each do |name_and_assoc|
|
503
|
+
name = name_and_assoc[0]
|
504
|
+
assoc = name_and_assoc[1]
|
505
|
+
debug "name = #{name}, #{assoc}"
|
506
|
+
debug "value = #{read_attribute(name).inspect}"
|
507
|
+
belongs_to_value = read_attribute(name)
|
508
|
+
if belongs_to_value
|
509
|
+
debug "save: has belongs_to_value: id(#{belongs_to_value.id}), #{belongs_to_value.attributes}"
|
510
|
+
belongs_to_value.save unless belongs_to_value.id
|
511
|
+
@attributes["#{name}_id"] = belongs_to_value.id
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
debug "save: self(after): #{self}"
|
516
|
+
if self.id
|
517
|
+
connection.update(table_name, self)
|
518
|
+
else
|
519
|
+
@attributes['id'] = connection.create(table_name, self)
|
520
|
+
end
|
521
|
+
|
522
|
+
debug "save: memory(after) = #{connection}"
|
523
|
+
end
|
524
|
+
|
525
|
+
def destroy
|
526
|
+
@connection.destroy(table_name, self)
|
527
|
+
end
|
528
|
+
|
529
|
+
def id
|
530
|
+
@attributes['id']
|
531
|
+
end
|
532
|
+
|
533
|
+
def table_name
|
534
|
+
self.class.table_name
|
535
|
+
end
|
536
|
+
|
537
|
+
def connection
|
538
|
+
self.class.connection
|
539
|
+
end
|
540
|
+
|
541
|
+
def to_s
|
542
|
+
"#{self.class}:#{self.attributes}"
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record'
|
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class A < ActiveRecord::Base
|
4
|
+
end
|
5
|
+
|
6
|
+
class B < ActiveRecord::Base
|
7
|
+
has_many :cs
|
8
|
+
end
|
9
|
+
|
10
|
+
class C < ActiveRecord::Base
|
11
|
+
belongs_to :b
|
12
|
+
has_many :ds
|
13
|
+
end
|
14
|
+
|
15
|
+
class D < ActiveRecord::Base
|
16
|
+
belongs_to :c
|
17
|
+
belongs_to :e
|
18
|
+
end
|
19
|
+
|
20
|
+
class E < ActiveRecord::Base
|
21
|
+
has_many :ds
|
22
|
+
has_many :cs, :through => :ds
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "ActiveRecord::Base" do
|
26
|
+
if running_with_real_active_record
|
27
|
+
before do
|
28
|
+
ActiveRecord::Base.establish_connection(
|
29
|
+
adapter: 'sqlite3',
|
30
|
+
database: 'test.sqlite3'
|
31
|
+
)
|
32
|
+
ActiveRecord::Base.connection.create_table("as") {|t| t.integer :x; t.integer :y}
|
33
|
+
ActiveRecord::Base.connection.create_table("bs") {|t| t.integer :x; t.integer :y}
|
34
|
+
ActiveRecord::Base.connection.create_table("cs") {|t| t.integer :x; t.integer :y; t.integer :b_id}
|
35
|
+
ActiveRecord::Base.connection.create_table("ds") {|t| t.integer :x; t.integer :y; t.integer :c_id; t.integer :e_id}
|
36
|
+
ActiveRecord::Base.connection.create_table("es") {|t| t.integer :x; t.integer :y}
|
37
|
+
end
|
38
|
+
|
39
|
+
after do
|
40
|
+
ActiveRecord::Base.connection.drop_table("as")
|
41
|
+
ActiveRecord::Base.connection.drop_table("bs")
|
42
|
+
ActiveRecord::Base.connection.drop_table("cs")
|
43
|
+
ActiveRecord::Base.connection.drop_table("ds")
|
44
|
+
ActiveRecord::Base.connection.drop_table("es")
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# only set memory_store for opal
|
48
|
+
let(:memory_store) { ActiveRecord::MemoryStore.new }
|
49
|
+
before do
|
50
|
+
ActiveRecord::Base.connection = memory_store
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if !running_with_real_active_record
|
55
|
+
describe ".new_from_json" do
|
56
|
+
context "when constructing just one object" do
|
57
|
+
it "should set attributes on a class with no relationships" do
|
58
|
+
a = A.new_from_json({"x" => 1, "y" => 2})
|
59
|
+
expect(a.x).to eq(1)
|
60
|
+
expect(a.y).to eq(2)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should set attributes on a class with a has_many relationship" do
|
64
|
+
b = B.new_from_json({"x" => 1, "y" => 2})
|
65
|
+
expect(b.x).to eq(1)
|
66
|
+
expect(b.y).to eq(2)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should set attributes on a class with a belongs_to relationship" do
|
70
|
+
d = D.new_from_json({"x" => 1, "y" => 2})
|
71
|
+
expect(d.x).to eq(1)
|
72
|
+
expect(d.y).to eq(2)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
=begin
|
77
|
+
context "when contructing an object with a has_many that contains embedded has_many objects" do
|
78
|
+
it "should create the first object and the has_many objects" do
|
79
|
+
b = B.new_from_json({x: 1, y: 2, cs: [{s: 3, t: 4}]})
|
80
|
+
expect(b.x).to eq(1)
|
81
|
+
expect(b.y).to eq(2)
|
82
|
+
expect(b.c.size).to eq(1)
|
83
|
+
#expect(b.c.first.s).to eq(3)
|
84
|
+
#expect(b.c.first.t).to eq(4)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
=end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when using an active record model with no associations" do
|
92
|
+
it "should be true" do
|
93
|
+
expect(nil).to eq(nil)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when using an active record model with no associations" do
|
98
|
+
|
99
|
+
context "when starting with a new model" do
|
100
|
+
let(:a) { A.new(x:1) }
|
101
|
+
|
102
|
+
context "when testing for equality" do
|
103
|
+
let(:a1) { A.new(x:1, y:1) }
|
104
|
+
let(:a2) { A.new(x:1, y:1) }
|
105
|
+
|
106
|
+
it "is equal if the objects are the same object" do
|
107
|
+
expect(a1 == a1).to eq(true)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "is not equal if the objects are different objects" do
|
111
|
+
expect(a1 == a2).to eq(false)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "can create one" do
|
116
|
+
expect(a.x).to eq(1)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "has no id" do
|
120
|
+
expect(a.id).to be_nil
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has an id after save" do
|
124
|
+
a.save
|
125
|
+
expect(a.id).to_not be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
if !running_with_real_active_record
|
129
|
+
it "is in the store after the save" do
|
130
|
+
a.save
|
131
|
+
expect(memory_store.tables["as"].size).to eq(1)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when searching for objects" do
|
138
|
+
let(:a1) { A.new(x:1, y:1) }
|
139
|
+
let(:a2) { A.new(x:1, y:2) }
|
140
|
+
let(:a3) { A.new(x:2, y:2) }
|
141
|
+
|
142
|
+
before do
|
143
|
+
a1.save
|
144
|
+
a2.save
|
145
|
+
a3.save
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when testing for equality" do
|
149
|
+
it "is equal if the objects have the same id" do
|
150
|
+
expect(a1 == A.first).to eq(true)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "is not equal if the objects are different objects" do
|
154
|
+
expect(a2 == A.first).to eq(false)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "finds the first object saved when using first" do
|
159
|
+
expect(A.first.x).to eq(1)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "finds the last object saved when using last" do
|
163
|
+
expect(A.last.x).to eq(2)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "returns all records when using load" do
|
167
|
+
expect(A.all.map{|a| a.x}.sort).to eq([1,1,2])
|
168
|
+
end
|
169
|
+
|
170
|
+
it "returns the record with 1 when using where" do
|
171
|
+
expect(A.where(x:1,y:1).load.map{|a| [a.x,a.y]}).to eq([[1,1]])
|
172
|
+
end
|
173
|
+
|
174
|
+
it "returns the record with x:1 and y:3 when using where" do
|
175
|
+
expect(A.where(x:1).load.map{|a| a.y}).to eq([1,2])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context "when using an active record models with has_many/belongs_to associations" do
|
181
|
+
let(:b) { B.new(x:1) }
|
182
|
+
let(:c) { C.new(y:1) }
|
183
|
+
let(:d) { D.new(x:1, y:1) }
|
184
|
+
|
185
|
+
context "when the objects are not yet saved" do
|
186
|
+
|
187
|
+
it "has a B object with [] for the C association" do
|
188
|
+
expect(b.cs).to eq([])
|
189
|
+
end
|
190
|
+
|
191
|
+
it "has a C object with nil for the B association" do
|
192
|
+
expect(c.b).to be_nil
|
193
|
+
end
|
194
|
+
|
195
|
+
it "allows setting of C object on B" do
|
196
|
+
b.cs = [c]
|
197
|
+
expect(b.cs).to eq([c])
|
198
|
+
end
|
199
|
+
|
200
|
+
it "allows setting of B object on C" do
|
201
|
+
c.b = b
|
202
|
+
expect(c.b).to eq(b)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "saves b when saving c" do
|
206
|
+
c.b = b
|
207
|
+
c.save
|
208
|
+
expect(b.id).to_not be_nil
|
209
|
+
end
|
210
|
+
|
211
|
+
it "saves b and c when saving d" do
|
212
|
+
c.b = b
|
213
|
+
d.c = c
|
214
|
+
d.save
|
215
|
+
expect(c.id).to_not be_nil
|
216
|
+
expect(b.id).to_not be_nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context "when the has many side is saved only" do
|
221
|
+
before do
|
222
|
+
b.save
|
223
|
+
end
|
224
|
+
|
225
|
+
context "when setting cs" do
|
226
|
+
it "allows setting of C object on B" do
|
227
|
+
b.cs = [c]
|
228
|
+
expect(b.cs.load).to eq([c])
|
229
|
+
end
|
230
|
+
|
231
|
+
it "saves C object when associating it with B" do
|
232
|
+
b.cs = [c]
|
233
|
+
expect(c.id).to_not be_nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when appending to cs" do
|
238
|
+
it "allows setting of C object on B" do
|
239
|
+
b.cs << c
|
240
|
+
expect(b.cs.load).to eq([c])
|
241
|
+
end
|
242
|
+
|
243
|
+
it "saves C object when associating it with B" do
|
244
|
+
b.cs << c
|
245
|
+
expect(c.id).to_not be_nil
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context "when the belongs_to side is saved only" do
|
251
|
+
before do
|
252
|
+
c.save
|
253
|
+
end
|
254
|
+
|
255
|
+
context "when setting b" do
|
256
|
+
|
257
|
+
it "doesn't save b on assignment to c" do
|
258
|
+
c.b = b
|
259
|
+
expect(b.id).to be_nil
|
260
|
+
end
|
261
|
+
|
262
|
+
it "saves b when saving c" do
|
263
|
+
c.b = b
|
264
|
+
c.save
|
265
|
+
expect(b.id).to_not be_nil
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context "when objects are saved" do
|
271
|
+
before do
|
272
|
+
b.save
|
273
|
+
c.save
|
274
|
+
end
|
275
|
+
|
276
|
+
it "has a B object with [] for the C association" do
|
277
|
+
expect(b.cs.load).to eq([])
|
278
|
+
end
|
279
|
+
|
280
|
+
it "has a C object with nil for the B association" do
|
281
|
+
expect(c.b).to be_nil
|
282
|
+
end
|
283
|
+
|
284
|
+
it "allows setting of C object on B" do
|
285
|
+
b.cs = [c]
|
286
|
+
expect(b.cs.load).to eq([c])
|
287
|
+
end
|
288
|
+
|
289
|
+
it "allows setting of B object on C" do
|
290
|
+
c.b = b
|
291
|
+
c.save
|
292
|
+
expect(c.b).to eq(b)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
if ENV['run_with_real_active_record']
|
3
|
+
Bundler.require(:test)
|
4
|
+
require "active_record"
|
5
|
+
else
|
6
|
+
if RUBY_ENGINE == "opal"
|
7
|
+
require 'opal-rspec'
|
8
|
+
require 'opal-activerecord'
|
9
|
+
else
|
10
|
+
require_relative '../opal/active_record/core'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
module TestUnitHelpers
|
16
|
+
def assert_equal actual, expected
|
17
|
+
actual.should == expected
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
config.include TestUnitHelpers
|
23
|
+
end
|
24
|
+
|
25
|
+
def running_with_real_active_record
|
26
|
+
ENV['run_with_real_active_record']
|
27
|
+
end
|
28
|
+
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: opal-activerecord
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Tuckner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: opal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.0
|
20
|
+
- - <
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.5.0
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: opal-rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
description: "\n This implements a subset of the rails/activerecord.\n
|
62
|
+
\ It currently handles has_many and belongs_to\n associations,
|
63
|
+
saving, finding and simple where\n queries. \n "
|
64
|
+
email:
|
65
|
+
- stevetuckner@stewdle.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- Gemfile
|
72
|
+
- Gemfile.lock
|
73
|
+
- LICENSE
|
74
|
+
- LICENSE.txt
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- lib/opal-activerecord.rb
|
78
|
+
- lib/opal/activerecord.rb
|
79
|
+
- lib/opal/activerecord/version.rb
|
80
|
+
- opal-activerecord.gemspec
|
81
|
+
- opal/active_record.rb
|
82
|
+
- opal/active_record/core.rb
|
83
|
+
- opal/opal-activerecord.rb
|
84
|
+
- spec/activerecord_spec.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
homepage: https://github.com/boberetezeke/opal-activerecord
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
metadata: {}
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options:
|
92
|
+
- --main
|
93
|
+
- README
|
94
|
+
- --line-numbers
|
95
|
+
- --include
|
96
|
+
- opal
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.0.3
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: A small port of the glorious ActiveRecord for Opal
|
115
|
+
test_files:
|
116
|
+
- spec/activerecord_spec.rb
|
117
|
+
- spec/spec_helper.rb
|