flex 0.1.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.
- data/LICENSE +20 -0
- data/README.md +20 -0
- data/VERSION +1 -0
- data/flex.gemspec +43 -0
- data/lib/flex.rb +418 -0
- data/lib/flex/api_methods.yml +108 -0
- data/lib/flex/class_proxy.rb +12 -0
- data/lib/flex/configuration.rb +57 -0
- data/lib/flex/errors.rb +42 -0
- data/lib/flex/http_clients/patron.rb +27 -0
- data/lib/flex/http_clients/rest_client.rb +38 -0
- data/lib/flex/loader.rb +116 -0
- data/lib/flex/logger.rb +16 -0
- data/lib/flex/model.rb +24 -0
- data/lib/flex/model/class_proxy.rb +45 -0
- data/lib/flex/model/instance_proxy.rb +101 -0
- data/lib/flex/model/manager.rb +67 -0
- data/lib/flex/rails.rb +12 -0
- data/lib/flex/rails/engine.rb +23 -0
- data/lib/flex/rails/helper.rb +16 -0
- data/lib/flex/related_model.rb +16 -0
- data/lib/flex/related_model/class_proxy.rb +23 -0
- data/lib/flex/related_model/class_sync.rb +23 -0
- data/lib/flex/related_model/instance_proxy.rb +28 -0
- data/lib/flex/result.rb +18 -0
- data/lib/flex/result/bulk.rb +20 -0
- data/lib/flex/result/collection.rb +51 -0
- data/lib/flex/result/document.rb +38 -0
- data/lib/flex/result/indifferent_access.rb +11 -0
- data/lib/flex/result/search.rb +51 -0
- data/lib/flex/result/source_document.rb +63 -0
- data/lib/flex/result/source_search.rb +32 -0
- data/lib/flex/structure/indifferent_access.rb +44 -0
- data/lib/flex/structure/mergeable.rb +21 -0
- data/lib/flex/tasks.rb +141 -0
- data/lib/flex/template.rb +187 -0
- data/lib/flex/template/base.rb +29 -0
- data/lib/flex/template/info.rb +50 -0
- data/lib/flex/template/partial.rb +31 -0
- data/lib/flex/template/search.rb +30 -0
- data/lib/flex/template/slim_search.rb +13 -0
- data/lib/flex/template/tags.rb +46 -0
- data/lib/flex/utility_methods.rb +140 -0
- data/lib/flex/utils.rb +59 -0
- data/lib/flex/variables.rb +11 -0
- data/lib/generators/flex/setup/setup_generator.rb +51 -0
- data/lib/generators/flex/setup/templates/flex_config.yml +16 -0
- data/lib/generators/flex/setup/templates/flex_dir/es.rb.erb +18 -0
- data/lib/generators/flex/setup/templates/flex_dir/es.yml.erb +19 -0
- data/lib/generators/flex/setup/templates/flex_dir/es_extender.rb.erb +17 -0
- data/lib/generators/flex/setup/templates/flex_initializer.rb.erb +44 -0
- data/lib/tasks/index.rake +23 -0
- data/test/flex.irt +143 -0
- data/test/flex/configuration.irt +53 -0
- data/test/irt_helper.rb +12 -0
- metadata +211 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module Flex
|
2
|
+
module Model
|
3
|
+
class ClassProxy < Flex::ClassProxy
|
4
|
+
|
5
|
+
include RelatedModel::ClassSync
|
6
|
+
|
7
|
+
attr_reader :parent_association, :parent_child_map
|
8
|
+
|
9
|
+
def initialize(base)
|
10
|
+
super
|
11
|
+
variables.add :index => Configuration.variables[:index],
|
12
|
+
:type => Manager.class_name_to_type(host_class.name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def index
|
16
|
+
variables[:index]
|
17
|
+
end
|
18
|
+
|
19
|
+
def index=(val)
|
20
|
+
variables[:index] = val
|
21
|
+
end
|
22
|
+
|
23
|
+
def type
|
24
|
+
variables[:type]
|
25
|
+
end
|
26
|
+
|
27
|
+
def type=(val)
|
28
|
+
variables[:type] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
def parent(parent_association, map)
|
32
|
+
@parent_association = parent_association
|
33
|
+
Manager.parent_types |= map.keys.map(&:to_s)
|
34
|
+
self.type = map.values.map(&:to_s)
|
35
|
+
@parent_child_map = map
|
36
|
+
@is_child = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_child?
|
40
|
+
!!@is_child
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Flex
|
2
|
+
module Model
|
3
|
+
class InstanceProxy < RelatedModel::InstanceProxy
|
4
|
+
|
5
|
+
# indexes the document
|
6
|
+
# usually called from after_save, you can eventually call it explicitly for example from another callback
|
7
|
+
# or whenever the DB doesn't get updated by the model
|
8
|
+
# you can also pass the :data=>flex_source explicitly (useful for example to override the flex_source in the model)
|
9
|
+
def store(vars={})
|
10
|
+
if instance.flex_indexable?
|
11
|
+
Flex.store metainfo.merge(:data => instance.flex_source).merge(vars)
|
12
|
+
else
|
13
|
+
Flex.remove(metainfo.merge(vars)) if Flex.get(metainfo.merge(vars.merge(:raise => false)))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# removes the document from the index (called from after_destroy)
|
18
|
+
def remove(vars={})
|
19
|
+
return unless instance.flex_indexable?
|
20
|
+
Flex.remove metainfo.merge(vars)
|
21
|
+
end
|
22
|
+
|
23
|
+
# gets the document from ES
|
24
|
+
def get(vars={})
|
25
|
+
return unless instance.flex_indexable?
|
26
|
+
Flex.get metainfo.merge(vars)
|
27
|
+
end
|
28
|
+
|
29
|
+
def parent_instance(raise=true)
|
30
|
+
return unless is_child?
|
31
|
+
@parent_instance ||= instance.send(class_flex.parent_association) || raise && raise(MissingParentError, "missing parent instance for document #{instance.inspect}.")
|
32
|
+
end
|
33
|
+
|
34
|
+
# helper that iterates through the parent record chain
|
35
|
+
# record.flex.each_parent{|p| p.do_smething }
|
36
|
+
def each_parent
|
37
|
+
pi = parent_instance
|
38
|
+
while pi do
|
39
|
+
yield pi
|
40
|
+
pi = pi.flex.parent_instance
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def type
|
45
|
+
@type ||= is_child? ? class_flex.parent_child_map[parent_instance.flex.type] : class_flex.type
|
46
|
+
end
|
47
|
+
|
48
|
+
def index
|
49
|
+
class_flex.index
|
50
|
+
end
|
51
|
+
|
52
|
+
def id
|
53
|
+
instance.id
|
54
|
+
end
|
55
|
+
|
56
|
+
def routing(raise=true)
|
57
|
+
@routing ||= case
|
58
|
+
when is_child? then parent_instance(raise).flex.routing
|
59
|
+
when is_parent? then create_routing
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def is_child?
|
64
|
+
@is_child ||= class_flex.is_child?
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_parent?
|
68
|
+
@is_parent ||= Manager.parent_types.include?(type)
|
69
|
+
end
|
70
|
+
|
71
|
+
def metainfo
|
72
|
+
@metainfo ||= begin
|
73
|
+
meta = { :index => index, :type => type, :id => id }
|
74
|
+
params = {}
|
75
|
+
params[:routing] = routing if routing
|
76
|
+
params[:parent] = parent_instance.id if is_child?
|
77
|
+
meta.merge!(:params => params) unless params.empty?
|
78
|
+
meta
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
BASE62_DIGITS = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z)
|
85
|
+
|
86
|
+
def create_routing
|
87
|
+
string = [index, type, id].join
|
88
|
+
remainder = Digest::MD5.hexdigest(string).to_i(16)
|
89
|
+
result = []
|
90
|
+
max_power = ( Math.log(remainder) / Math.log(62) ).floor
|
91
|
+
max_power.downto(0) do |power|
|
92
|
+
digit, remainder = remainder.divmod(62**power)
|
93
|
+
result << digit
|
94
|
+
end
|
95
|
+
result << remainder if remainder > 0
|
96
|
+
result.map{|digit| BASE62_DIGITS[digit]}.join
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Flex
|
2
|
+
module Model
|
3
|
+
module Manager
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
attr_accessor :parent_types
|
8
|
+
@parent_types = []
|
9
|
+
|
10
|
+
def init
|
11
|
+
Configuration.flex_models.each {|m| eval"::#{m}" if m.is_a?(String) }
|
12
|
+
end
|
13
|
+
|
14
|
+
# arrays of all the types
|
15
|
+
def types
|
16
|
+
type_class_map.keys.map{|k| k.split('/').last}
|
17
|
+
end
|
18
|
+
|
19
|
+
# sets the default parent/child mappings and merges with the config_file
|
20
|
+
# returns the indices structure used for creating the indices
|
21
|
+
def indices(file=Configuration.config_file)
|
22
|
+
@indices ||= begin
|
23
|
+
default = {}.extend Structure::Mergeable
|
24
|
+
Configuration.flex_models.each do |m|
|
25
|
+
m = eval"::#{m}" if m.is_a?(String)
|
26
|
+
next unless m.flex.is_child?
|
27
|
+
index = m.flex.index
|
28
|
+
m.flex.parent_child_map.each do |parent, child|
|
29
|
+
default.add index => {'mappings' => {child => {'_parent' => {'type' => parent }}}}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
hash = YAML.load(Utils.erb_process(file))
|
33
|
+
hash.delete('ANCHORS')
|
34
|
+
default.deep_merge(hash)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# maps all the index/types to the ruby class
|
39
|
+
def type_class_map
|
40
|
+
@type_class_map ||= begin
|
41
|
+
map = {}
|
42
|
+
Configuration.flex_models.each do |m|
|
43
|
+
m = eval("::#{m}") if m.is_a?(String)
|
44
|
+
types = m.flex.type.is_a?(Array) ? m.flex.type : [m.flex.type]
|
45
|
+
types.each do |t|
|
46
|
+
map["#{m.flex.index}/#{t}"] = m
|
47
|
+
end
|
48
|
+
end
|
49
|
+
map
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def class_name_to_type(class_name)
|
54
|
+
type = class_name.tr(':', '_')
|
55
|
+
type.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
56
|
+
type.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
57
|
+
type.downcase!
|
58
|
+
type
|
59
|
+
end
|
60
|
+
|
61
|
+
def type_to_class_name(type)
|
62
|
+
type.gsub(/__(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/flex/rails.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'flex'
|
2
|
+
require 'rails'
|
3
|
+
require 'flex/rails/helper'
|
4
|
+
|
5
|
+
if Rails.version.match /^3/
|
6
|
+
require 'flex/rails/engine'
|
7
|
+
else
|
8
|
+
Flex::Configuration.configure do |c|
|
9
|
+
c.config_file = Rails.root.join('config', 'flex.yml').to_s
|
10
|
+
c.flex_dir = Rails.root.join('app', 'flex').to_s
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Flex
|
2
|
+
module Rails
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
|
5
|
+
ActiveSupport.on_load(:before_configuration) do
|
6
|
+
Flex::Configuration.configure do |c|
|
7
|
+
c.variables[:index] = [self.class.name.split('::').first.underscore, ::Rails.env].join('_')
|
8
|
+
c.config_file = ::Rails.root.join('config', 'flex.yml').to_s
|
9
|
+
c.flex_dir = ::Rails.root.join('app', 'flex').to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load(:after_initialize) do
|
14
|
+
Helper.after_initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
config.to_prepare do
|
18
|
+
Flex.reload!
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Flex
|
2
|
+
module Rails
|
3
|
+
module Helper
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def after_initialize
|
7
|
+
# use the same app logger
|
8
|
+
Flex::Configuration.logger = ::Rails.logger
|
9
|
+
# we need to reload the flex API methods with the new variables
|
10
|
+
Flex.reload!
|
11
|
+
Flex::Model::Manager.init
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Flex
|
2
|
+
module RelatedModel
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
class << self; attr_reader :flex end
|
7
|
+
@flex ||= Flex::RelatedModel::ClassProxy.new(base)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def flex
|
12
|
+
@flex ||= InstanceProxy.new(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Flex
|
2
|
+
module RelatedModel
|
3
|
+
class ClassProxy
|
4
|
+
|
5
|
+
attr_reader :host_class
|
6
|
+
|
7
|
+
def initialize(host_class)
|
8
|
+
@host_class = host_class
|
9
|
+
end
|
10
|
+
|
11
|
+
include RelatedModel::ClassSync
|
12
|
+
|
13
|
+
alias_method :full_sync, :sync
|
14
|
+
|
15
|
+
def sync(*synced)
|
16
|
+
raise ArgumentError, 'You cannot flex.sync(self) a Flex::RelatedModel.' \
|
17
|
+
if synced.any?{|s| s == host_class}
|
18
|
+
full_sync
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Flex
|
2
|
+
module RelatedModel
|
3
|
+
module ClassSync
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
attr_accessor :synced
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def sync(*synced)
|
12
|
+
@synced = synced
|
13
|
+
host_class.class_eval do
|
14
|
+
raise NotImplementedError, "the class #{self} must implement :after_save and :after_destroy callbacks" \
|
15
|
+
unless respond_to?(:after_save) && respond_to?(:after_destroy)
|
16
|
+
after_save { flex.sync }
|
17
|
+
after_destroy { flex.sync }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Flex
|
2
|
+
module RelatedModel
|
3
|
+
class InstanceProxy
|
4
|
+
attr_reader :instance, :class_flex
|
5
|
+
|
6
|
+
def initialize(instance)
|
7
|
+
@instance = instance
|
8
|
+
@class_flex = instance.class.flex
|
9
|
+
end
|
10
|
+
|
11
|
+
def sync
|
12
|
+
class_flex.synced.each do |s|
|
13
|
+
case
|
14
|
+
when s == instance.class # only called for Flex::Model
|
15
|
+
instance.destroyed? ? remove : store
|
16
|
+
when s.is_a?(Symbol)
|
17
|
+
instance.send(s).flex.sync
|
18
|
+
when s.is_a?(String)
|
19
|
+
parent_instance.flex.sync if s == parent_instance.flex.type
|
20
|
+
else
|
21
|
+
raise ArgumentError, "self, string or symbol expected, got #{s.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/flex/result.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Flex
|
2
|
+
class Result < ::Hash
|
3
|
+
|
4
|
+
attr_reader :template, :variables, :response
|
5
|
+
|
6
|
+
def initialize(template, variables, response, result=nil)
|
7
|
+
@template = template
|
8
|
+
@variables = variables
|
9
|
+
@response = response
|
10
|
+
replace result || !response.body.empty? && MultiJson.decode(response.body) || return
|
11
|
+
Configuration.result_extenders.each do |ext|
|
12
|
+
next if ext.respond_to?(:should_extend?) && !ext.should_extend?(self)
|
13
|
+
extend ext
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Flex
|
2
|
+
class Result
|
3
|
+
module Bulk
|
4
|
+
|
5
|
+
# extend if result comes from a bulk url
|
6
|
+
def self.should_extend?(result)
|
7
|
+
result.response.url =~ /\b_bulk\b/
|
8
|
+
end
|
9
|
+
|
10
|
+
def failed
|
11
|
+
self['items'].reject{|i| i['index']['ok']}
|
12
|
+
end
|
13
|
+
|
14
|
+
def successful
|
15
|
+
self['items'].select{|i| i['index']['ok']}
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Flex
|
2
|
+
class Result
|
3
|
+
module Collection
|
4
|
+
|
5
|
+
attr_reader :total_entries, :variables
|
6
|
+
|
7
|
+
def setup(total_entries, variables)
|
8
|
+
@total_entries = total_entries
|
9
|
+
@variables = variables
|
10
|
+
end
|
11
|
+
|
12
|
+
def per_page
|
13
|
+
(@variables[:per_page] || @variables[:params] && @variables[:params][:size] || @variables[:size] || 10).to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def total_pages
|
17
|
+
( @total_entries.to_f / per_page ).ceil
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_page
|
21
|
+
if @variables[:page]
|
22
|
+
@variables[:page].to_i
|
23
|
+
else
|
24
|
+
(per_page + (@variables[:from]||0).to_i) / per_page
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous_page
|
29
|
+
current_page > 1 ? (current_page - 1) : nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def next_page
|
33
|
+
current_page < total_pages ? (current_page + 1) : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def offset
|
37
|
+
per_page * (current_page - 1)
|
38
|
+
end
|
39
|
+
|
40
|
+
def out_of_bounds?
|
41
|
+
current_page > total_pages
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :limit_value, :per_page
|
45
|
+
alias_method :total_count, :total_entries
|
46
|
+
alias_method :num_pages, :total_pages
|
47
|
+
alias_method :offset_value, :offset
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|