opium 1.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +24 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +10 -0
- data/lib/generators/opium/config_generator.rb +15 -0
- data/lib/generators/opium/model_generator.rb +33 -0
- data/lib/generators/opium/templates/config.yml +27 -0
- data/lib/generators/opium/templates/model.rb +10 -0
- data/lib/opium/config.rb +44 -0
- data/lib/opium/extensions/array.rb +10 -0
- data/lib/opium/extensions/boolean.rb +13 -0
- data/lib/opium/extensions/date.rb +18 -0
- data/lib/opium/extensions/date_time.rb +18 -0
- data/lib/opium/extensions/false_class.rb +7 -0
- data/lib/opium/extensions/float.rb +13 -0
- data/lib/opium/extensions/geo_point.rb +37 -0
- data/lib/opium/extensions/hash.rb +43 -0
- data/lib/opium/extensions/integer.rb +13 -0
- data/lib/opium/extensions/numeric.rb +7 -0
- data/lib/opium/extensions/object.rb +15 -0
- data/lib/opium/extensions/pointer.rb +20 -0
- data/lib/opium/extensions/regexp.rb +12 -0
- data/lib/opium/extensions/string.rb +20 -0
- data/lib/opium/extensions/time.rb +19 -0
- data/lib/opium/extensions/true_class.rb +7 -0
- data/lib/opium/extensions.rb +16 -0
- data/lib/opium/model/attributable.rb +37 -0
- data/lib/opium/model/callbacks.rb +38 -0
- data/lib/opium/model/connectable.rb +155 -0
- data/lib/opium/model/criteria.rb +123 -0
- data/lib/opium/model/dirty.rb +35 -0
- data/lib/opium/model/field.rb +31 -0
- data/lib/opium/model/fieldable.rb +57 -0
- data/lib/opium/model/findable.rb +20 -0
- data/lib/opium/model/kaminari/queryable.rb +46 -0
- data/lib/opium/model/kaminari/scopable.rb +15 -0
- data/lib/opium/model/kaminari.rb +4 -0
- data/lib/opium/model/persistable.rb +153 -0
- data/lib/opium/model/queryable.rb +150 -0
- data/lib/opium/model/scopable.rb +58 -0
- data/lib/opium/model/serialization.rb +13 -0
- data/lib/opium/model.rb +47 -0
- data/lib/opium/railtie.rb +14 -0
- data/lib/opium/user.rb +44 -0
- data/lib/opium/version.rb +3 -0
- data/lib/opium.rb +9 -0
- data/opium.gemspec +40 -0
- data/spec/opium/config/opium.yml +5 -0
- data/spec/opium/config_spec.rb +56 -0
- data/spec/opium/extensions/array_spec.rb +34 -0
- data/spec/opium/extensions/boolean_spec.rb +28 -0
- data/spec/opium/extensions/date_spec.rb +55 -0
- data/spec/opium/extensions/date_time_spec.rb +55 -0
- data/spec/opium/extensions/float_spec.rb +42 -0
- data/spec/opium/extensions/geo_point_spec.rb +55 -0
- data/spec/opium/extensions/hash_spec.rb +76 -0
- data/spec/opium/extensions/integer_spec.rb +42 -0
- data/spec/opium/extensions/object_spec.rb +24 -0
- data/spec/opium/extensions/pointer_spec.rb +28 -0
- data/spec/opium/extensions/regexp_spec.rb +23 -0
- data/spec/opium/extensions/string_spec.rb +65 -0
- data/spec/opium/extensions/time_spec.rb +55 -0
- data/spec/opium/model/attributable_spec.rb +45 -0
- data/spec/opium/model/callbacks_spec.rb +59 -0
- data/spec/opium/model/connectable_spec.rb +218 -0
- data/spec/opium/model/criteria_spec.rb +285 -0
- data/spec/opium/model/dirty_spec.rb +39 -0
- data/spec/opium/model/fieldable_spec.rb +133 -0
- data/spec/opium/model/findable_spec.rb +57 -0
- data/spec/opium/model/kaminari/queryable_spec.rb +22 -0
- data/spec/opium/model/kaminari/scopable_spec.rb +20 -0
- data/spec/opium/model/kaminari_spec.rb +104 -0
- data/spec/opium/model/persistable_spec.rb +367 -0
- data/spec/opium/model/queryable_spec.rb +338 -0
- data/spec/opium/model/scopable_spec.rb +115 -0
- data/spec/opium/model/serialization_spec.rb +51 -0
- data/spec/opium/model_spec.rb +49 -0
- data/spec/opium/user_spec.rb +195 -0
- data/spec/opium_spec.rb +5 -0
- data/spec/spec_helper.rb +25 -0
- metadata +400 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7949dfc491925078b8d64f4b093524530d183608
|
4
|
+
data.tar.gz: d9949725cb81e568d62bd46c1be3fb28af0c2b08
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 012d1e38fab68ab65636e981b5c06861dd6acdee6e4d0d025ee9ac72b6a9c7216e3fd15e4a9b999adff35fcb706253520f78d5fda386430240ad31511d67b6a3
|
7
|
+
data.tar.gz: 496fd7162e76f218f08caaa37cb300bb3662843577c4c77f4cb31ab98e00f8f34606cab80540ba482c9bda15fc0069c429284f584c5f98bd7db0cb36e50ed462
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.DS_Store
|
24
|
+
local/
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Joshua Bowers
|
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,71 @@
|
|
1
|
+
# Opium
|
2
|
+
|
3
|
+
Provides an intuitive, Mongoid-inspired mapping layer between your application's object space and Parse.
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/joshuabowers/opium.svg?branch=master)](https://travis-ci.org/joshuabowers/opium)
|
6
|
+
[![Coverage Status](https://img.shields.io/coveralls/joshuabowers/opium.svg)](https://coveralls.io/r/joshuabowers/opium)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/joshuabowers/opium/badges/gpa.svg)](https://codeclimate.com/github/joshuabowers/opium)
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'opium'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install opium
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Within Rails
|
26
|
+
|
27
|
+
Opium will automatically establish itself as the default ORM for Rails.
|
28
|
+
|
29
|
+
#### ORM Configuration
|
30
|
+
|
31
|
+
Create a config file to communicate with your Parse database by running the config generator:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
$ rails g opium:config
|
35
|
+
```
|
36
|
+
|
37
|
+
See the generated file at `config/opium.yml` for more details
|
38
|
+
|
39
|
+
#### Model Generator
|
40
|
+
|
41
|
+
A generator exists for creating new models; this should be invoked whenever `rails g model` gets invoked.
|
42
|
+
|
43
|
+
```bash
|
44
|
+
$ rails g model game title:string price:float
|
45
|
+
```
|
46
|
+
|
47
|
+
### Specifying a model
|
48
|
+
|
49
|
+
Models are defined by mixing in `Opium::Model` into a new class. Class names should match the names of the
|
50
|
+
classes defined within Parse. You can define fields on your model which mirror the columns within a Parse
|
51
|
+
class.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class Game
|
55
|
+
include Opium::Model
|
56
|
+
|
57
|
+
field :title, type: String
|
58
|
+
field :price, type: Float
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
All models automatically come with three fields: *:id*, *:created_at*, and *:updated_at*. Field names are
|
63
|
+
converted from a native ruby snake_case naming convention to a Parse lowerCamel convention.
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it ( https://github.com/[my-github-username]/opium/fork )
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
# Default directory to look in is `/specs`
|
5
|
+
# Run with `rake spec`
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
7
|
+
task.rspec_opts = %w[--color --format documentation]
|
8
|
+
end
|
9
|
+
|
10
|
+
task default: :spec
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Opium
|
4
|
+
module Generators
|
5
|
+
class ConfigGenerator < ::Rails::Generators::Base
|
6
|
+
source_root File.expand_path( "../templates/", __FILE__ )
|
7
|
+
|
8
|
+
desc "Creates an Opium configuration file at config/opium.yml"
|
9
|
+
|
10
|
+
def create_config_file
|
11
|
+
copy_file 'config.yml', File.join( 'config', 'opium.yml' )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rails/generators/named_base'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Generators
|
5
|
+
class GeneratedAttribute
|
6
|
+
def type_class
|
7
|
+
type.to_s.camelcase
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Opium
|
14
|
+
module Generators
|
15
|
+
class ModelGenerator < ::Rails::Generators::NamedBase
|
16
|
+
source_root File.expand_path( "../templates/", __FILE__ )
|
17
|
+
|
18
|
+
desc "Creates an Opium model"
|
19
|
+
|
20
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
21
|
+
|
22
|
+
check_class_collision
|
23
|
+
|
24
|
+
class_option :parent, type: :string, desc: "The parent model for the generated model when using STI"
|
25
|
+
|
26
|
+
def create_model_file
|
27
|
+
template "model.rb", File.join( "app/models", class_path, "#{file_name}.rb" )
|
28
|
+
end
|
29
|
+
|
30
|
+
hook_for :test_framework
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
development:
|
2
|
+
# The Application ID for your Parse app. Mandatory
|
3
|
+
app_id: PARSE-APP-ID
|
4
|
+
|
5
|
+
# The REST API key for your Parse app. Mandatory
|
6
|
+
api_key: PARSE-API-KEY
|
7
|
+
|
8
|
+
# Your Parse app's master key setting. You may omit this,
|
9
|
+
# but you will be unable to edit Users except by ACL rules
|
10
|
+
master_key: PARSE-MASTER-KEY
|
11
|
+
|
12
|
+
# Any communications done with parse will either be (true) displayed or (false) silenced
|
13
|
+
log_network_responses: true
|
14
|
+
|
15
|
+
test:
|
16
|
+
app_id: PARSE-TEST-APP-ID
|
17
|
+
api_key: PARSE-TEST-API-KEY
|
18
|
+
master_key: PARSE-TEST-MASTER-KEY
|
19
|
+
log_network_responses: true
|
20
|
+
|
21
|
+
# You could hardcode the values for the production Parse app here, but it is suggested
|
22
|
+
# you set these through the suggested environment variables.
|
23
|
+
production:
|
24
|
+
app_id: <%= ENV['PARSE_APP_ID'] %>
|
25
|
+
api_key: <%= ENV['PARSE_API_KEY'] %>
|
26
|
+
master_key: <%= ENV['PARSE_MASTER_KEY'] %>
|
27
|
+
log_network_responses: false
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent] %>
|
3
|
+
<% unless options[:parent] -%>
|
4
|
+
include Opium::Model
|
5
|
+
<% end -%>
|
6
|
+
<% attributes.reject {|attr| attr.reference?}.each do |attribute| -%>
|
7
|
+
field :<%= attribute.name %><%= ", type: #{ attribute.type_class }" if attribute.type_class %>
|
8
|
+
<% end -%>
|
9
|
+
end
|
10
|
+
<% end -%>
|
data/lib/opium/config.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Opium
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def configure
|
5
|
+
yield config
|
6
|
+
end
|
7
|
+
|
8
|
+
def config
|
9
|
+
@config ||= Opium::Config.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def load!( path, environment = nil )
|
13
|
+
settings = load_yaml( path, environment )
|
14
|
+
configure do |config|
|
15
|
+
settings.each do |key, value|
|
16
|
+
config.send("#{key}=", value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
@config = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def load_yaml( path, environment = nil )
|
28
|
+
env = environment ? environment.to_s : env_name
|
29
|
+
YAML.load(ERB.new(File.new(path).read).result)[env]
|
30
|
+
end
|
31
|
+
|
32
|
+
def env_name
|
33
|
+
defined?( Rails ) ? Rails.env : ( ENV["RACK_ENV"] || ENV["OPIUM_ENV"] || raise( "Could not determine environment" ) )
|
34
|
+
end
|
35
|
+
|
36
|
+
class Config
|
37
|
+
include ActiveSupport::Configurable
|
38
|
+
|
39
|
+
config_accessor( :app_id ) { 'PARSE_APP_ID' }
|
40
|
+
config_accessor( :api_key ) { 'PARSE_API_KEY' }
|
41
|
+
config_accessor( :master_key ){ 'PARSE_MASTER_KEY' }
|
42
|
+
config_accessor( :log_network_responses ) { false }
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Date
|
2
|
+
def to_parse
|
3
|
+
{
|
4
|
+
'__type' => 'Date',
|
5
|
+
'iso' => self.iso8601
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def to_ruby(object)
|
11
|
+
object.to_date if object
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_parse(object)
|
15
|
+
object.to_date.to_parse if object
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class DateTime < Date
|
2
|
+
def to_parse
|
3
|
+
{
|
4
|
+
'__type' => 'Date',
|
5
|
+
'iso' => self.iso8601
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def to_ruby(object)
|
11
|
+
object.to_datetime if object
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_parse(object)
|
15
|
+
object.to_datetime.to_parse if object
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class GeoPoint
|
2
|
+
def initialize( value )
|
3
|
+
self.latitude, self.longitude = *
|
4
|
+
case value
|
5
|
+
when Hash
|
6
|
+
[value[:latitude] || value['latitude'], value[:longitude] || value['longitude']]
|
7
|
+
when Array
|
8
|
+
[value.first, value.last]
|
9
|
+
else
|
10
|
+
raise ArgumentError.new( "invalid value for GeoPoint: \"#{value}\"" )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :latitude, :longitude
|
15
|
+
|
16
|
+
def to_geo_point
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_parse
|
21
|
+
{ "__type" => "GeoPoint", "latitude" => self.latitude, "longitude" => self.longitude }
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{self.latitude},#{self.longitude}"
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def to_ruby(object)
|
30
|
+
object.to_geo_point unless object.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_parse(object)
|
34
|
+
object.to_geo_point.to_parse
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Hash
|
2
|
+
def to_geo_point
|
3
|
+
return GeoPoint.new(self) if [:latitude, :longitude].all? {|required| self.key? required}
|
4
|
+
raise ArgumentError.new( "invalid value for GeoPoint: \"#{self}\"" )
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_datetime
|
8
|
+
retrieve_iso_key.to_datetime
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_date
|
12
|
+
retrieve_iso_key.to_date
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_time
|
16
|
+
retrieve_iso_key.to_time
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_parse
|
20
|
+
Hash[ *self.flat_map {|key, value| [key, value.to_parse]} ]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def retrieve_iso_key
|
26
|
+
validates_keys_present( '__type', 'iso' )
|
27
|
+
validate_key_equals( '__type', 'Date' )
|
28
|
+
value_for_indifferent_key('iso')
|
29
|
+
end
|
30
|
+
|
31
|
+
def validates_keys_present(*expected_keys)
|
32
|
+
result = expected_keys - self.keys.map(&:to_s)
|
33
|
+
raise ArgumentError, "expected key(s): #{result}" unless result.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate_key_equals( key, value )
|
37
|
+
raise ArgumentError, "conversion to Date/Time expectes a #{key} of #{value}" unless value_for_indifferent_key( key ) == value
|
38
|
+
end
|
39
|
+
|
40
|
+
def value_for_indifferent_key( key )
|
41
|
+
self[key] || self[key.to_sym]
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Opium
|
2
|
+
class Pointer
|
3
|
+
def initialize( attributes = {} )
|
4
|
+
self.class_name = attributes[:class_name] || attributes[:model_name] || (attributes[:class] || attributes[:model]).model_name
|
5
|
+
self.id = attributes[:id]
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :class_name, :id
|
9
|
+
alias_method :model_name, :class_name
|
10
|
+
|
11
|
+
def to_parse
|
12
|
+
{ __type: 'Pointer', className: class_name, objectId: id }.with_indifferent_access
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_writer :class_name, :id
|
18
|
+
alias_method :model_name=, :class_name=
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Regexp
|
2
|
+
def to_parse
|
3
|
+
{ '$regex' => self.source }.tap do |h|
|
4
|
+
ops = ''
|
5
|
+
{ IGNORECASE => 'i', MULTILINE => 'm', EXTENDED => 'x' }.each do |option, value|
|
6
|
+
ops += value unless ( self.options & option ) == 0
|
7
|
+
end
|
8
|
+
|
9
|
+
h['$options'] = ops unless ops.blank?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ::String
|
2
|
+
def to_bool
|
3
|
+
return true if self == true || self =~ (/^(true|t|yes|y|1)$/i)
|
4
|
+
return false if self == false || self.blank? || self =~ (/^(false|f|no|n|0)$/i)
|
5
|
+
raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_geo_point
|
9
|
+
return GeoPoint.new( self.split(',').map {|c| c.to_f} ) if self =~ /^[+-]?\d+(\.\d+)?\s*,\s*[+-]?\d+(\.\d+)?$/
|
10
|
+
raise ArgumentError.new("invalid value for GeoPoint: \"#{self}\"")
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def to_ruby(object)
|
15
|
+
object.to_s if object
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :to_parse, :to_ruby
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Time
|
2
|
+
def to_parse
|
3
|
+
{
|
4
|
+
'__type' => 'Date',
|
5
|
+
'iso' => self.iso8601
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def to_ruby(object)
|
11
|
+
object = object.utc if object.respond_to? :utc
|
12
|
+
object.to_time if object
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_parse(object)
|
16
|
+
object.to_time.to_parse if object
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'opium/extensions/object'
|
2
|
+
require 'opium/extensions/string'
|
3
|
+
require 'opium/extensions/numeric'
|
4
|
+
require 'opium/extensions/float'
|
5
|
+
require 'opium/extensions/integer'
|
6
|
+
require 'opium/extensions/boolean'
|
7
|
+
require 'opium/extensions/true_class'
|
8
|
+
require 'opium/extensions/false_class'
|
9
|
+
require 'opium/extensions/geo_point'
|
10
|
+
require 'opium/extensions/hash'
|
11
|
+
require 'opium/extensions/array'
|
12
|
+
require 'opium/extensions/date_time'
|
13
|
+
require 'opium/extensions/date'
|
14
|
+
require 'opium/extensions/time'
|
15
|
+
require 'opium/extensions/regexp'
|
16
|
+
require 'opium/extensions/pointer'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Attributable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActiveModel::ForbiddenAttributesProtection
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
@attributes ||= self.class.default_attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def attributes=(values)
|
15
|
+
sanitize_for_mass_assignment( rubyize_field_names( values ) ).each do |k, v|
|
16
|
+
field_info = self.class.fields[k]
|
17
|
+
if field_info.present?
|
18
|
+
send( "#{k}=", v )
|
19
|
+
else
|
20
|
+
attributes[k] = v
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def attributes_to_parse( options = {} )
|
26
|
+
options[:except] ||= self.class.fields.values.select {|f| f.readonly? }.map {|f| f.name} if options[:not_readonly]
|
27
|
+
Hash[*self.as_json( options ).map {|k, v| [self.class.fields[k].name_to_parse, v.to_parse]}.flatten]
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def rubyize_field_names( hash )
|
33
|
+
Hash[*hash.map {|k, v| [self.class.ruby_canonical_field_names[k] || k, v]}.flatten]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|