conjoin 0.0.1 → 0.0.2
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 +4 -4
- data/conjoin.gemspec +20 -0
- data/lib/conjoin.rb +27 -1
- data/lib/conjoin/active_record.rb +374 -0
- data/lib/conjoin/assets.rb +205 -0
- data/lib/conjoin/auth.rb +92 -0
- data/lib/conjoin/class_methods.rb +15 -0
- data/lib/conjoin/csrf.rb +26 -0
- data/lib/conjoin/cuba.rb +72 -0
- data/lib/conjoin/env_string.rb +17 -0
- data/lib/conjoin/form_builder.rb +334 -0
- data/lib/conjoin/i18n.rb +97 -0
- data/lib/conjoin/inputs/boolean.rb +8 -0
- data/lib/conjoin/inputs/checkbox.rb +14 -0
- data/lib/conjoin/inputs/date.rb +11 -0
- data/lib/conjoin/inputs/decimal.rb +9 -0
- data/lib/conjoin/inputs/file.rb +17 -0
- data/lib/conjoin/inputs/hidden.rb +10 -0
- data/lib/conjoin/inputs/integer.rb +9 -0
- data/lib/conjoin/inputs/password.rb +10 -0
- data/lib/conjoin/inputs/radio.rb +35 -0
- data/lib/conjoin/inputs/select.rb +67 -0
- data/lib/conjoin/inputs/state.rb +68 -0
- data/lib/conjoin/inputs/string.rb +9 -0
- data/lib/conjoin/inputs/time.rb +11 -0
- data/lib/conjoin/middleware.rb +40 -0
- data/lib/conjoin/recursive_ostruct.rb +117 -0
- data/lib/conjoin/tasks.rb +4 -0
- data/lib/conjoin/tasks/migrate.rake +70 -0
- data/lib/conjoin/tasks/migrate.rb +142 -0
- data/lib/conjoin/version.rb +1 -1
- data/lib/conjoin/widgets.rb +323 -0
- metadata +296 -2
data/lib/conjoin/i18n.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'r18n-core'
|
2
|
+
|
3
|
+
# https://github.com/ai/r18n
|
4
|
+
module Conjoin
|
5
|
+
module I18N
|
6
|
+
include R18n::Helpers
|
7
|
+
|
8
|
+
def self.setup(app)
|
9
|
+
app.settings[:default_locale] = 'en-US'
|
10
|
+
app.settings[:translations] = File.join(app.root, 'i18n')
|
11
|
+
::R18n::Filters.off :untranslated
|
12
|
+
::R18n::Filters.on :untranslated_html
|
13
|
+
if Conjoin.env.test? or Conjoin.env.development?
|
14
|
+
::R18n.clear_cache!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_locale(req, force_default = false)
|
19
|
+
::R18n.set do
|
20
|
+
::R18n::I18n.default = settings[:default_locale]
|
21
|
+
locale = get_locale_from_host
|
22
|
+
# You can add support for path language info :) Just do it and pull request it ;)
|
23
|
+
# locale = get_locale_from_path if locale.nil?
|
24
|
+
if locale.nil? and not force_default
|
25
|
+
locales = ::R18n::I18n.parse_http req.env['HTTP_ACCEPT_LANGUAGE']
|
26
|
+
if req.params['locale']
|
27
|
+
locales.insert 0, req.params['locale']
|
28
|
+
elsif req.session['locale']
|
29
|
+
locales.insert 0, req.session['locale']
|
30
|
+
end
|
31
|
+
else
|
32
|
+
locales = []
|
33
|
+
locales << locale
|
34
|
+
locales << settings[:default_locale]
|
35
|
+
end
|
36
|
+
::R18n::I18n.new locales, settings[:translations]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_locale_from_host
|
41
|
+
# auxiliar method to get locale from the subdomain (assuming it is a valid locale).
|
42
|
+
data = req.host.split('.')[0]
|
43
|
+
data if ::R18n::Locale.exists? data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module R18n
|
49
|
+
class << self
|
50
|
+
def t(*params)
|
51
|
+
if params.first.is_a? String
|
52
|
+
params.first.split('.').inject(get.t) { |h, k| h[k.to_sym] }
|
53
|
+
else
|
54
|
+
get.t(*params)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Override
|
60
|
+
# https://github.com/ai/r18n/blob/master/r18n-core/lib/r18n-core/locale.rb#L152
|
61
|
+
class Locale
|
62
|
+
# Convert +object+ to String. It support Fixnum, Bignum, Float, Time, Date
|
63
|
+
# and DateTime.
|
64
|
+
#
|
65
|
+
# For time classes you can set +format+ in standard +strftime+ form,
|
66
|
+
# <tt>:full</tt> (“01 Jule, 2009”), <tt>:human</tt> (“yesterday”),
|
67
|
+
# <tt>:standard</tt> (“07/01/09”) or <tt>:month</tt> for standalone month
|
68
|
+
# name. Default format is <tt>:standard</tt>.
|
69
|
+
def localize(obj, format = nil, *params)
|
70
|
+
case obj
|
71
|
+
when Integer
|
72
|
+
format_integer(obj)
|
73
|
+
when Float, BigDecimal
|
74
|
+
format_float(obj.to_f)
|
75
|
+
when Time, DateTime, Date
|
76
|
+
return strftime(obj, format) if format.is_a? String
|
77
|
+
return month_standalone[obj.month - 1] if :month == format
|
78
|
+
return obj.to_s if :human == format and not params.first.is_a? I18n
|
79
|
+
|
80
|
+
type = obj.is_a?(Date) ? 'date' : 'time'
|
81
|
+
format = :standard unless format
|
82
|
+
|
83
|
+
unless respond_to? "format_#{type}_#{format}"
|
84
|
+
raise ArgumentError, "Unknown time formatter #{format}"
|
85
|
+
end
|
86
|
+
|
87
|
+
send "format_#{type}_#{format}", obj, *params
|
88
|
+
else
|
89
|
+
obj.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_time_time time, *params
|
94
|
+
format_time(time)[1..-1]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class CheckboxInput < Input
|
4
|
+
def display
|
5
|
+
options[:value] = 'on' if data.value == true
|
6
|
+
options[:checked] = 'checked' if data.value
|
7
|
+
options[:type] = :checkbox
|
8
|
+
options[:class].gsub!(/form-control/, '')
|
9
|
+
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class FileInput < Input
|
4
|
+
def display
|
5
|
+
key = options[:s3_upload_path].call(record)
|
6
|
+
|
7
|
+
mab do
|
8
|
+
unless options[:value]
|
9
|
+
div id: id, name: options[:name], class: 'file s3-uploader', value: options[:value]
|
10
|
+
end
|
11
|
+
input id: id, type: :hidden, name: options[:name], class: 'form-control file s3-uploader', value: options[:value]
|
12
|
+
text! S3Uploader.js_button(id, key, options[:callback_url], options[:callback_params])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class RadioInput < Input
|
4
|
+
def display
|
5
|
+
options[:type] = :radio
|
6
|
+
options[:class].gsub!(/form-control/, '')
|
7
|
+
|
8
|
+
radios = options[:radios] || [:yes, :no]
|
9
|
+
|
10
|
+
opts = options.dup
|
11
|
+
|
12
|
+
mab do
|
13
|
+
div class: 'form-control' do
|
14
|
+
radios.each_with_index do |name, i|
|
15
|
+
name = name.to_s
|
16
|
+
opts[:value] = name
|
17
|
+
opts[:id] = "#{options[:id]}_#{i}"
|
18
|
+
|
19
|
+
if (opts[:value] == 'no' and data.value == false) \
|
20
|
+
or (opts[:value] == 'yes' and data.value == true) \
|
21
|
+
or (opts[:value] == data.value)
|
22
|
+
opts[:checked] = 'checked'
|
23
|
+
else
|
24
|
+
opts.delete :checked
|
25
|
+
end
|
26
|
+
|
27
|
+
input opts
|
28
|
+
span name.humanize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Conjoin
|
2
|
+
module FormBuilder
|
3
|
+
class SelectInput < Input
|
4
|
+
@select_options = {}
|
5
|
+
|
6
|
+
def display
|
7
|
+
mab do
|
8
|
+
# automatically add a prompt by default
|
9
|
+
options[:prompt] = true unless options.key? :prompt
|
10
|
+
options[:class] += ' select2'
|
11
|
+
selected_value = options.delete :value
|
12
|
+
|
13
|
+
select options do
|
14
|
+
if prompt = options.delete(:prompt)
|
15
|
+
opts = {
|
16
|
+
value: ''
|
17
|
+
}
|
18
|
+
opts['selected'] = 'selected' unless selected_value
|
19
|
+
option opts do
|
20
|
+
text prompt.to_s == 'true' ? 'Please Choose One.' : prompt
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if not options[:group]
|
25
|
+
select_options.each do |name, value|
|
26
|
+
option render_opts(value, selected_value, opts) do
|
27
|
+
text name.titleize
|
28
|
+
end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
select_options.each do |group_select, group|
|
32
|
+
optgroup label: group.to_s.titleize do
|
33
|
+
group_select.each do |value, name|
|
34
|
+
option render_opts(value, selected_value, opts) do
|
35
|
+
text name.titleize
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_opts value, selected_value, opts
|
46
|
+
opts = {
|
47
|
+
value: value
|
48
|
+
}
|
49
|
+
if selected_value.is_a? ActiveRecord::Associations::CollectionProxy
|
50
|
+
opts['selected'] = 'selected' if selected_value.map(&:id).include? value
|
51
|
+
else
|
52
|
+
opts['selected'] = 'selected' if selected_value == value.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.select_options
|
59
|
+
@select_options
|
60
|
+
end
|
61
|
+
|
62
|
+
def select_options
|
63
|
+
self.class.select_options.invert
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'select'
|
2
|
+
|
3
|
+
module Conjoin
|
4
|
+
module FormBuilder
|
5
|
+
class StateInput < SelectInput
|
6
|
+
@select_options = {
|
7
|
+
'AL' => 'Alabama',
|
8
|
+
'AK' => 'Alaska',
|
9
|
+
'AS' => 'America Samoa',
|
10
|
+
'AZ' => 'Arizona',
|
11
|
+
'AR' => 'Arkansas',
|
12
|
+
'CA' => 'California',
|
13
|
+
'CO' => 'Colorado',
|
14
|
+
'CT' => 'Connecticut',
|
15
|
+
'DE' => 'Delaware',
|
16
|
+
'DC' => 'District of Columbia',
|
17
|
+
'FM' => 'Micronesia',
|
18
|
+
'FL' => 'Florida',
|
19
|
+
'GA' => 'Georgia',
|
20
|
+
'GU' => 'Guam',
|
21
|
+
'HI' => 'Hawaii',
|
22
|
+
'ID' => 'Idaho',
|
23
|
+
'IL' => 'Illinois',
|
24
|
+
'IN' => 'Indiana',
|
25
|
+
'IA' => 'Iowa',
|
26
|
+
'KS' => 'Kansas',
|
27
|
+
'KY' => 'Kentucky',
|
28
|
+
'LA' => 'Louisiana',
|
29
|
+
'ME' => 'Maine',
|
30
|
+
'MH' => 'Islands',
|
31
|
+
'MD' => 'Maryland',
|
32
|
+
'MA' => 'Massachusetts',
|
33
|
+
'MI' => 'Michigan',
|
34
|
+
'MN' => 'Minnesota',
|
35
|
+
'MS' => 'Mississippi',
|
36
|
+
'MO' => 'Missouri',
|
37
|
+
'MT' => 'Montana',
|
38
|
+
'NE' => 'Nebraska',
|
39
|
+
'NV' => 'Nevada',
|
40
|
+
'NH' => 'New Hampshire',
|
41
|
+
'NJ' => 'New Jersey',
|
42
|
+
'NM' => 'New Mexico',
|
43
|
+
'NY' => 'New York',
|
44
|
+
'NC' => 'North Carolina',
|
45
|
+
'ND' => 'North Dakota',
|
46
|
+
'OH' => 'Ohio',
|
47
|
+
'OK' => 'Oklahoma',
|
48
|
+
'OR' => 'Oregon',
|
49
|
+
'PW' => 'Palau',
|
50
|
+
'PA' => 'Pennsylvania',
|
51
|
+
'PR' => 'Puerto Rico',
|
52
|
+
'RI' => 'Rhode Island',
|
53
|
+
'SC' => 'South Carolina',
|
54
|
+
'SD' => 'South Dakota',
|
55
|
+
'TN' => 'Tennessee',
|
56
|
+
'TX' => 'Texas',
|
57
|
+
'UT' => 'Utah',
|
58
|
+
'VT' => 'Vermont',
|
59
|
+
'VI' => 'Virgin Island',
|
60
|
+
'VA' => 'Virginia',
|
61
|
+
'WA' => 'Washington',
|
62
|
+
'WV' => 'West Virginia',
|
63
|
+
'WI' => 'Wisconsin',
|
64
|
+
'WY' => 'Wyoming'
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rack/protection"
|
2
|
+
|
3
|
+
module Conjoin
|
4
|
+
class Middleware
|
5
|
+
class << self
|
6
|
+
# taken from
|
7
|
+
# https://github.com/rkh/rack-protection/blob/master/lib/rack/protection.rb#L20
|
8
|
+
def new app, options = {}
|
9
|
+
# except = Array options[:except]
|
10
|
+
# use_these = Array options[:use]
|
11
|
+
Rack::Builder.new do
|
12
|
+
if not Conjoin.env.mounted?
|
13
|
+
use Rack::Session::Cookie, secret: ENV['SECRET_BASE_KEY']
|
14
|
+
end
|
15
|
+
# we need to disable session_hijacking because IE uses different headers
|
16
|
+
# for ajax request over standard ones.
|
17
|
+
# https://github.com/rkh/rack-protection/issues/11#issuecomment-9005539
|
18
|
+
use Rack::Protection, except: :session_hijacking
|
19
|
+
|
20
|
+
if not Conjoin.env.mounted? and Conjoin.env.development?
|
21
|
+
require 'rack-livereload'
|
22
|
+
use Rack::LiveReload
|
23
|
+
use Rack::Reloader
|
24
|
+
end
|
25
|
+
|
26
|
+
if Conjoin.env.test? or Conjoin.env.development?
|
27
|
+
require 'better_errors'
|
28
|
+
use BetterErrors::Middleware
|
29
|
+
# require 'pry'
|
30
|
+
# require 'pry-rescue'
|
31
|
+
# use PryRescue::Rack
|
32
|
+
end
|
33
|
+
|
34
|
+
# continue running the application
|
35
|
+
run app
|
36
|
+
end.to_app
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# https://gist.github.com/rmw/2710460
|
2
|
+
require 'hashie'
|
3
|
+
|
4
|
+
class Hash
|
5
|
+
# options:
|
6
|
+
# :exclude => [keys] - keys need to be symbols
|
7
|
+
def to_ostruct(options = {})
|
8
|
+
convert_to_ostruct_recursive(self, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_sym_keys
|
12
|
+
# self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
13
|
+
self.each_with_object({}) { |(k,v), memo| memo[k.to_sym] = v }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert_to_ostruct_recursive(obj, options)
|
19
|
+
result = obj
|
20
|
+
if result.is_a? Hash
|
21
|
+
result = result.dup.with_sym_keys
|
22
|
+
result.each do |key, val|
|
23
|
+
result[key] = convert_to_ostruct_recursive(val, options) unless options[:exclude].try(:include?, key)
|
24
|
+
end
|
25
|
+
result = OpenStruct.new result
|
26
|
+
elsif result.is_a? Array
|
27
|
+
result = result.map { |r| convert_to_ostruct_recursive(r, options) }
|
28
|
+
end
|
29
|
+
return result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class HashIndifferent < Hash
|
34
|
+
include Hashie::Extensions::MergeInitializer
|
35
|
+
include Hashie::Extensions::IndifferentAccess
|
36
|
+
end
|
37
|
+
|
38
|
+
class OpenStruct
|
39
|
+
def to_hash options = {}
|
40
|
+
convert_to_hash_recursive(self, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def convert_to_hash_recursive(obj, options)
|
46
|
+
result = obj
|
47
|
+
if result.is_a? OpenStruct
|
48
|
+
result = result.dup.to_h.with_sym_keys
|
49
|
+
result.each do |key, val|
|
50
|
+
result[key] = convert_to_hash_recursive(val, options) unless options[:exclude].try(:include?, key)
|
51
|
+
end
|
52
|
+
result = HashIndifferent.new result
|
53
|
+
elsif result.is_a? Array
|
54
|
+
result = result.map { |r| convert_to_hash_recursive(r, options) }
|
55
|
+
end
|
56
|
+
return result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# require 'spec_helper'
|
61
|
+
#
|
62
|
+
# describe Hash do
|
63
|
+
#
|
64
|
+
# describe "#to_ostruct_recursive" do
|
65
|
+
# describe "replace a nested hash" do
|
66
|
+
# before do
|
67
|
+
# @h = { :a => { :b => { :c => 1 } } }
|
68
|
+
# @o = @h.to_ostruct_recursive
|
69
|
+
# end
|
70
|
+
# it "should be an OpenStruct" do
|
71
|
+
# @o.is_a?(OpenStruct).should be_true
|
72
|
+
# end
|
73
|
+
# it "should have a nested OpenStruct" do
|
74
|
+
# @o.a.should be
|
75
|
+
# @o.a.is_a?(OpenStruct).should be_true
|
76
|
+
# end
|
77
|
+
# it "should have a nested nested OpenStruct" do
|
78
|
+
# @o.a.b.should be
|
79
|
+
# @o.a.b.is_a?(OpenStruct).should be_true
|
80
|
+
# end
|
81
|
+
# it "should have a nested nested nested value of 1" do
|
82
|
+
# @o.a.b.c.should be
|
83
|
+
# @o.a.b.c.should == 1
|
84
|
+
# end
|
85
|
+
# describe "exclude a key from being converted to an OpenStruct" do
|
86
|
+
# before do
|
87
|
+
# @o_exclude = @h.to_ostruct_recursive({ :exclude => [:b] })
|
88
|
+
# end
|
89
|
+
# it "should be an OpenStruct" do
|
90
|
+
# @o.is_a?(OpenStruct).should be_true
|
91
|
+
# end
|
92
|
+
# it "should have a nested OpenStruct" do
|
93
|
+
# @o.a.is_a?(OpenStruct).should be_true
|
94
|
+
# end
|
95
|
+
# it "should have a nested nested Hash" do
|
96
|
+
# @o_exclude.a.b.is_a?(Hash).should be_true
|
97
|
+
# @o_exclude.a.b.should == { :c => 1 }
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
# describe "replace a nest hash in an array" do
|
102
|
+
# before do
|
103
|
+
# @h = { :a => [ {:a1 => 1 } ] }
|
104
|
+
# @o = @h.to_ostruct_recursive
|
105
|
+
# end
|
106
|
+
# it "should be an OpenStruct" do
|
107
|
+
# @o.is_a?(OpenStruct).should be_true
|
108
|
+
# end
|
109
|
+
# it "should have an array with 1 struct" do
|
110
|
+
# @o.a.is_a?(Array).should be_true
|
111
|
+
# @o.a.size.should == 1
|
112
|
+
# @o.a.first.is_a?(OpenStruct).should be_true
|
113
|
+
# @o.a.first.a1.should == 1
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
# end
|
117
|
+
# end
|